Implementing client-server communication using serialization and TCP/IP in C#

As software developers, we are always developing applications that can communication with other components: A server side script that echoes html to the browser, the client application that send information to a remote server endpoint and etc. One of the requirements that I got from my project was to display feedback from a windows service. However, because of session 0 isolation in windows 7, invocations of visual display logic from the windows service application is not enough to fulfill the requirement. In order to display feedback from a windows service application, I created a separate form application that runs when users log in and have the form application connects to the windows service application via TCP/IP to listen for feedback. Communication between the two applications is achieved via Object Serialization in .NET framework.

Reasons for using Object Serialization over TCP/IP

While there are a few ways to workaround the session 0 isolation problem, I chose TCP/IP mainly because of my familiarity with the protocol. In addition, the keep alive nature of the protocol made the coding work more straightforward. By using serialization and TCP/IP as the communication protocol between my applications, I saved some time for developing other areas of my project.

Defining the message payload

I modeled the Message class based on the ShowBalloonTip method of the NotifyIcon class. Hence, each message received from the windows service application includes a title, a content/description, as well as a type. With this design in mind, defining the message payload is pretty straightforward:

using System;
using System.Runtime.Serialization;

// Enumeration that define the type of message
public enum MessageType { Error, Warning, Info }

[Serializable]
public class Message
{
    private MessageType _messageType;
    private string _messageTitle;
    private string _messageContents;

    public Message(string title, string contents, MessageType type)
    {
        this.Title = title;
        this.Contents = contents;
        this.Type = type;
    }

    public MessageType Type
    {
        get
        {
            return this._messageType;
        }
        set
        {
            this._messageType = value;
        }
    }

    public string Title
    {
        get
        {
            return this._messageTitle;
        }
        set
        {
            this._messageTitle = value;
        }
    }

    public string Contents
    {
        get
        {
            return this._messageContents;
        }
        set
        {
            this._messageContents = value;
        }
    }
} // end public class Message

This is it, by marking the class with Serializable attribute, instances of the Message class can be sent and received via Object Serialization. The next step would then to implement the MessageServer and the MessageClient classes.

The MessageServer class

The MessageServer class utilizes TcpListener to allow instances of MessageClient to connect to it. Connected clients are then stored in a Dictionary with the username of the current active client, which is retrieved via Machine.getInstance().getUsername() function that was discussed in a previous post. Also, the MessageServer provides a SendMessageToActiveClient method that utilizes the Serialize method of BinaryFormatter to send instances of the Message class through the TcpClient instance that is associated with the active user. With such configurations, whenever there is a feedback from the windows service application, the feedback is always sent to the windows form application instance of the currently active user. The windows form application instance can then display the feedback to the active user.

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;

public class MessageServer
{
    private int _port;
    private TcpListener _tcpListener;
    private Dictionary<string, TcpClient> _clientsDictionary;
    private bool _running, _disposed;

    private BinaryFormatter _bFormatter;

    private Thread _connectionThread;

    // Create a message server that listens on the indicated port
    public MessageServer(int port)
    {
        this._port = port;
        this._tcpListener = new TcpListener(IPAddress.Loopback, port);
        this._clientsDictionary = new Dictionary<string,TcpClient>();
        this._running = false;
        this._bFormatter = new BinaryFormatter();
    } // end public MessageServer(int port) 

    public void Start()
    {
        if (!_running) 
        {
            this._tcpListener.Start();
            this._running = true;
            this._connectionThread = new Thread
                (new ThreadStart(ListenForClientConnections));
            this._connectionThread.Start();
        } // end if (!_running)

    } // end public void Start()

    public void Stop()
    {
        if (this._running) 
        {
            this._tcpListener.Stop();
            this._running = false;
        }
    } // end public void Stop()

    public bool Running()
    {
        return this._running;
    } // end public bool Running()

    // Thread body for listening for client connections
    private void ListenForClientConnections()
    {
        while(this._running) 
        {
            TcpClient connectedTcpClient = this._tcpListener.AcceptTcpClient();
            // Remember the current client connection
            string activeUsername = Machine.getInstance().getUsername();
            lock (this)
            {
                // If there is another connection from a same client
                if (this._clientsDictionary.ContainsKey(activeUsername))
                {
                    // close the connection.
                    this._clientsDictionary[activeUsername].Close();
                    // Remember the new connection
                    this._clientsDictionary[activeUsername] = connectedTcpClient; 
                }
                // Else 
                else
                {
                    // Remember the new connection
                    this._clientsDictionary.Add(activeUsername, 
                        connectedTcpClient);
                } // end if

            } // end lock(this._clientsDictionary)
        } // end while(this._running)
    } // end private void ListenForClientConnections()

