With a half-established connection I mean a connection for which the client's call to connect()
returned successfully, but the servers call to accept()
didn't. This can happen the following way: The client calls connect()
, resulting in a SYN
packet to the server. The server goes into state SYN-RECEIVED
and sends a SYN-ACK
packet to the client. This causes the client to reply with ACK
, go into state ESTABLISHED
and return from the connect()
call. If the final ACK
is lost (or ignored, due to a full accept queue at the server, which is probably the more likely scenario), the server is still in state SYN-RECEIVED
and the accept()
does not return. Due to timeouts associated with the SYN-RECEIVED
state the SYN-ACK
will be resend, allowing the client to resend the ACK
. If the server is able to process the ACK
eventually, it will go into state ESTABLISHED
as well. Otherwise it will eventually reset the connection (i.e. send a RST
to the client).
You can create this scenario by starting lots of connections on a single listen socket (if you do not adjust the backlog and tcp_max_syn_backlog
). See this questions and this article for more details.
I performed several experiments (with variations of this code) and observed some behaviour I cannot explain. All experiments where performed using Erlang's gen_tcp
and a current Linux, but I strongly suspect that the answers are not specific to this setup, so I tried to keep it more general here.
connect()
-> wait -> send()
-> receive()
My starting point was to establish a connection from the client, wait between 1 to 5 seconds, send a "Ping" message to the server and wait for the reply. With this setup I observed that the receive()
failed with the error closed
when I had a half-established connection. There was never an error during the send()
on a half-established connection. You can find a more detailed description of this setup here.
connect()
-> long wait -> send()
To see, if I can get errors while sending data on a half-established connection I waited for 4 minutes before sending data. The 4 minutes should cover all timeouts and retries associated with the half-established connection. Sending data was still possible, i.e. send()
returned without error.
connect()
-> receive()
Next I tested what happens if I only call receive()
with a very long timeout (5 minutes). My expectation was to get an closed
error for the half-established connections, as in the original experiments. Alas, nothing happend, no error was thrown and the receive eventually timed out.
send()
on a half-established connection successful?receive()
only fail if I send data first?Any help, especially links to detailed explanations, are welcome.
From the client's point of view, the session is fully established, it sent SYN, got back SYN/ACK and sent ACK. It is only on the server side that you have a half-established state. (Even if it gets a repeated SYN/ACK from the server, it will just re-ACK because it's in the established state.)
The send
on this session works fine because as far as the client is concerned, the session is established. The sent data does not have to be acknowledged by the far side in order to succeed (the send system call is finished when the data is copied into kernel buffers) but see below.
I believe here that the send actually is generating an error on the connection (probably a RST) because the receiving system cannot ACK data on a session it has not finished establishing. My guess is that any system call referencing the socket on the client side that happens after the send plus a short delay (i.e. when the RST has had a chance to come back) will result in an error.
The receive by itself never causes an error because the client side doesn't need to do anything (I mean TCP protocol-wise) for a receive; it's just idly waiting. But once you send some data, you've forced the server side's hand: it either has completed the session establishment (in which case it can accept the data) or it must send a reset (my guess here that it can't "hold" undelivered data on a session that isn't fully established).