Socket States

Summary

WHAT ARE SOCKET STATES

Regardless of the socket type (TCP or UDP), operation mode (blocking, non-blocking, or asynchronous) or application type (single- or multi-threaded), changes in socket states are what propel a network application. Therefore, for a network application to be efficient, it needs to detect and handle changes in socket states as efficient as possible.

The state of a socket determines which network operations will succeed, which operations will block, and which operations with will fail (the socket state even determines the error code). Sockets have a finite number of states, and the WinSock API clearly defines the conditions that trigger a transition from one state to another. Note that different types of sockets ( stream vs. datagram have different states and different transitions.

DATAGRAM SOCKET STATES

The following diagram represents all states that can be detected programmatically for a datagram (UDP) socket. About the only thing that UDP socket applications care about is the readable state:



The states in the figure above are detailed as follows:

Socket State Meaning
opened socket() returned an unnamed socket (an unnamed socket is one that is not bound to a local address and port). The socket can be named explicitly with bind or implicitly with sendto() or connect().
named A named socket is one that is bound to a local address and a port. The socket can now send and/or receive.
readable The network system received data and is ready to be read by the application using recv() or recvfrom().
not writable The network system does not have enough buffers to accommodate outgoing data.
closed The socket handle is invalid.

STREAM SOCKET STATES

The following diagram represents all states that can be detected programmatically for a stream (TCP) socket:



The states in the figure above are detailed as follows:

Socket State Meaning
opened socket() returned an unnamed socket (an unnamed socket is one that is not bound to a local address and port). The socket can be named explicitly with bind() or implicitly with conenct().
named and listening The socket is named (bound to a local address and port) and is ready to accept incoming connection requests.
connection pending The network system received an incoming connection requests and is waiting for the application to respond.
connected An association (virtual circuit) has been established between a local and remote host. Sending and receiving data is now possible
readable The network system received data and is ready to be read by the application using recv() or recvfrom().
OOB readable Out Of Band data received by the network system received data and is ready to be read by the application (using recv() or recvfrom().)
Not writable The network system does not have enough buffers to accommodate outgoing data.
close pending The virtual circuit is close.
closed The socket handle is invalid.

DETECTING SOCKET CHANGES

How does one detect changes in socket state? There are three different ways

WSAAsyncSelect() is by far the preferred method for detecting socket state changes.

FUNCTION CALL SUCCESS OR FAILURE

The return value of most functions can be used to determine a socket’s state. This strategy works fine with blocking sockets, but is very inefficient with non-blocking sockets, as the application will need to perform polling in order to detect state changes. Use this method only with blocking sockets.

SYNCHRONOUS DETECTION

Synchronous detection is performed by calling blocking or non-blocking functions whose return values indicate the state of the socket.

ASYNCHRONOUS DETECTION

Asynchronous socket state detection means that WinSock DLL will notify the application any time after the asynchronous socket state detection function has been called. Notification is performed via Windows messages. Asynchronous socket state detection is performed using WSAAsyncSelect().  On error this function returns SOCKET_ERROR and WSAGetLastError() can be used to get the specific error. On success, this function returns zero to indicate that:

WSAAsyncSelect() will remain in effect until WSAAsyncSelect() is called with a different lEvent parameter, or with lEvent equal to zero.

WSAAsyncSelect() is simple to work with. Basically, you tell WSAAsyncSelect() what you want to know about, and the WinSock DLL will tell you about it when it happens. The following diagrams illustrate how WSAAsyncSelect() works for a client that sends data and reads it back, and for the server to which the client connects.


WHAT ARE THE WSAASYNCSELECT EVENTS?

The following table lists the events that WSAAsyncSelect() can detect. Note that each event is equivalent to a socket state

Event When it occurs(socket state) Meaning  Re-enable function
FD_ACCEPT  Connection request received (connection pending)  accept() likely to succeed. Must call accept() to get the socket handle for the new connection.  accept()
FD_CLOSE Connection close received (close pending)  Remote done sending data, though data may remain to be read. Therefore, call recv() to check for any remaining data. closesocket() will complete immediately.  <none>
FD_CONNECT  Connection now established (connected)  Association established so sending and receiving on the socket is possible <none>
FD_OOB  Out Of Band data with ready to read  recv() with MSG_OOB will return OOB data. Avoid using OOB at all.  recv() or recvfrom()
FD_READ  Data received by network system is ready for application to read (readable)  recv() or recvfrom() likely to succeed. If any data remains after calling recv() or recvfrom(), WinSock will post another FD_READ message.  recv() or recvfrom()
FD_WRITE Network system buffers are available for outgoing data.  send() or sendto() likely to succeed  send() or sendto()

When a call to WSAAsyncSelect() succeeds, WinSock DLL checks the current state of the socket immediately and posts a message for each requested event that matches the socket state.

WHEN TO CALL WSAASYNCSELECT ()

As a rule of thumb, call WSAAsyncSelect() immediately after you open the socket with socket(). This guarantees that you register for asynchronous event notification before the event can occur.

WHAT ARE RE-ENABLING FUNCTIONS?

Events have re-enabling functions so that WinSock DLL can avoid flooding applications with notification messages. After WinSock DLL posts a notification message, it will not post another until the application calls the enabling function for that message.

For example, WinSock DLL posts the FD_READ when data first arrives (level trigger), but does not post again as more data arrives. After the application calls recv() – the FD_READ enabling function - WinSock DLL will post another FD_READ message if there is still data available (level-trigger).  The following diagram illustrates a flowchart of WSAAsyncSelect() level triggering event notification

WHAT IS IN A NOTIFICATION MESSAGE?

A WinSock DLL notification message is handled very similarly to Windows messages where lParam and wParam are used to hold information

  wParam           : Socket handle
(LOWORD)lParam    : Event value
(HIWORD)lParam    : WinSock error value

Always use WSAGETSELECTEVENT to extract the event value from lParam, and WSAGETSELECTERROR to extract the error value from lParam. Never use WSAGetLastError() as the value returned by this function may be different from that returned by WSAGETSELECTERROR macro.

BE PREPARED FOR FAILURE

Between the time WinSock DLL posts an asynchronous notification message to your application, and the time your application acts on it, things may change. Always handle WSAWOULDBLOCK gracefully to avoid serious problems. The WinSock DLL is required to post another notification message since you called the enabling function and the state did not change when the DLL posted the first message.

LOOPING IN RESPONSE

Asynchronous event notification has an inherent latency between the event and the arrival of the event notification message. The only event affected adversely by this latency is FD_READ event. You can offset this effect by calling recv() or recvfrom() more than once in response to each FD_READ event. This will generate extra FD_READ messages each time you call and data still remains. These extra messages will enhance the effect of the looping reads.

To loop safely calling recv() or recvfrom(), you should:

CANCELING ASYNCHRONOUS NOTIFICATION

There are two ways to cancel asynchronous event notifications

Note that in both cases, asynchronous notification messages may still be in the queue. Your application should anticipate them and either avoid normal processing, or, be prepared to handle the function failure that may result (failure with WSANOTSOCK error after calling closesocket())

SAMPLE APPLICATION

<TODO>

SELECT()

Like WSAAsyncSelect(), select() is a general purpose state detection and notification function. It can detect all states detected by WSAAsyncSelect(), but does not provide the same resolution; in order to determine some socket states, you must interpret the results of a call to select() in the context of the current socket state.

The select() function operates synchronously. select() is very similar in concept to WaitForMultipleObject() Win32 API. select() takes a set of sockets and blocks until one of the sockets is ‘signalled’ Using select() involves more work in coding than WSAAsyncSelect() which is far more elegant and far more efficient.

PEEKING AT DATA

Some application might require the ability to know how much data is available, or they may need to take a look at the data before they remove it from the incoming buffer.

To detect how much data is available, use ioctlsocket() with FIONREAD flag. To copy incoming data into an application buffer without actually removing the data from the network system buffers, use recv() or recvfrom() with MSG_PEEK flag.

Using these facilities is not recommended as any peek-read is inherently inefficient. It may even cause the application to fail. Your application will work faster, be more portable, and reliable if you simply use recv() or recvfrom() to read data directly into your application.