• Advertisement

How to abort a file transfer immediately?

Recommended Posts

I am sending a file from one socket to another, and there are a lot of packets that are now in transit and in the socket send buffer. What I want to do is to abort the file transfer immediately (once the user clicks "Abort", the file transfer is aborted without any delay).

 

If I closed the connection gracefully (by sending a FIN packet), then the FIN packet will only get processed by the other socket when the packets in transit and in the send buffer has been fully received by the other socket (which could take a long time).

 

I think that the only way to do this is to close the connection ungracefully (by sending an RST packet). Am I correct or is there another way?

Share this post


Link to post
Share on other sites
Advertisement

Yes, that is what "gracefully" means. Another way could be to keep a separate connection with another socket over which you send control information ("file transfer started", "file transfer complete", "file transfer aborted" and so on).

 

Also, ungracefully closing a TCP connection is not inherently bad, it's only bad in that you may lose buffered data or data in transit that, from the application's perspective, has already been sent (unless the application implements its own acknowledgment protocol on top of TCP). If you're aborting a transfer, you presumably no longer care if the file arrives intact, so it's okay. Just make sure the remote can distinguish between a completed transfer and a prematurely aborted transfer, and make sure the socket is not used for anything else.

Share this post


Link to post
Share on other sites


Another way could be to keep a separate connection with another socket over which you send control information ("file transfer started", "file transfer complete", "file transfer aborted" and so on).

 

Let's say I did that, now I am sending a file through the first socket, and after a while I decided to abort the file transfer, so I send the string "file transfer aborted" through the second socket and then this string is received by the other end, what should the other end do now?

Share this post


Link to post
Share on other sites


Let's say I did that, now I am sending a file through the first socket, and after a while I decided to abort the file transfer, so I send the string "file transfer aborted" through the second socket and then this string is received by the other end, what should the other end do now?

 

The second end can then "gracefully" close his socket and let the OS clean up in the background while it moves on to other stuff since it now knows the transfer is aborted. That's if you really want to close it gracefully; like I said, if you need to tear down a connection NOW and don't care what happens to the data already committed to the TCP stream, then a hard shutdown is acceptable (to be fair, such situations are rare; when transferring large files it is common to transfer them block by block with, depending on your requirements, periodic status and integrity checking, which allows you to send control information over the same socket with minimal latency).

Share this post


Link to post
Share on other sites


The second end can then "gracefully" close his socket

 

I assume you mean it should close the socket that is receiving the file.

When the socket wants to gracefully close the connection, it would send a FIN packet to the other end, now the other end will also send its own FIN packet, which like I said earlier will only get processed by the other socket when the packets in transit and in the send buffer has been fully received by the other socket, which doesn't solve the problem (unless I misinterpreted what you mean).

 

Note that I think I will go with the RST approach, however I'm curious to understand this other approach.

Share this post


Link to post
Share on other sites

I'm not sure what level you're working at.

 

When you say "send a FIN packet" and "send a RST" packet, you talk in terms of the implementation.

 

If you control the implementation, then you also control what the user has buffered. Thus, you can throw away what the user has buffered, and only keep the bit that is currently "on the wire," which is the size of the remote accept window.

If you want the connection to appear to have terminated gracefully, at the TCP level, then that's what you have to do.

However, some higher-level application protocol will probably be confused. For example, if the protocol says "here comes file X" and then data starts streaming, and then data stops streaming -- should the remote end assume that it has all of X? If there is a length field in the protocol, the remote end will know it didn't receive all of it, so the "graceful" shutdown at the TCP level still ends up being a "forceful/unexpected" shutdown at the application level.

 

So, if the reason you want to "immediately" stop the transfer is that you changed your mind -- you don't want to send that 20 GB file over your cell phone data plan -- then the best thing to do is to remove all the data that's on the wire as well as all the data that the user has buffered, which means tearing down the connection state, which means sending a RST, and responding with RST if you hear anything more from the other end.

 

Now -- this is from the point of view of the implementation. When you're writing application software, you typically don't have control at that level, but instead use some particular socket API. In Berkeley sockets (most common,) compare the actions of shutdown() with the actions of close(). They may translate to particular TCP packets on the wire, but there is no guarantee -- there is only the specific defined semantics of those API calls that the application makes.

Share this post


Link to post
Share on other sites

I'm not sure what level you're working at.

 

I am working at the application level, that is, I am using sockets and not controlling the TCP packets themselves (which can only be done in raw sockets I suppose).

 

 

 

 


When you say "send a FIN packet" and "send a RST" packet, you talk in terms of the implementation.

 

When I say "send a RST", this means that I want to close the connection ungracefully, which means that when I close the socket, the socket will remove all of its data (send buffer, receive buffer, etc.) and sends an RST packet to the other end telling it that it is no longer there.

 

