Communication between a local and a remote socket takes time. How the application waits for the communication to complete and how it detects this determines the operation mode of the WinSock application. With respect to sockets, there are three modes of operation: blocking, non-blocking, and asynchronous.
The three operating modes of sockets compare directly to the three modes of communication when trying to reach someone by phone:
A socket returned from socket() is blocking by default. This means that WinSock functions that reference that socket handle will block until the operation completes - accept(), closesocket(), connect(), recv(), recvfrom(), send(), sendto(). There are other functions that will block irrespective of whether the socket is blocking or not – select(), gethostbyname(), gethostbyaddr(), etc.
While a blocking operation is in progress, no other network application is possible. This is called the blocking context. All calls except
WSACancelBlockingCall() will return the
WSAEINPROGRESS WinSock error.
There are a number of ways to handle this blocking context
WSACancelBlockingCall() allows you to force the completion of a blocking call by forcing failure. Note two important restrictions on the use of
WSACancelBlockingCall(): Cancellation occurs later, and further socket use is limited.
WSACancelBlockingCall() returns immediately. It does not wait for the pending operation to complete. So when this
WSACancelBlockingCall() returns, the blocking call is still pending. To handle cancellation, all blocking functions must handle the
WSAEINTR error.
Also note that after cancellation of blocking functions (other than accept() and
select() ), the only network operation guaranteed to work is
closesocket().
To handle cancellation, all blocking function calls must be ready to handle WSAEINTR error code. Here is an example of how an application may handle
canceling a blocking call
/* User requested cancellation of a blocking call
- or we had a timeout on a blocking operation and we want to cancel it */
if (WSAIsBlocking())
WSACancelBlockingCall(); // Returns immediately. Blocked functions return
WSAEINTR
/* Any blocking function must be able to handle WSAEINTR error when a blocked function is cancelled or times out
*/
nRet = recv( ... );
if (nRet == SOCKET_ERROR)
{
// recv function failed. Examine error code
nWSAError = WSAGetLastError();
// If blocking call was cancelled, abort connection
if (nWSAError == WSAEINTR)
{
// Set SO_LINGER timeout = 0 and close connection
}
else
{
// General failure. Report error
}
} // nRet == SOCKET_ERROR
else if (nRet == 0)
{
// Remote system closed stream
connection. Connection is close
}
else
{
// recv returned successfully. Check amount received and process. Also
check for user cancellation.
}
Blocking functions do not return until the function completes. What happens if the operations never completes? There are 4 basic strategies to handle this situation using timeouts.
Some functions such as connect(), send() and gethostbyname() will time out automatically. Only the network system alone determines when the timeout will occur. The WinSock API does not provide for a way to detect or change these timeout values.
Some functions such as select() and closesocket() allow an application to determine a timeout value. By default, closesocket() does not block (SO_DONELINGER) even on a blocking socket. closesocket() will only block on a blocking socket if a timeout value was set using SO_LINGER. In general, do not set a timeout value for closesocket().
Some functions like recv(), recvfrom() and accept() can block forever. These functions have no time limit and it is possible that they will never return if no data is received. On 32-bit WinSocket, SO_RCVTIMEO option can handle this specific issue.
Another way to force a timeout on a stream socket, is by setting SO_KEEPALIVE option on setsockopt(). This causes the protocol stack to sent TCP Keep-Alive packets periodically to the receiver, which is obliged to reflect the keep-alive packets back to the sender and acknowledgement of its receipt. If the sender does not receive the reflected keep-alive sockets, it returns with WSAETIMEOUT error. In general this option should not be used as it wastes bandwidth.
A blocking function like recv() or recvfrom() will return as soon as the network system copies any number of bytes. It will copy no more than the len field specifies, but it may copy less. You should always compare the return value of these functions with the expected number of bytes. You may have to receive bytes again.
Non-blocking socket functions return immediately whether the network function call completed or not. Most non-blocking operations are a hybrid of non-blocking functions and the blocking select() function to multiplex multiple sockets efficiently.
By default, socket() returns a blocking socket. A socket can be made non-blocking either implicitly or explicitly. A socket is made non-blocking implicitly using WSAAsyncSelect() (discussed later). A socket is made non-blocking explicitly using the ioctlsocket() function with FIONBIO command.
In non-blocking mode, success and failure of an operation is not absolute. Non-blocking function calls can fail with error codes that indicate success or with a socket state. For example, the WinSock error code
WSAEWOULDBLOCK means that the operation has been initiated or the user must call that function again.
It is very important to check the return value from non-blocking functions. Do not check only for success or failure. For example, compare the value returned from functions such as
recv(), recvfrom(),
send() and sendto()
to the length you requested. You may have to adjust the buffer pointer and retry the operation to transfer more.
/* Non-Blocking Socket: Illustrates how to connect to a server, send a fixed-length string, read it back, then close the connection */
/* Poll on recv() to detect connection completion. Not recommended. Use select() with readfds, or better use asynchronous FD_CONNECT notification. Note how various errors are expected as we poll */
< TO DO >
/* Poll to send data. Note that we poll as many times as needed in case we cannot send the requested buffer in one go. We retry if we get a ‘would block’ error which occurs when the network system cannot buffer our outbound data. It is also possible to poll for a certain amount of time in case the network system buffers never become available. */
< TO DO >
/* Poll to receive data (similar to sending). Only difference is that we can receive 0 from recv() which means that the server close the connection */
< TO DO >
The Windows Sockets asynchronous operation mode uses Windows messages instead of blocking or non-blocking calls. This is the preferred mode of operation.
The most significant of all is WSAAsynSelect which detects a socket state. Below is an overview of the most commonly used asynchronous functions
| Function | Purpose |
WSAAsyncGetHostByAddr |
Retrieve host information by host address |
WSAAsyncGetHostByName |
Retrieve host information by host name |
WSAAsyncGetProtoByName |
Retrieve protocol information corresponding to a protocol name |
WSAAsyncGetProtoByNumber |
Retrieve protocol information corresponding to a protocol number |
WSAAsyncGetServByName |
Retrieve service information corresponding to a service name |
WSAAsyncGetServByPort |
Retrieve service information corresponding to a service number |
WSAAsyncSelect |
Asks WinSock DLL to notify the application of certain events such as connection completion, socket closure, readiness to read, etc. |
WSAAsyncGetXByY is not an actual function but denotes a family of function for database access. Whether, we’re getting host, service, or protocol information, these function look and act the same.
The basic operation is as follows: The user calls the asynchronous function passing among other parameters a message to be received on completion, and a buffer on to which to copy the response. When the asynchronous operation is completed, WinSock DLL fills the provided buffer with the response, and fires the requested message. The application handles the message just as it would any other Windows message.
WSAAsyncSelect is used to request Windows message-based notification for a dozen of network events for a socket. Two of the events FD_CONNECT and FD_READ indicate operation completeness, where as the remaining events indicate operation readiness. Note that WSAAsyncSelect returns no handle and automatically makes the socket non-blocking.
WSAAsyncGetXByY asynchronous operations are cancelled with WSACancelAsyncRequest. Note that WSACancelAsyncRequest does not affect calls to WSAAsyncSelect. WSACleanup also cancels pending asynchronous operations.
The following table illustrates performance differences between the three possible modes op operations:
| Operation Mode | Friendly | Fast |
| Non-Blocking | þ | þþþ |
| Asynchronous | þþ | þþ |
| Blocking | þþþ | þ |
Where possible use Asynchronous mode of operation.