This article is aimed at socket programmers, using C or C++, with WinSock libraries. The following topics are covered:
- Blocking and non-blocking sockets
- Asynchronous socket programming techniques
- Programming server socket connections
WinSock programmers will need to remember that the function calls are only valid once the WSAStartup WinSock API function has been called, and that errors have to be checked by calling the WSAGetLastError function, where return codes from socket functions may be unreliable or not detailed enough.
Linux programmers can use the same techniques, but implementations will vary slightly, and they need to check their documentation for equivalents of the WSAStartup and WSAGetLastError function calls.
Blocking Socket Calls
Essentially, all socket calls are blocking in nature. In other words, they do not return control to the calling code until they complete.
Ordinarily, this wouldn't matter, except that in network programming in general, and socket programming in particular, most operations fall into one of two categories:
- they take time to complete
- they rely on a third party to complete
For example, the accept function call will block until there is a connection available to be accepted.
While blocked, the call will not return, and subsequently, no more processing can take place, which is especially dangerous in a single threaded application.
Non-Blocking Sockets
Setting a socket to be 'non-blocking' simply means that the library is instructed not to block when it is called. So, if accept is called on a socket, and there are no connections pending, rather than waiting for one, control will be returned to the calling block.
To set a non-blocking socket, the ioctlsocket function is used, specifying FIONBIO as the parameter, and 1 as the argument.
Simple Polling Asynchronous Sockets
Asynchronous socket programming simply disassociates the sending and receiving of data on a socket, i.e. the program does not wait for data to be received, either as a response to data sent, or ever. Clearly, non-blocking sockets are required for this technique to be used.
It is the responsibility of the application managing the server socket connections to poll the non-blocking, asynchronous sockets for data.
This can be achieved within an infinite loop, by first allocating a pool of available sockets (or managing a dynamic heap of sockets), and then using accept and recv to manage the connection and data exchange.
The sockets are created, bound to the local IP address using bind, and then set to listen for incoming connections, before being set up as non-blocking using the ioctlsocket function as described above. All this is done outside of the loop which monitors for connections using an accept call on the current socket.
Once a socket is accepted, send and recv can be used to communicate, and the next socket in the pool becomes the one that will be used to accept new calls. If zero data is returned, the connection can be terminated, and the socket returned to the pool.
The golden rule as far as WinSock programming with non-blocking asynchronous sockets is concerned is to remember that each call to a socket that has no operations available for it will result in an error. Checking WSAGetLastError will usually return a value of 10035 (WSAEWOULDBLOCK), which just means that, if this were a blocking socket, the operation would block. Any other return code can be considered to be a 'real' error.
Using the above techniques the socket programmer can ensure that data is effectively processed without neglecting any of the concurrent connections, and that potentially blocking socket calls are avoided.