Recently while doing a colleague project I’ve tackled a problem of sending serialized classes via sockets. Most tutorials at the Internet base on sending strings with “EOF” sign. Managing a bunch of strings can be quite unwieldy and cumbersome. In this tutorial we’ll write a simple server with synchronous receiving and asynchronous response which will check response time not using any funny string ‘eof’ enders but a custom class.
Before starting any implementation it’s crucial to understand some basics of asynchronous programming nomenclature in .NET. Here’s a little quote about function naming(pay attention to BeginX and EndX as they refer to BeginSend, BeginReceive, BeginConnect etc.):
The BeginInvoke method is used to initiate the asynchronous call. It has the same parameters as the method you want to execute asynchronously, plus two additional parameters that will be described later. BeginInvoke returns immediately and does not wait for the asynchronous call to complete. BeginInvoke returns an IasyncResult, which can be used to monitor the progress of the call.
The EndInvoke method is used to retrieve the results of the asynchronous call. It can be called any time after BeginInvoke; if the asynchronous call has not completed, EndInvoke will block until it completes.
A full article may be found at Asynchronous Programming Overview
First let’s create a “Datagram” class. It’ll be used as an information for the recipient what to do and where shall it send the response. We will definitely use the serialization process so the attribute “[Serializable]” will be required. Let us also implement a serialization/deserialization method.
[Serializable]
class Datagram
{
public long Id;
public IPEndPoint Sender { get; set; }
public IPEndPoint Recipient { get; set; }
public String AdditionalData;
public ActionType Action;
public enum ActionType
{
PrintAdditionalData,
ResponseTimeRequest,
ResonseTimeResponse
}
static public Byte[] SerializeDatagram(Datagram datagram)
{
var ms = new MemoryStream();
var bf = new BinaryFormatter();
byte[] byteDatagram = null;
int counter = 0;
try
{
bf.Serialize(ms, datagram);
ms.Seek(0, SeekOrigin.Begin);
byteDatagram = new byte[ms.Length];
while (counter < ms.Length)
{
byteDatagram[counter++] = Convert.ToByte(ms.ReadByte());
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
ms.Close();
}
return byteDatagram;
}
static public Datagram DeserializeDatagram(Byte[] datagramBytes, int totalBytes)
{
var ms = new MemoryStream();
var bf = new BinaryFormatter();
Datagram datagram = null;
try
{
for (int offset = 0; offset < totalBytes; offset++)
{
ms.WriteByte(datagramBytes[offset]);
}
ms.Seek(0, SeekOrigin.Begin);
datagram = (Datagram) bf.Deserialize(ms);
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
ms.Close();
}
return datagram;
}
Ok, it was quite simple. Now it’s time for something more sophisticated. The NetworkServer class. It will handle all requests from other machines and if needed respond without waiting for the receipt. It’s crucial to understand what is the difference between asynchronous and synchronous communication. Working with synchronous communication is simple. If we are sending data, then using the Send method will block the application till every last byte was sent. Similarly when listening for communication (receiving) will cause the machine to halt until all data was received.
The asynchronous communication is a little different. Here there are no blocking functions. Everything works on callback functions running in background, so for example if we send data we delegate this work to some other thread meanwhile doing some more important stuff, or halt the main process (but it denies the sense of using asynchronous communication). Working with this type of communication is not for the faint of heart.
Hereunder we have a StartServer() method which loops around waiting for new messages. It is synchronous receiving. After receipt it runs TransitionState() method. It’s role is basically to process the received message for example send a response with the machine time. One more important thing that can bother you:
do {… } while (bytesRec > 0);
This loop receives data not knowing the actual size of the message. On the msdn website we can find an information that “Receive” method will halt the process until it gets all the data from the sender (I know, for some it may be not accurate enough but let us leave the actual protocol architecture) then it returns ‘0’ and we may continue with other tasks. A common mistake is to use "socket.Available" in Receive method. For instance if you change Receive to something like that:
do
{
bytesRec = handler.Receive(bytes, totalBytes, handler.Availible, SocketFlags.None); //Totally wrong!
totalBytes += bytesRec;
} while (bytesRec > 0);
You will end up with an undeterministic algorithm. As it will sometimes end the receive loop right away, event though some client has data to send.
public class NodeNetwork
{
private const int DATAGRAM_BUFFER = 10000;
private Socket _listener;
private readonly IPEndPoint _serverEndpoint;
public NodeNetwork(IPEndPoint serverEndpoint)
{
_serverEndpoint = serverEndpoint;
}
public void StartServer()//string hostNameOrAddress, int port)
{
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
_listener.Bind(_serverEndpoint);
_listener.Listen(Int32.MaxValue);
while (true)
{
Console.WriteLine("Waiting for connection...");
Socket handler = _listener.Accept();
Console.WriteLine("Receiving data from [{0}]?", handler.RemoteEndPoint);
int totalBytes = 0;
int bytesRec;
var bytes = new byte[DATAGRAM_BUFFER];
do
{
bytesRec = handler.Receive(bytes, totalBytes, 2000, SocketFlags.None);
totalBytes += bytesRec;
} while (bytesRec > 0);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
TransitionState(bytes, totalBytes); //perform action with the received data
}
}
catch (SocketException e)
{
Console.WriteLine(e);
}
}
private void TransitionState(byte[] bytesReceived, int totalBytes)
{
throw new NotImplementedException();
}
This would be it for the first part of my tutorial. In the next part you will find out how to implement the TransitionState method. It’ll use asynchronous send which will be also implemented with some tips and hints how to handle this type of communication.