But when I say "send a FIN packet", this means that I want to close the connection gracefully, which means that when I close the socket, all of the data in transit and in the send buffer will be sent to the other end (followed by a FIN packet), and all of the data from the other end in transit and in its send buffer will be sent (followed by a FIN packet also). Which as I have said earlier is a bad idea if I want to abort a file transfer immediately, because it could take a long time for the pending data to arrive before the connection is closed.

 

 

 


So, if the reason you want to "immediately" stop the transfer is that you changed your mind -- you don't want to send that 20 GB file over your cell phone data plan -- then the best thing to do is to remove all the data that's on the wire as well as all the data that the user has buffered, which means tearing down the connection state, which means sending a RST, and responding with RST if you hear anything more from the other end.

 

Yes, this is the only approach that makes sense.

 

BTW, I am working under Windows.

Edited by tom_d1

Share this post


Link to post
Share on other sites

Documentation for Winsock closesocket() is here:

 

https://msdn.microsoft.com/en-us/library/windows/desktop/ms737582(v=vs.85).aspx

 

Especially this bit:

 

If the l_onoff member of the linger structure is nonzero and l_linger member is zero, closesocket is not blocked even if queued data has not yet been sent or acknowledged. This is called a hard or abortive close,

 

Other TCP implementations may do different things -- read your OS man pages!

Share this post


Link to post
Share on other sites

Documentation for Winsock closesocket() is here:

 

https://msdn.microsoft.com/en-us/library/windows/desktop/ms737582(v=vs.85).aspx

 

Especially this bit:

 

If the l_onoff member of the linger structure is nonzero and l_linger member is zero, closesocket is not blocked even if queued data has not yet been sent or acknowledged. This is called a hard or abortive close, which stops data transfer from the socket.

 

Other TCP implementations may do different things -- read your OS man pages!

 

This only works on Windows platform? 

Edited by stasia8954

Share this post


Link to post
Share on other sites

This only works on Windows platform?


If only there was some way to figure out how other platforms implement SO_LINGER.
That would be great, wouldn't it?

Turns out, there is! Here's how Linux does it: http://lmgtfy.com/?q=linux+SO_LINGER&l=1
Here's how MacOS X does it: http://lmgtfy.com/?q=MacOS+X+SO_LINGER&l=1
Here's how FreeBSD does it: http://lmgtfy.com/?q=FreeBSD+SO_LINGER&l=1

Also, to save us all from having to read man pages, No-Bugs Hare did suggest an answer to the question in the previous comment:

From what I've seen, linger (a.k.a. SO_LINGER) option works pretty consistently across the board.

Share this post


Link to post
Share on other sites

 

Documentation for Winsock closesocket() is here:

 

https://msdn.microsoft.com/en-us/library/windows/desktop/ms737582(v=vs.85).aspx

 

Especially this bit:

 

If the l_onoff member of the linger structure is nonzero and l_linger member is zero, closesocket is not blocked even if queued data has not yet been sent or acknowledged. This is called a hard or abortive close, which stops data transfer from the socket.

 

Other TCP implementations may do different things -- read your OS man pages!

 

This only works on Windows platform? 

 

same question

Edited by Werfkjl

Share this post


Link to post
Share on other sites

 

Documentation for Winsock closesocket() is here:

 

https://msdn.microsoft.com/en-us/library/windows/desktop/ms737582(v=vs.85).aspx

 

Especially this bit:

 

If the l_onoff member of the linger structure is nonzero and l_linger member is zero, closesocket is not blocked even if queued data has not yet been sent or acknowledged. This is called a hard or abortive close, which stops data transfer from the socket.

 

Other TCP implementations may do different things -- read your OS man pages!

 

This only works on Windows platform? 

 

 

That didn't work for me!

Edited by Denroues

Share this post


Link to post
Share on other sites

The annoying thing with SO_LINGER is that it is a parade example of a total API and documentation fuckup. It is impossible to understand what it is or what it does, what the purpose is, or what you can use it for, unless you already know.

 

The name suggests that if you set the connection to linger, then doing something with the socket (closing, to be precise) will cause your application to block for some time. Since you have to do something for that to happen, this is obviously not the default behaviour, or so you should believe.

 

Microsoft is maximally obscure about what goes on in their implementation, as usual. It merely says "specifies whether a socket should remain open for some time", which doesn't tell us anything about whether the call to closesocket() will block or not. The wording "after a closesocket call" in the Remarks section, however, suggests that the close does not block in either case (it does, however!). Otherwise, how could it ever be "after", it would have to be "during". MSDN also mentions that shutdown() will not block regardless of whether linger is enabled, but it fails to mention that calling shutdown() actually disables linger. Surprise. There exists another well-hidden page on MSDN -- if you can find it -- which speaks of linger as does not complete immediately, which suggests closesocket() indeed blocks with linger (which it does, too!).

 

If you only keep searching long enough, you learn that by default Windows does not block on closesocket, but keeps the socket lingering for a graceful shutdown (with no way for you of knowing whether the data was delivered!), but it also supports blocking and abortive close via linger.

 

POSIX on the other hand side specifies very precisely what happens with linger:

 

