Saturday, July 5, 2008

Sending Typed (Serialized) Messages over .NET Sockets (C# Source Code Included)

Client-server communication via .NET sockets can be established pretty easily by using the 'Sockets communication' package introduced in .NET Socket. The package supports sending/receiving raw array of bytes in two directions and allows multiple clients connection. 

image

In some applications we'll rather transfer structured data in the form of typed messages over raw array of bytes. To accomplish this we need to add another tiny layer on the top of the communication package; the extra layer serialize typed message into bytes array on the sender side and de-serialize bytes array into  typed message in the receiver side.

image

In this post you can find all the code that you need in order to add the referred layer to .NET Socket communication packaged.

Implementation

MessageComposer

MessageComoser is used to convert message (that derive from MessageBase) to bytes[] and to convert bytes to message.

public class MessageComposer
{
    public static byte[] Serialize(int messageKind, MessageBase msg)
    {
        MemoryStream ms = new MemoryStream();
        BinaryFormatter bf1 = new BinaryFormatter();

        bf1.Serialize(ms, messageKind);
        bf1.Serialize(ms, msg);

        return ms.ToArray();
    }

    public static void Deserialize(byte[] buffer, 
        out int messageKind, out MessageBase msg)
    {
        MemoryStream ms = new MemoryStream(buffer);
        BinaryFormatter formatter = new BinaryFormatter();
        messageKind = (int)formatter.Deserialize(ms);
        msg = (MessageBase)formatter.Deserialize(ms);
    }
}

 

Concrete Message

Message should inherit from MessageBase so it can be serialized and deserialized via MesageComposer. The following message carry message string and time. It is used to measure network latency.

[Serializable]
 public class SendingTimeMessage: MessageBase
 {
     private DateTime m_time;
     private string m_message;

     public SendingTimeMessage(DateTime time, string message)
     {
         m_time = time;
         m_message = message;
     }

     public DateTime Time
     {
         get { return m_time; }
     }

     public string Message
     {
         get { return m_message; }
     }

     public TimeSpan CalcSpan()
     {
         return DateTime.Now - m_time;
     }
 }

 

Sending Concrete-Message

MessageComposer is used to covert SendingTimeMessage into bytes, the bytes are than being sent using ClientTerminal.

string mes = "Message content...";

// Create the concrete message
SendingTimeMessage message = new SendingTimeMessage(mes);

int messageKind = (int)MessageKind.SendingTime;

byte[] buffer = MessageComposer.Serialize(messageKind, message);

// Send the message (as bytes) to the server.
m_ClientTerminal.SendMessage(buffer);
Receiving Concrete-Message

MessageComposer is used to covert bytes received from the client into message, the message is being converted to string and presented on the screen. Then, the bytes are being distributed to all connected clients.

void m_Terminal_MessageRecived(Socket socket, byte[] buffer)
{
    string message = ConvertBytesToString(buffer);

    PresentMessage(listMessages, string.Format("Sockets: {0}", message));

    // Send Echo
    m_ServerTerminal.DistributeMessage(buffer);
}


private string ConvertBytesToString(byte[] bytes)
{
    int messageKind;
    MessageBase msg;
    MessageComposer.Deserialize(bytes, out messageKind, out msg);

    MessageKind kind = (MessageKind) messageKind;

    switch(kind)
    {
        case MessageKind.SendingTime:
            SendingTimeMessage sendingTimeMessage = (SendingTimeMessage)msg;
            return "SendingTimeMessage: " + sendingTimeMessage.Message;

        case MessageKind.Simple:
            SimpleMessage simpleMessage = (SimpleMessage)msg;
            return "SimpleMessage: " + simpleMessage.Message;
    }

    return "UnKnown";
}

 

Sample project

Download from here

6 comments:

  1. Great article - thanks for posting!

    ReplyDelete
  2. I've download sample project.
    There's a problem in reading data from network in ClientTerminal class. The amount of bytes read from the socket may vary. Here's more information about the issue (http://vadmyst.blogspot.com/2008/03/part-2-how-to-transfer-fixed-sized-data.html).

    ReplyDelete
  3. Thank you. Super.
    I wish you peace of mind.

    ReplyDelete
  4. Thank you for the nice and quick explanation :)
    The Sample File cannot be downloaded anymore :(

    ReplyDelete
  5. Can you please upload the code somewhere.The existing link is not accessible

    ReplyDelete
  6. Done, the project is now shared via google.docs, you can use the same link to download. If you still have problems, send me an email and I will provide you with a private copy.

    ReplyDelete