Thursday, August 18, 2011

The listen(2) Function (socket)

The listen(2) function is how the server is able to express its interest in listening for connections. The function is very simple to call, and its function prototype is given as follows:

#include <sys/socket.h>
int listen(int s,int backlog);

The two input arguments are the following:
  • The socket s to use as the listening socket.
  • The backlog, which specifies the connect queue length in entries.

The function returns zero when it is successful. Otherwise -1 is returned, and the reason for the error is posted to errno.

Understanding the Connect Queue

It might seem odd that the application programmer would have to supply something apparently so cryptic as backlog parameter value in the call to the listen(2)function. However, there is a sound reason for it. Figure 8.2 shows the general activity of the very popular www.woohoo.com Web server.


Figure:
The Linux kernel manages the backlog queue when accepting new connections.

Notice the block in Figure above that is labeled listen(2). This block represents the call to listen(2), which establishes a listening queue. A small arrow points up to the queue it
established, which resides within the Linux kernel. The length of this queue is determined by the backlog argument of the listen(2) call. Now look at the block at the bottom right of Figure 8.2. This block is labeled "Process Client Request." The arrow labeled as number one represents the very first connection into your server. In Figure above, this connection has just been accepted by the accept(2) function call and is currently being processed by your server code. However, while server processing is taking place on the first client connection, more connection requests are coming into your server from all over the world. Within the queue that listen(2) established, you have connect requests two through five pending and waiting to be accepted. Figure above shows another connect request being inserted into the tail end of the queue as request number six by the Linux kernel. Even as that is happening, connect requests seven, eight, and nine are being received. A busy server indeed!

The thrust of Figure above has been to demonstrate the purpose of the backlog argument, within the listen(2) function call. As you have seen, this parameter sets the length of the incoming queue.
Now that you understand its purpose, let's discuss what practical values should be used in this parameter.

Specifying a Value for backlog

Historically, the value of backlog in the listen(2) call has been less than precise. In the early days of UNIX, the value 5 was commonly used. However, with the faster and busier systems of
today, this value might not be suitable for your server application. So how does one determine a reasonable value?
UNIX literature advises not to use a value of 0 in the backlog argument. This is good advice to programmers writing applications that must be ported to various flavors of UNIX, including Linux.
The reason is that for some platforms this means no connections are accepted. For others, it means that at least one connection can be pending in accept(2). A negative value does not make sense, and so it should not be used.

The man page for listen(2) indicates that the behavior of the backlog argument changed at Linux kernel release 2.2. Previously, this count included connections that were still establishing
communications with the listening socket. As of 2.2 and later, this backlog count only pertains to those connections that have been established with the listening socket, but are waiting to be accepted by the server.

It would seem that for small servers, the queue length should be specified as 5 or more. For Web servers, however, you might need to experiment with larger numbers. Some tests published by
Richard Stevens suggest that for a Web server that is receiving approximately 45,000 connects per hour, you might want to use a backlog length of 16 or more.

The final value chosen for the backlog parameter depends largely upon the amount of elapsed time between each accept(2) function call. If your server accepts one connection and then completely processes this request before accepting the next connection, you'll want to use a larger backlog value. The longer each request takes to process in this scenario, the more critical the backlog queue length becomes.

If, on the other hand, your server can concurrently process several client connections, the backlog parameter value will be lower. This is true because the server will loop back and accept the next pending connect, within the limits of a very efficient processing loop. Processing multiple clients concurrently within a server will be covered in coming post, please wait for that.

No comments:

Post a Comment