I had to recently write a client-server socket application and so I was mulling over all that needs to be considered when programming with sockets especially using TCP. I have summarized them below.
1. Partial Packets – Never assume that how much ever you send from one side will be received at the other side in a single receive. You might have to queue in multiple receives. So incase you need the data in full for processing, send the size of the data before the data itself, so that you can wait for so many bytes to arrive before processing the data,
2. Message boundaries :
a. A single receive posted at B might get all the data sent by A.
b. Or the sends might be broken and merged with subsequent sends - If A sends abcde followed by fghijkl where a..l represent different bytes, B can receive abc, then in another receive defg and then hijkl or as abcdefghij and then kl
[http://blogs.msdn.com/joncole gives sample code and explains this in great detail]
3. The above argument is true for subsequent synchronous sends. When asynchronous sends are queued, there is no guarantee in the order the sends are processed, i.e., The second BeginSend call could have been processed before the first.
3. When A does a graceful close, read on B returns 0 bytes. This is the most commonly used mechanism to know that the other end has closed the connection.
4. When Nagling is enabled, the small sends are delayed. So if you have small packets that need to be sent soon, disable nagling, by setting Socket.NoDelay to true.
5. Eventhough you supply a very high value in socket.Listen parameter, the OS has a limit on these values (http://support.microsoft.com/kb/127144/en-us)
From the Support site:
The WinSock listen() call backlog parameter under Windows NT version 3.5 and 3.51 accepts a maximum value of 100. The maximum backlog parameter is 5 on Windows NT 4.0 Workstation, and 200 on Windows NT 4.0 Server. WinSock server applications use the listen() call to establish a socket for listening for incoming connections. This calls second parameter, backlog, is defined as the maximum length to which the queue of pending connections may grow. The WinSock version 1.1 specification states the maximum value for the backlog parameter is 5. However, Windows NT version 3.5 accepts up to 100 for this value.
6. To prevent the TCP Syn attack, whenever a SYN is received, a SYN+ACK is immediately sent irrespective of whether the server can handle the request or not. This SynProtection mechanism is available in Win2k3 and higher. How does this get into our path when we use System.Net to do socket programming as we don’t deal with SYN packets?
Here is the scenario.
If suppose the server listens with a backlog of 2 (just for the sake of explanation I have used the value 2) and it has not yet issued the Accept call. If 5 clients try to connect, the connect from all the 5 clients will succeed as the clients get SYN +ACK immediately from the server. But when they try to send data before the server issues Accept, the send fails with the following exception. Only on a send or the first IO request, the winsock allocates data structures for the socket.
Here is the stack trace from my small repro code.
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.Send(Byte buffer, Int32 offset, Int32 size, So
at System.Net.Sockets.Socket.Send(Byte buffer)
So a successful connect need not necessarily mean that client is connected. A subsequent send should pass too.
7. When the socket is disconnected (note: not closed), you can use the same socket only to connect to some other server and through one of the asynchronous APIS only
8. There is no send/receive timeout for asynchronous calls. They are valid only for synchronous as it is very difficult to cancel overlapped calls, when timeout occurs.
byte b=new byte;
clientSocket.BeginReceive(b, 0, 100, SocketFlags.None, new AsyncCallback(ReceiveCallback), clientSocket);
when there is no send on the other end, the ReceiveCallback is never called at all, even after the receivetimeout that has been set.