2009-05-27

Using Socket as a Server (Listening) Socket

(This post is part of the TCP/IP .NET Sockets FAQ)

Normally, server sockets may accept multiple client connections. Conceptually, a server socket listens on a known port. When an incoming connection arrives, the listening socket creates a new socket (the "child" socket), and establishes the connection on the child socket. The listening socket is then free to resume listening on the same port, while the child socket has an established connection with the client that is independent from its parent.

One result of this architecture is that the listening socket never actually performs a read or write operation. It is only used to create connected sockets.

The listening socket usually proceeds through the operations below.

  1. Construct. Socket construction is identical for all TCP/IP sockets; see Socket Operations for details.
  2. Bind. Binding for listening sockets is usually done only on the port, setting the IP address parameter to IPAddress.Any (MSDN). A Bind failure is usually due to another process already bound to that port (possibly another instance of the server process).
  3. Listen. The listening socket actually begins listening at this point. It is not yet accepting connections, but the OS may accept connections on its behalf.
    The confusing "backlog" parameter. The "backlog" parameter to Socket.Listen is how many connections the OS may accept on behalf of the application. This is not the total number of active connections; it is only how many connections will be established if the application "gets behind". Once connections are Accepted, they move out of the backlog queue and no longer "count" against the backlog limit.
    The value to pass for the "backlog" parameter. Historically, this has been restricted to a maximum of 5, though modern systems have a cap of 200. Specifying a backlog higher than the maximum is not considered an error; the maximum value is used instead. The .NET docs fail to mention that int.MaxValue can be used to invoke the "dynamic backlog" feature (Windows Server systems only), essentially leaving it up to the OS. It is tempting to set this value very high (e.g., always passing int.MaxValue), but this would hurt system performance (on non-server machines) by pre-allocating a large amount of scarce resources. This value should be set to a reasonable amount (usually between 2 and 5), based on how many connections one is realistically expecting and how quickly they can be Accepted.
  4. (repeat) Accept. When a socket connection is accepted by the listening socket, a new socket connection is created. The listening socket should continue listening on the same port by re-starting the Accept operation as soon as it completes. The result of a completed Accept operation is a new, connected socket. This new socket may be used for reading and writing. For more information on using connected sockets, see Using Socket as a Connected Socket. The new socket is completely independent from the listening socket; closing either socket does not affect the other socket.
  5. Close. Since the listening socket is never actually connected (it only accepts connected sockets), there is no Disconnect operation. Rather, closing a listening socket simply informs the OS that the socket is no longer listening and frees those resources immediately.

There are a few common variations on the above theme:

  1. A listening socket may choose to bind to an actual IP address in addition to a port. This is normally done for security reasons. If this is done, then the Bind operation may fail if the network cable is unplugged or wireless router is down.
  2. A listening socket may choose not to bind (actually, the socket is still bound; it is just bound to an OS-chosen port). This is extremely rare, and only found in very old protocols such as non-PASV FTP. This requires an application protocol that can notify the other side of the port that the OS chose to bind, and this tight coupling of the application protocol (e.g., FTP) with the transport mechanism (e.g., TCP) is not recommended. One reason is that it requires any NAT'ing (network address translating) devices to monitor the protocol and dynamically predict the necessary port forwarding.

(This post is part of the TCP/IP .NET Sockets FAQ)

16 comments:

  1. I must say this is the best and most organized resource regarding TCP/IP sockets. I wish you had it published in book.

    ReplyDelete
  2. This post clarifies many misconception !

    ReplyDelete
  3. it is nice explanation
    i have one quietions that what would happen if one to one connection is stablished and an other client try to connect?

    ReplyDelete
  4. @Anonymous:

    Once the connection is established (i.e., the Accept operation completes), the server socket normally begins another Accept operation immediately to allow other clients to connect.

    If a client attempts to connect in the time after the first Accept completes and before the second Accept starts, then it is placed into the "backlog" queue. As soon as the second Accept starts, it will complete because there's already a connection in the backlog queue.

    ReplyDelete
  5. Best article I found net. Really thanks. I can buy your book if u write

    ReplyDelete
  6. Nice explanation of the backlog parameter. I would not have known about the int.MaxValue trick for server class systems.

    -Brian Gideon

    ReplyDelete
  7. re: Accept - The new socket is completely independent from the listening socket

    Does the new socket inherit the listening socket's properties? Specifically, if the listening socket was set to be non-blocking, is the new socket also non-blocking?

    Thanks!

    ReplyDelete
    Replies
    1. Yes, it does inherit those properties. From MSDN (WSAAccept): "The newly created socket has the same properties as socket s including asynchronous events registered with WSAAsyncSelect or with WSAEventSelect."

      Note that "non-blocking" has a special meaning for sockets. Sockets can be blocking, non-blocking, or asynchronous, and you want asynchronous, not non-blocking.

      Delete
  8. Hi, very nice resource. Can you please explain what happened if the backlog queue is full ? Do you think that is it a good idea to increase backlog size in order to run load tests ?
    thanks

    ReplyDelete
    Replies
    1. If the backlog queue is full, then connections are rejected (before they have a chance to be accepted).

      You should not increase the backlog size when running load tests; the backlog queue is specifically meant as a throttling mechanism, and in a load test it's important to determine how the system will actually behave in production. IMO, of course. :)

      Delete
    2. first of all thanks for your quick answer. and for sur you're right about the load test :-). In our case we create severals instance of clients. They are all distributed on virtual machine. The backlog size is set to 10000 connections and ours clients received socket connection exception from a certain number attempt of new connections. I'm trying to find if the problem may come from the backlog size ...

      Delete
    3. Sorry, I just have one more question. Is it possible to know how many connections are avalaible in queue ?

      Delete
    4. Not to my knowledge. Remember, the backlog queue is intended to just be a small buffer in case your server gets temporarily behind. You should always be accepting new connections, and the backlog should hardly ever be used.

      Delete
  9. very nice article!!! Thnx a lot...
    i have one question. When a new connection is accepted, it communicates on different port number?? because in java when we create server socket we specify a particular port number, like ServerSocket serverSoc = new ServerSocket(8989). So server socket is listening at port 8989. What happens when multiple clients are requesting? New connection is accepted as Socket clientSoc = serverSoc.accept(). So what about port number?

    ReplyDelete
    Replies
    1. The new socket connection is actually bound to the same (local) port as the listening socket. This works because the remote address/port is different. I've updated this post to be a bit less confusing.

      The "uniqueness" of a TCP/IP socket is based on four variables: local address, local port, remote address, remote port. Not just the local port.

      Delete
    2. Thanks a lot sir!!!:-) This article cleared my few doubts:-)

      Delete