If SO_LINGER is set, the system shall block the calling thread during close() until it can transmit the data or until the time expires. If SO_LINGER is not specified, and close() is issued, the system handles the call in a way that allows the calling thread to continue as quickly as possible.

 

Except that's not as abvious as one would wish. Every normal-wired person should read this as: If I set SO_LINGER to one, my calls to close() will block, otherwise they will not block. If I don't say anything, the behaviour is probably "no" because I need to do something to enable linger.

 

The wording of POSIX is also congruent with the wording in Richard Steven's (kinda authorative) book:

If l_onoff is nonzero and l_linger is zero, TCP aborts the connection when it is closed (pp. 1019-1020 of TCPv2). That is, TCP discards any data still remaining in the socket send buffer and sends an RST to the peer, not the normal four-packet connection termination sequence

 

The Linux documentation, however, states that calling exit() automatically causes any open sockets to linger in the background. Always. What does that mean? Does it mean exit() blocks? That would be funny. Does it mean exit() succeeds immediately but the socket remains (this is what actually happens!)? For how long? Forever? We have no way of knowing, they're not telling us. Well yes, we have a way of knowing, there's a proc thingie for that... but it's not really obvious.

 

What is the intention behind this complicated mess?

TCP has guaranteed delivery. Once you have successfully passed data to the network stack, you have the guarantee that it will either be delivered, or you will get an error message. How do you get an error? Surely not from send() because at the time send() returns the data is most certainly still inside the send buffer and hasn't even made it to the wire, let alone the target machine. There is no way of telling whether any of the data ever arrives anywhere at this point.

 

So, someone else must tell you that an error occurred (such as the other host is no longer reachable, user pulled a cable, whatever). That would be the next time you send or receive, or well... once you close the socket. This is your last chance of knowing whether everything went as you expected. Once close() returns, the socket is gone. No more errors, ever again.

 

Thus, the "correct" behavior of close must be to block (on TCP sockets), by default. That's however not what you conclude from reading either the POSIX or Windows documentation (at least I don't). Blocking indefinitively can be undesirable. If nothing else, it is a huge potential resource leak, and enables a very easy DoS attack on every server.

 

Therefore, the "next best" behaviour would arguably be to block for some time, and then give up. That's what linger does. Or at least, that's what linger does under POSIX systems. Under Windows, if you trust what MSDN says, the socket stays open, but if errors occur, then that's just bad luck. You never know because closesocket() didn't wait for them. Stevens requires that the call to close() blocks for the specified time, and it returns EWOULDBLOCK in case data was dropped because of timeout.

 

So what is this good for?

You will almost certainly only ever either use linger with a zero linger time (that is, abortive), or not use it at all and simply let TCP work the way it works by default. Which is (hopefully) the safest, and most desirable way on the average case.

 

The linger option lets you demand that it waits, and lets you specify for how long. However, telling it "zero" means that it will not wait at all, and closing the socket will not block. In other words, it lets you abort a connection (dropping data) rather than close it. Which... duh... is exactly the behaviour that you actually expected from the beginning.

 

Luckily, this indeed seems to work consistently pretty much everywhere. At least under Windows, Linux, FreeBSD, and OS X it certainly does.

 

There might be the temptation to use linger with a non-zero time. I don't recommend doing that since it is not very useful, and also the results are a bit unpredictable. OS X has a somewhat funny idea of what the word "seconds" means, and Cygwin (at least the 1.7.xxx version that I tried) totally fucks up, blocking infinitely.

Edited by samoth

Share this post


Link to post
Share on other sites
On 2016/5/30 at 3:10 PM, Denroues said:

 

Documentation for Winsock closesocket() is here:

 

https://msdn.microsoft.com/en-us/library/windows/desktop/ms737582(v=vs.85).aspx

 

Especially this bit:

 

If the l_onoff member of the linger structure is nonzero and l_linger member is zero, closesocket is not blocked even if queued data has not yet been sent or acknowledged. This is called a hard or abortive close, which stops data transfer from the socket.

 

Other TCP implementations may do different things -- read your OS man pages!

 

This only works on Windows platform? 

 

 

That didn't work for me!

 

On 2016/5/27 at 10:50 AM, Werfkjl said:

 

Documentation for Winsock closesocket() is here:

 

https://msdn.microsoft.com/en-us/library/windows/desktop/ms737582(v=vs.85).aspx

 

Especially this bit:

 

If the l_onoff member of the linger structure is nonzero and l_linger member is zero, closesocket is not blocked even if queued data has not yet been sent or acknowledged. This is called a hard or abortive close, which stops data transfer from the socket.

 

Other TCP implementations may do different things -- read your OS man pages!

 

This only works on Windows platform? 

 

same question

Me too.

Share this post


Link to post
Share on other sites

The question seems like it is entirely answered in this thread.

What is that that is not working for you?

Can you post code for setting the linger option and closing the socket?

Can you describe what you expect to happen in the code?

Can you describe what you observe actually happening, and why you think that's not what should happen?

We can't read your mind, and thus can't answer your question without information.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Advertisement