Calling close() on a non blocking socket from within an io watcher
schmorp at schmorp.de
Sat Aug 8 16:22:44 CEST 2009
On Sat, Aug 08, 2009 at 04:01:01PM +0200, Graham Leggett <minfrin at sharp.fm> wrote:
> > You surely mean the bit saying "If there is still data waiting to be
> > transmitted over the connection, normally close tries to complete this
> > transmission." :)
> > You quote from the wrong part of that page, namely shutdown. That has
> > obviously no relevance to close.
> Quoting from my original message:
> "If I call shutdown()/close() immediately after the write()..."
> I am calling shutdown before close, thus the reference to the behaviour
> of shutdown.
As explained in my other mail, shutdown for writing does not discard any
data, it's simply an EOF in TCP, it does not close the socket.
My point is, if you wait for an ack, then you never call close (or
shutdown) after the write.
Even though it is safe, at least for reasonable amounts of data under
normal network conditions to just close (due to lingering), if you want to
do it correctly, use an ack, then the situation you described can *never*
(under normal circumstances, shutdown for write will not even linger, so
it is completely safe, as it always prioperly sends all data)
> > Besides, nothing is said about blockign vs. non-blocking. If you think
> > a while you will realise that it doesn't matter whether the socket was
> > blocking or not, as the write behaviour is exactly the same when you write
> > any data.
> I have thought for a very long while on this, and the behaviour is very
> different. In the blocking case, the write will only return when the
> write is complete. In the non-blocking case, the write returns
> immediately, giving you the option to run shutdown()/close()
> immediately, which in this case is causing data to be discarded.
No, you are confused: non-blocking just means wait will not block when it
_cannot_ write the data.
I also explained to you that neither shutdown nor close will discard any data
(at leats not immediately). If you want to keep repeating this falseness and
somehow try to understand the behaviour then:
a) you are doomed to failed, as you cnanot understand actual behaviour by
making false assumptions. b) ask somebody else or google, or best, read a
good book (tcp/ip illustrated) when you do not believe me.
When write can write data, the behaviour is the same. In no case will
write wait until the data has been tranmitted, or received.
When write() writes some data, it doesn't matter whether the socket was in
blockign or non-blocking mode.
> As it turns out, setting SO_LINGER explicitly seems to have solved this
> on MacOSX at least. Not tried Linux yet, that's next.
Then macosx is utter crap and doesn't implement close correctly (but I
tested it some while aho, and at least 10.5 does work correctly here).
In any case, lingering will not avoid your problem. the only fix is to
properly design your protocol to wait for an ack, then the case you
describe simply cannot happen.
Your problem is a misdesigned protocol.
If you have to use the bad protocol, you have to increase lingering time or
somehow live with it.
If you can fix your protocol, do so.
> > Design your protocol that way and your problem will be gone.
> When you say "ack", an ack from what layer?
Not sure what you mean with layer, your applciation of course, in whatever
layer it finds convinient (as high as possible is usually best).
Here is an example of how typical tcp/ip protocols work (SMTP etc.):
A send data
A wait for ack/close
B receive data
B send ack
B wait for ack/close
A receive ack
B receive close
You can do it simpler sometimes, by only using close:
A send data
A wait for ack/close
B read data
A rad close
In both cases, the situsation you describe cannot happen.
When you rely on lingering, you can do it even simpler, but then you have
to live with the fact that data can get lost due to timeouts (but thats
not so bad, you alwys need a timeout anyways).
> f I expect the other side to send me a message saying "I ack your
> goodbye, goodbye", all I've done is turned the problem around, and now
> the other side is writing "goodbye", followed by "shutdown/close", with
> data loss on the other side.
Except that close does not cause data loss on a unix or windows system,
no matter how often you repeat this untrue statement.
And shutdown does not even linger, it simply waits until all data has been
transferred, and then signals an EOF to the other side.
i.e. this is guarenteed to work:
A write request data
B read data
A shutdown for writing
B gets EOF/sends response (in any order)
A read response
> Existing protocols like HTTP don't wait for an ack, how do they do it?
They do send an ack some way or another - HTTP/1.1 for example tells you
how long the request or response is before sending it (or parts of it).
HTTP/1.0 did not always do that, which is why HTTP/1.0 is not very
reliable (you cannot know whether you received all data or not in many
cases, which is why for HTTP/1.1 they fixed the protocol).
So, either protocols use an ack of some form, or they are buggy. That
applies to HTTP just as to any other protocol.
The choice of a Deliantra, the free code+content MORPG
-----==- _GNU_ http://www.deliantra.net
----==-- _ generation
---==---(_)__ __ ____ __ Marc Lehmann
--==---/ / _ \/ // /\ \/ / pcg at goof.com
More information about the libev