How to abort a file transfer immediately?

Started by
19 comments, last by hplus0603 6 years, 3 months ago


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

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

Advertisement

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?

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.

enum Bool { True, False, FileNotFound };

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

same question


Same answer!
enum Bool { True, False, FileNotFound };

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!

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.

Thanks for sharing!
enum Bool { True, False, FileNotFound };
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.

This topic is closed to new replies.

Advertisement