    // Send a message to the currently logged in user
    public void SendMessageToActiveClient(Message message)
    {
        lock(this) 
        {
            // Get the current active user
            string activeUsername = Machine.getInstance().getUsername();
            // If client had connected to the message server
            if (this._clientsDictionary.ContainsKey(activeUsername))
            {
                try
                {
                    // send message to client
                    this.sendMessage
                        (this._clientsDictionary[activeUsername], message);
                }
                catch (Exception)
                {
                    // close the client connection
                    this._clientsDictionary[activeUsername].Close();
                    // Remove the client connection from memory
                    this._clientsDictionary.Remove(activeUsername);
                } // end try-catch
            } // end if
        } // end lock
    } // end public void sendMessageToActiveClient()
    
    private void sendMessage(TcpClient client, Message message)
    {
        // Send message to the client.
        _bFormatter.Serialize(client.GetStream(), message);
    } // end private void sendMessage(TcpClient client, Message message)

} // end public class MessageServer

The MessageClient class

The MessageClient utilises the TcpClient class to connect to the MessageServer. After connecting to the MessageServer, the TcpClient uses the Deserialize method of the BinaryFormatter class to listen to instances of Message received from the MessageServer. When an instance of Message is received, the MessageClient will trigger the MessageReceived event.

using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;

public delegate void MessageReceivedEventHandler(object sender, Message message);

public class MessageClient
{
    private int _port;
    private TcpClient _tcpClient;
    private BinaryFormatter _bFormatter;
    private Thread _listenThread;
    private bool _running, _disposed;
    public event MessageReceivedEventHandler MessageReceived;     


    public MessageClient(int port)
    {
        this._port = port;
        this._tcpClient = new TcpClient("localhost", port);
        this._bFormatter = new BinaryFormatter();
        this._running = false;
    } // end public MessageClient(int port)

    public void StartListening()
    {
        lock (this)
        {
            if (!_running)
            {
                this._running = true;
                this._listenThread = new Thread
                    (new ThreadStart(listenForMessage));
                this._listenThread.Start();
            }
            else
            {
                this._running = true;
                this._tcpClient = new TcpClient("localhost", this._port);
                this._listenThread = new Thread
                    (new ThreadStart(listenForMessage));
                this._listenThread.Start();
            } // end if (!_running)
        } // end lock (this)
    } // end public void StartListening()

    private void ListenForMessage()
    {
        try
        {
            while (this._running)
            {
                // Block until an instance Message is received 
                Message message =
                    (Message)this._bFormatter.Deserialize
                        (this._tcpClient.GetStream());
                // Notify all registered event handlers about the message.
                if (MessageReceived != null && message != null) 
                {
                    MessageReceived(this, message);
                } // end if MessageReceived != null
            } // end while
        }
        catch (Exception)
        {
            this._running = false;
        } // end try-catch
    } // end private void ListenForMessage();

    public void StopListening()
    {
        lock (this)
        {
            if (this._running)
            {
                this._tcpClient.Close();
                _running = false;
            }
        } // end lock(this)
    } // end public void StopListening()

} // end class MessageClient

Some posts that may interest you

Advertisements

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people.

2 Comments

  • Rob
    November 15, 2012 at 4:28 am

    I really like this post. However, looking at the MessageServer code, it’s hard to see how the server can store multiple MessageClients (tcp clients). I can see a dictionary, but how does it get populated with more than one client The following line suggest that the activeUsername will always be the same, i.e the user name of the machine, running the server (TcpListener).

    string activeUsername = Machine.getInstance().getUsername();

    Am I missing something.

    • Clivant
      November 20, 2012 at 2:58 pm

      Hi Rob,

      Thank you for your thoughts.

      This post is an abstract from a project which I had done in the past. Machine.getInstance().getUsername() is supposed to return the username of the current client (windows user) who just logs into the machine.

      You can find out more about the Machine class in “How to retrieve the username of the user who logged onto Windows from windows service“.

Advertisements
Advertisements