Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

A Simple Multi-Threaded Server Client Instant Messenger Application

4.32/5 (26 votes)
4 Jun 2006CPOL2 min read 1   4.6K  
A Simple Multi-Threaded Server Client Instant Messenger Application that uses TCP Listener and TCP Client. Allows Personal message and Conference communication between Clients and with the Server
Sample Image - maximum width is 600 pixels

The Library Project

I created a ChatLibrary that will contain the valid commands and a Message that will contain the parsing.

C#
public enum Command
{
   Login = 0,
   PersonalMessage = 1,    
   ClientList = 2,        
   Conference = 3,
   Logout = 4        
};

public class Message
{
string strSender;
string strReceiver;
Command cmdMessageCommand;
string strMessageDetail;
public Message ()
{
}
public Message (byte [] rawMessage)
{
  string strRawStringMessage = System.Text.Encoding.ASCII.GetString (rawMessage);   
  string [] strRawStringMessageArray = strRawStringMessage.Split(new char []{'|'});
  this.strSender = strRawStringMessageArray[1];
  this.strReceiver = strRawStringMessageArray[2];
  this.cmdMessageCommand = (Command) Convert.ToInt32(strRawStringMessageArray[3]);
  this.MessageDetail = strRawStringMessageArray[4];
}
...

public byte [] GetRawMessage ()
{
   System.Text.StringBuilder sbMessage = new System.Text.StringBuilder ("John");
   sbMessage.Append("|");
   sbMessage.Append(strSender);
   sbMessage.Append("|");
   sbMessage.Append(strReceiver);
   sbMessage.Append("|");
   sbMessage.Append((int)cmdMessageCommand);
   sbMessage.Append("|");
   sbMessage.Append(strMessageDetail);
   sbMessage.Append("|");
   return System.Text.Encoding.ASCII.GetBytes(sbMessage.ToString());
}
...

The Server Project

I created a SocketServer that calls the TCPListener.Start() method:

C#
IPEndPoint endPoint = new IPEndPoint (ipaAddress, iPort);
listener = new TcpListener (endPoint);
listener.Start();

Upon calling Listen, a new thread will be created that will listen for incoming clients:

C#
thrListenForClients = new Thread(new ThreadStart(ListenForClients));
thrListenForClients.Start();

The ListenForClients method will wait for connections and will assign the incoming socket to a new Client instance:

C#
Client acceptClient = new Client();
acceptClient.Socket = listener.AcceptTcpClient();
listenForMessageDelegate = new ListenForMessageDelegate (ListenForMessages);
listenForMessageDelegate.BeginInvoke
    (acceptClient, new AsyncCallback(ListenForMessagesCallback), "Completed");

The Client, by the way is a class that contains a TCPClient, so we can keep track of our connections:

C#
public class Client {
   string strName;
   TcpClient tcpClient;
   public Client()
   {
   }
   public string Name 
   {
    get{return strName;}
    set{ this.strName = value;}            
   }
   public TcpClient Socket
   {
    get{return tcpClient;}
    set{ this.tcpClient = value;}
   }
   public void SendMessage (Message sendMessage)
   {
    NetworkStream stream = tcpClient.GetStream();
    stream.Write(sendMessage.GetRawMessage() , 0, sendMessage.GetRawMessage().Length);            
   }
}

Our server is now ready to listen for incoming messages. To do this, we pass the client socket that we received, then make a loop. We use a NetworkStream to read the message:

C#
NetworkStream stream = client.Socket.GetStream();
byte [] bytAcceptMessage = new byte [1024];
stream.Read(bytAcceptMessage, 0, bytAcceptMessage.Length);
Message message = new ChatLibrary.Message(bytAcceptMessage);

Once we receive the message, we can do anything that we want:

C#
txtStatus.Text += "\r\n" + strDisplayMessageType + 
    strWriteText.TrimStart(new char[]{'\r','\n'});

My SocketServer makes use of a few events that make coding a little easier:

C#
public event ClientConnectedEventHandler ClientConnected;
public event ClientDisconnectingEventHandler ClientDisconnecting;
public event MessageReceivedEventHandler MessageReceived;

In my ServerForm code, what I did was I kept a copy of each connected Client in a ClientCollection that inherits from System.Collections.CollectionBase. With this, I can iterate through the Clients.

The Client Project

The Client does basically the same thing. I created a ClientSocket that will create a TCPListener and call Connect():

C#
IPEndPoint serverEndpoint = new IPEndPoint (ipaAddress , iPort);
tcpClient = new TcpClient ();
tcpClient.Connect(serverEndpoint);
thrListenForMessages = new Thread (new ThreadStart(ListenForMessages));
thrListenForMessages.Start();

What ListenForMessages will do is loop with NetworkStream.Read():

C#
stream = tcpClient.GetStream();
byte [] bytRawMessage = new byte [1024];
stream.Read(bytRawMessage, 0, bytRawMessage.Length);
ChatMessage receivedMessage = new ChatLibrary.Message (bytRawMessage);

Then we do the same process, create a Message using the received bytes.

Again, my goal is to create a YM/MSN - looking Instant Messenger, so I made two UI forms. The MessengerForm and the ClientWindow. The MessengerForm is the class that instantiates the ClientSocket and receives the messages. Upon receiving a message, it calls the MessengerWindow that should display the text.

Note that I didn't do a regular instantiation. I called Invoke to be able to make my controls thread safe:

C#
this.Invoke(createNewClientDelegate, new object []{receivedMessage});

History

  • 4th June 2006: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)