Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C#
Article

A Simple Multi-Threaded Server Client Instant Messenger Application

Rate me:
Please Sign up or sign in to vote.
4.32/5 (26 votes)
4 Jun 2006CPOL2 min read 157.8K   4.6K   109   38
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)


Written By
Web Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIn VS.net 10 : error in connecting client to server ! Pin
Dariush Ajami11-Jun-17 23:55
Dariush Ajami11-Jun-17 23:55 
GeneralCross threading issue in Visual Studio 2013... Pin
Arslan Tariq22-May-14 9:20
Arslan Tariq22-May-14 9:20 
Generalthis code can not send bytes>1024 Pin
daodetianzun19-Sep-08 4:09
daodetianzun19-Sep-08 4:09 
GeneralGreat but there are some errors to modify Pin
Sugathe0427-Aug-08 18:43
Sugathe0427-Aug-08 18:43 
GeneralHelp Please: If I kick a client, or client click disconnect, the server raises a error of "Server has stopped working" Pin
JameYoon2-Jul-08 17:35
JameYoon2-Jul-08 17:35 
GeneralPlease Help :) If I stop the Server(even if there is no client connected), it arises an error. [modified] Pin
JameYoon1-Jul-08 18:18
JameYoon1-Jul-08 18:18 
QuestionNot sure how to use!!! Pin
Lecram21-Jun-08 20:34
Lecram21-Jun-08 20:34 
GeneralCross-thread operation not valid: Control 'txtStatus' accessed from a thread other than the thread it was created on. Pin
Shawn12814-Jun-08 22:42
Shawn12814-Jun-08 22:42 
AnswerRe: Cross-thread operation not valid: Control 'txtStatus' accessed from a thread other than the thread it was created on. Pin
Jason Barry25-Jun-08 2:57
professionalJason Barry25-Jun-08 2:57 
GeneralRe: Cross-thread operation not valid: Control 'txtStatus' accessed from a thread other than the thread it was created on. Pin
Member 87892936-Apr-12 10:16
Member 87892936-Apr-12 10:16 
QuestionClient IP-Address problem Pin
Husti5-Apr-08 9:03
Husti5-Apr-08 9:03 
GeneralSystem.ObjectDisposedException after disconnect Pin
Gaara [BTK]15-Aug-07 14:22
Gaara [BTK]15-Aug-07 14:22 
GeneralRe: System.ObjectDisposedException after disconnect Pin
Antbu1325-Oct-07 20:41
Antbu1325-Oct-07 20:41 
GeneralRe: System.ObjectDisposedException after disconnect Pin
Eagle196520-May-09 10:43
Eagle196520-May-09 10:43 
Generalproblem in running this application [modified] Pin
vrani12-Jul-07 19:38
vrani12-Jul-07 19:38 
GeneralMulti-threading error fix Pin
stormist10-Jun-07 13:43
stormist10-Jun-07 13:43 
I am using Visual Studio 2005, and was unable to run this because of threading errors. The writes to the main messagebox windows are done from seperate threads, which my version will not allow to compile. The fix is to create two methods:
private void SendTo_txtStatus(string txt)
{
if (this.InvokeRequired)
{
this.EndInvoke(this.BeginInvoke(new MethodInvoker(delegate() { SendTo_txtStatus(txt); })));
}
else
{
txtStatus.Text += txt;
}
}

and
private void SendTo_1stClients(string txt) //Event handlers run on a seperate thread, so direct access to txtbox is not possible. We must run invoke with a delegate.
{
if (this.InvokeRequired)
{
this.EndInvoke(this.BeginInvoke(new MethodInvoker(delegate() { SendTo_1stClients(txt); })));
}
else
{
lstClients.Items.Add(txt);
}
}

then replace any direct calls to update the main form textbox and listbox with the above.

Does anyone know why I recieved this error and no one else did? I'm assuming its my version but what version are you guys using?

Generaltextbox Pin
dark_magician_65-May-07 4:50
dark_magician_65-May-07 4:50 
Questionnot working on lan plz help me Pin
Member 37941123-Mar-07 0:26
Member 37941123-Mar-07 0:26 
Questionit is working in LAN but what do i need to do for WAN ? Pin
evalenzuela2-Feb-07 11:57
evalenzuela2-Feb-07 11:57 
GeneralError Pin
Christian Siebmanns7-Oct-06 5:04
Christian Siebmanns7-Oct-06 5:04 
GeneralRe: Error Pin
bubbler_below9-Oct-06 17:09
bubbler_below9-Oct-06 17:09 
GeneralRe: Error Pin
bubbler_below10-Oct-06 22:25
bubbler_below10-Oct-06 22:25 
GeneralRe: Error Pin
Christian Siebmanns21-Oct-06 5:24
Christian Siebmanns21-Oct-06 5:24 
AnswerRe: Error Pin
duffer_0113-Apr-07 10:30
duffer_0113-Apr-07 10:30 
AnswerRe: Error Pin
Jim Weiler13-Aug-07 10:06
Jim Weiler13-Aug-07 10:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.