Reputation: 661
In Berkeley sockets, the accept() function is used to accept an incoming connection. If the TCP protocol is used, then, does this function block until the TCP connection establishment (SYN, then SYN-ACK, then ACK) completes?
If this is the case, then, how can I write a server program that handles incoming connections safely? The TCP connection establishment may take a long time (for example, the client sends the ACK after a long time), or even never finish (for example, the client never sends the ACK). So, the server may be blocked in accept() for a long time or even infinitely.
I've heard that there is an non-blocking mode. But I don't know how to use it to avoid this problem. It seems that, even in the non-blocking mode, when there is an incoming connection, the accept() still blocks until the incoming connection has been processed.
Upvotes: 0
Views: 109
Reputation: 73039
If the TCP protocol is used, then, does this function block until the TCP connection establishment (SYN, then SYN-ACK, then ACK) completes?
Sort of -- in the default (blocking) socket-mode, accept()
won't return until either it has a connected TCP-connection-socket to return to you, or until an error occurs. Since there might not be any incoming connections pending (or indeed even any clients out in the world trying to connect), that means that accept()
might not return for a very long time (possibly never).
Note that the accept()
call doesn't actually send or receive SYN or ACK packets -- all that implicit network I/O occurs asynchronously inside the networking stack, regardless of what functions your program is or isn't calling. All the accept()
call does is try to pop a TCP socket out of the kernel's incoming-connection-sockets queue and hand it over to your code (and in blocking mode, it will also block until such a TCP socket is available to return).
If this is the case, then, how can I write a server program that handles incoming connections safely? [...] So, the server may be blocked in accept() for a long time or even infinitely.
That's true, so either you need to write your program in such a way that it's okay for accept()
to not return for a long time (e.g. by placing the accept()
call into a separate thread), or you need to put your accepting-socket into non-blocking mode so that accept()
won't block.
I've heard that there is an non-blocking mode. But I don't know how to use it to avoid this problem. It seems that, even in the non-blocking mode, when there is an incoming connection, the accept() still blocks until the incoming connection has been processed.
If the socket you pass into accept()
is in non-blocking mode, then the accept()
call is guaranteed not to block -- either it will immediately return a valid incoming socket (if there is an incoming TCP connection that is already ready-for-use and waiting for you inside the networking stack), or it will return -1 and set errno to EWOULDBLOCK
(if there isn't one).
Therefore, the traditional way to deal with accept()
in a non-blocking manner would be to pass your accepting-socket into select()
(or poll()
, or some other similar block-until-one-of-these-sockets-has-IO-ready-for-me type of multiplexing API), and when the accepting-socket selects() as ready-for-read, that's when you know an incoming TCP connection is available for pickup, and that's when it's a good time to call accept()
to retrieve it.
If you want to also handle other sockets (e.g. the accepted TCP connections themselves) within the same thread, you can pass them in to the select()
or poll()
call along with your accept-ing socket, so that you can get notified when they have I/O events ready for you to handle also.
Upvotes: 1