Geekpedia Programming Tutorials









C# Chat: Part 1 - Building the Chat Client

In this two part tutorial you will learn how to create a chat client that connects to a chat server and exchanges messages with all the other connected clients. The first part covers the development of the client application.

On Saturday, October 20th 2007 at 12:43 PM
By Andrei Pociu (View Profile)
*****   (Rated 5 with 14 votes)
Contextual Ads
More C# Resources
Advertisement
» C# Chat: Part 1 - Building the Chat Client (currently reading)
» C# Chat: Part 2 - Building the Chat Server

Download this Visual Studio 2005 project Download the Chat Client Application project (Visual Studio 2005)


Building a Chat Server and a Chat Client

To no avail I've been searching the web for a good C# chat client/server application that is not thousands of lines of code long. The only simple ones I could find were flawed or very buggy, but then again I tried to combine the good parts of all the tutorials I found into one application, and one tutorial that you can see here today. We are going to build a fairly larger application (a few hundred lines of codes) than the ones we're used to here on Geekpedia, but we're still going to keep it simple. In fact, there will be two applications: one is a chat server, that handles incoming connections, stores them in hash tables, and distributes new messages to everyone connected, and the other is of course, the chat client, which connects to the server, sends messages and listens for incoming messages. A little knowledge of C#'s networking objects, multi-threading, events and delegates will definitely help you understand this tutorial more easily. A good way to start learning about delegates, events and creating client-server applications is to read the Delegates and Events in C# tutorial first.
The chat server will be able to accept as many chat clients as allowed by the hash table (and you are able to define the limit of the hash table yourself), and it will also track all the messages that are going back and forth in its own chat window, so you should be able to scale this code to a full blown chat application as long as you add the necessary error handling and the bells and whistles.


The client application

The client application is, as you might have expected, the simpler one, since all it has to do is to attempt to connect to the chat server, request an username, start listening for messages and sending its own, and finally disconnecting.
Start Visual Studio 2005 and create a new C# Windows Application. I've given mine the obvious "Chat Client" name.

Chat Client Form

The first two TextBoxes (txtIp and txtUser) will hold the IP address of the server we want to connect to and the desired username. Before testing out this code keep in mind to change this IP address to the one of the computer in your network that runs the client. If you read the Delegates and Events in C# tutorial, you probably assume you can run the chat application and the server application on the same machine, without needing two different computers connected through a network or the Internet. And you would be right.
The Connect (btnConnect) and Send (btnSend) buttons are obvious, they're for connecting to the server and sending messages. The large multi-line TextBox is named txtLog and it is where all the messages will be shown.The small TextBox at the bottom is called txtMessage and it is where the message to be sent to the server will be typed.

Now that we're done with the actual design of the form, we can finally code. Since we will be making use of networking, streaming and threading objects, start by adding the following using statements:

using System.Net;

using System.Net.Sockets;

using System.IO;

using System.Threading;


We're going to declare most of our objects inside the class, as private, since we don't need them accessible from anywhere else outside the class:

// Will hold the user name

private string UserName = "Unknown";

private StreamWriter swSender;

private StreamReader srReceiver;

private TcpClient tcpServer;

// Needed to update the form with messages from another thread

private delegate void UpdateLogCallback(string strMessage);

// Needed to set the form to a "disconnected" state from another thread

private delegate void CloseConnectionCallback(string strReason);

private Thread thrMessaging;

private IPAddress ipAddr;

private bool Connected;


And now that we have them declared, let's put them to use. Let's start with the btnConnect_Click event which can be automatically generated if you double click on the Connect button. Inside it we're going to check whether or not we are connected to a server. If we are, we should call the method that initializes the connection, otherwise we call the method that closes the connection, also specifying a reason why to it:

private void btnConnect_Click(object sender, EventArgs e)

{

    // If we are not currently connected but awaiting to connect

    if (Connected == false)

    {

        // Initialize the connection

        InitializeConnection();

    }

    else // We are connected, thus disconnect

    {

        CloseConnection("Disconnected at user's request.");

    }

}


All simple so far, so let's move to InitializeConnection():

private void InitializeConnection()

{

    // Parse the IP address from the TextBox into an IPAddress object

    ipAddr = IPAddress.Parse(txtIp.Text);

    // Start a new TCP connections to the chat server

    tcpServer = new TcpClient();

    tcpServer.Connect(ipAddr, 1986);

 

    // Helps us track whether we're connected or not

    Connected = true;

    // Prepare the form

    UserName = txtUser.Text;

 

    // Disable and enable the appropriate fields

    txtIp.Enabled = false;

    txtUser.Enabled = false;

    txtMessage.Enabled = true;

    btnSend.Enabled = true;

    btnConnect.Text = "Disconnect";

 

    // Send the desired username to the server

    swSender = new StreamWriter(tcpServer.GetStream());

    swSender.WriteLine(txtUser.Text);

    swSender.Flush();

 

    // Start the thread for receiving messages and further communication

    thrMessaging = new Thread(new ThreadStart(ReceiveMessages));

    thrMessaging.Start();

}


Nothing too complicated happens in there. The IP address is parsed from the TextBox into an IPAddress object, and then we open a TCP connection to that address. The port is 1986 but it makes no difference as long as its free. We then prepare the controls on the form by disabling some and enabling the others. We also change the caption of btnConnect to now say Disconnect. Through a stream, we then tell the server which username we want, and immediately after that we start a new thread that calls the method ReceiveMessages() which will listen for incoming messages from now on. By putting this in a separate thread, our application is still fully usable while it is listening for messages from the server and keeping the connection alive.

It's time to see what the ReceiveMessages() method is all about:

private void ReceiveMessages()

{

    // Receive the response from the server

    srReceiver = new StreamReader(tcpServer.GetStream());

    // If the first character of the response is 1, connection was successful

    string ConResponse = srReceiver.ReadLine();

    // If the first character is a 1, connection was successful

    if (ConResponse[0] == '1')

    {

        // Update the form to tell it we are now connected

        this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { "Connected Successfully!" });

    }

    else // If the first character is not a 1 (probably a 0), the connection was unsuccessful

    {

        string Reason = "Not Connected: ";

        // Extract the reason out of the response message. The reason starts at the 3rd character

        Reason += ConResponse.Substring(2, ConResponse.Length - 2);

        // Update the form with the reason why we couldn't connect

        this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] { Reason });

        // Exit the method

        return;

    }

    // While we are successfully connected, read incoming lines from the server

    while (Connected)

    {

        // Show the messages in the log TextBox

        this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() });

    }

}


A new stream reader is hooked up to the TCP client. It will listen for incoming messages. But first of all, we read the first line coming from the server. The reason for that is that we know the first line contains a response telling us whether or not we connected successfully. Two reasons why we might've not connected successfully are if we attempted to use an username that is already taken, or if we attempted to use Administrator as the username, which is reserved for obvious purposes. The first character of the response given by the server tells us through a 1 that the connection was successful, and through a 0 if it was unsuccessful. And in that case, it also reads a reason as to why it was unsuccessful. That reason starts at the 3rd character of the message, since the first one is the number, and the second one is a pipe character. For example: 0|Username is already in use. Now you can see why if the first character is not a 1 we read the string that starts at the 3rd character and ends at the end of the line.

The this.Invoke() calls tell the form to update itself. We can't directly update the form elements ourselves from this method because it's in a separate thread (remember we called it using ThreadStart()) and cross-thread operations are illegal.

Finally, the while (Connected) loop keeps calling the srReceiver.ReadLine() method which checks for incoming messages from the server.

Next comes the method that we kept calling using this.Invoke() - all it does is to update the txtLog TextBox with the latest message:

// This method is called from a different thread in order to update the log TextBox

private void UpdateLog(string strMessage)

{

    // Append text also scrolls the TextBox to the bottom each time

    txtLog.AppendText(strMessage + "\r\n");

}

So far we've seen how to receive messages from the server, but nothing about how to send them. When do we want to send a message? When the Send button is clicked or when the Enter key is pressed while txtMessage has the focus. This should be hooked up to the Click event of the btnSend button:

// We want to send the message when the Send button is clicked

private void btnSend_Click(object sender, EventArgs e)

{

    SendMessage();

}


And this needs to be hooked up to the KeyPress event of txtMessage:

// But we also want to send the message once Enter is pressed

private void txtMessage_KeyPress(object sender, KeyPressEventArgs e)

{
    // If the key is Enter

    if (e.KeyChar == (char)13)

    {

        SendMessage();

    }

}


You can see that both of them make a call to SendMessage, which we are going to see next:

// Sends the message typed in to the server

private void SendMessage()

{

    if (txtMessage.Lines.Length >= 1)

    {

        swSender.WriteLine(txtMessage.Text);

        swSender.Flush();

        txtMessage.Lines = null;

    }

    txtMessage.Text = "";

}


Quite simple, isn't it? It just checks for the number of lines to be greater or equal to 1, and then writes that line to the TCP connection through the StreamWriter object. Calling Flush() ensures that the messages are being sent right away.

We seem to be almost done with the client application. But let's not forget that when btnConnect was clicked, if we were already connected, we called a method called CloseConnection() - what happened to that? Well here it is:

// Closes a current connection

private void CloseConnection(string Reason)

{

    // Show the reason why the connection is ending

    txtLog.AppendText(Reason + "\r\n");

    // Enable and disable the appropriate controls on the form

    txtIp.Enabled = true;

    txtUser.Enabled = true;

    txtMessage.Enabled = false;

    btnSend.Enabled = false;

    btnConnect.Text = "Connect";

 

    // Close the objects

    Connected = false;

    swSender.Close();

    srReceiver.Close();

    tcpServer.Close();

}


The form is being brought back to the not-connected state, and the TCP connection and streams are being closed. But what happens if the user doesn't click Disconnect and just closes the application while the connection with the server is alive? We surely don't want to leave the connection open like this till it dies by its own. Thankfully there is the ApplicationExit event that fires when the application closes, and that's where we can close our connection. To hook up the event change your Form1 constructor to the following:

public Form1()

{

    // On application exit, don't forget to disconnect first

    Application.ApplicationExit += new EventHandler(OnApplicationExit);

    InitializeComponent();

}


And here is the event handler that does the actual disconnection:

// The event handler for application exit

public void OnApplicationExit(object sender, EventArgs e)

{

    if (Connected == true)

    {

        // Closes the connections, streams, etc.

        Connected = false;

        swSender.Close();

        srReceiver.Close();

        tcpServer.Close();

    }

}


Believe or not, we're done with the chat client application. You should be able to compile and run it now, but of course there's nothing to connect to because we haven't developed the server. But that comes next in C# Chat: Part 2- Building the Chat Server. Here's a little teaser of our applications in action, with the server in the shadow of the two:

Chat Clients
Digg Digg It!     Del.icio.us Del.icio.us     Reddit Reddit     StumbleUpon StumbleIt     Newsvine Newsvine     Furl Furl     BlinkList BlinkList

Rate Rate this tutorial
Comment Current Comments
by Kahey on Wednesday, October 31st 2007 at 02:34 PM

Very good! Thank you for making it easy enough for an amateur coder to understand.
I\'ve been looking for a simple chat to incorporate into an online game (php/mysql, html/javascript) I\'ve spent months writing. I know there\'s no end of free ones to download however it\'s more satisfying to create ones own. Due to budget restrictions I\'ve coded everything thus far in good old notepad.
To this end I wonder is it possible to create a chat using javascript or php?

[Note: The cost of visual studio prohibits me from using C# at this point in my budget.]

by Andrei Pociu on Wednesday, October 31st 2007 at 05:49 PM

It is possible, the best approach would be to use PHP and JavaScript together to create a chat using AJAX.

You don't need Visual Studio to compile C# applications. You can just use the .NET Framework's command prompt, or better yet, Visual Studio Express. You can download it for free at http://msdn2.microsoft.com/en-us/express/default.aspx

by Kahey on Friday, November 2nd 2007 at 02:18 AM

Thanks Andrei.
I'm trying to keep everything browser-based and non-intrusive (ie: no need for customers to download anything at all) which is another reason for this project being php/mysql, html/javascript.

I've had a look at VSE now. I think I'll step up my learning curve into C# etc next project in 2008.

For now do you know where I could find a tutorial as good as yours for using PHP and JavaScript together to create a chat using AJAX?

Any help is always greatly appreciated,
Kahey

by D on Friday, November 30th 2007 at 06:54 PM

I have a question. I get an error when coming to the

if (ConResponse[0] == \'1\').

It says that Object reference not set to an instance of an object. How would I fix this?

by Martin on Monday, December 10th 2007 at 09:29 PM

I've been fiddling with this tutorial for weeks and even my courageous & endless searching of Google has failed to turn any results. Sadly to say, this tutorial is either complete crap, or is severely out-of-date. Every little bit of it (even if you were to copy & paste it from the pages, which I tried after having manually typed out all of the code) gets nit-picked by VC# Express, and there's nothing but errors left & right.

The biggest problem is the ConResponse[0] null object reference exception... but to me, logically speaking, the code looks complete... & I fail to see how the string ConResponse has not been instantiated either... and this appeared to be the best & most intuitive C# tutorial on the net... in fact it was, all other C# sites are pure rubbish... I guess C# is no territory for a Python & PHP programmer... I hope fewer people will throw caution to the wind when writing tutorials from now on...

by Andrei Pociu on Monday, December 10th 2007 at 10:14 PM

Martin, I believe you are moving into a topic to advanced for your knowledge of C#. While this tutorial is nowhere near the advanced level, you need to have at least a basic understanding of the language.

For example you pointed out that ConResponse is not getting instantiated. If you look above in the tutorial you will see:

string ConResponse = srReceiver.ReadLine();

If ConResponse[0] is null, your client is not receiving a message from the server. There\'s a multitude of reasons why this might be, some unrelated to the code. If you place breakpoints in the code and monitor the value of the objects you should easily be able to pinpoint the exact problem.

I haven\'t used VC# Express but it shouldn\'t make any difference.

by Martin on Wednesday, December 12th 2007 at 07:56 PM

I think I get it now. I wasn't assuming that VC#'s debugging was telling me that the code itself was semantically wrong, but after more testing this morning I realized what you've explained to me here. So, I'll simply need to do some troubleshooting with my network. Sorry for my harsh generalization. I'm just having a frustrated time moving into the world of languages like C#. I think it may be a wise bet for me to drop back to some even simpler tutorials, although to be honest... I think most of the basic tutorials I've found have been dull, unintuitive, and haven't really taught me much. Oh well, thanks again and sorry for leaking my frustration here.

by Rami on Monday, January 14th 2008 at 11:18 AM

Really it's a good work and explaniation....but pls can you describe how to make chat rooms...at least the idea not the code!!!!!

by Ben on Tuesday, January 15th 2008 at 08:07 AM

Thanks alot for all the tutorials, its help me learn alot about C# and understand the difference from php which is where i am from :)

But for some reason the chat client doesn\'t but i have a fealing its not the code itself, but something wrong with my machine. When ever i try and use the TcpClient class it seems to kill my application.

Any ideas on what could be causing this? or is it back to google for me? I am using the asp.net 3.0 framework.

by Steve on Saturday, January 19th 2008 at 11:32 PM

Why is it when I disconnect with the client it always errors out?
The debugger points me to this part of the code:
while (Connected)
{
// Show the messages in the log TextBox
this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() });
}

It's as if the connection is shutting down and this part of the code is still trying to read incoming lines from the server.

by john on Friday, February 1st 2008 at 04:53 AM

hi I had some error wen i tried to run both of them in my network can some one recomend a good c# book for this kind of advanced projects? thanks ;)

by Owen on Monday, February 11th 2008 at 01:48 PM

hi a book named 'beggining C#' might help you with it.

my question is do you have to make the server or does it tell you in this artical ? because i'am confused :P

by Bob on Saturday, February 23rd 2008 at 10:12 AM

This is wonderful! Only when i disconnect i get some error:


{
// Show the messages in the log TextBox
this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() });

}

This should be fixed but i dont know :(...and when you disconnect it should work but it didnt.

Also i am dieing to know how you make a userlist so you can see who is online and a private message system. Can you please make a tutor or add to this tutor how to make userlist and when u single click on it and than click on a button private message that u can send private messages to the user u want. Please can you make that!

thanks man! Andrei Pociu

by kenix on Tuesday, February 26th 2008 at 08:19 PM

the server application could be transformed into a service so that it runs in the background

Good explanation. I had try it... it worked perfectly. By the way, the client application can be transform into window service so that it runs in the background? I wish to run the

i had tried it but failed. once i run the services for the client( server is on ), it return error at this part
UpdateLog("Connected Successfully!);

==> by original code is (Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { "Connected Successfully!" });) which is only use for the System.windows.form

so, when i build the service, it cant use it. so, i make it and direct call the UpdateLog() method.

As i search from net the Invoke() Method is executes a delegate/represent on the thread that owns the controls underlying window handle. SO, it is related to the thread that i can not direct call the UpdateLog() method and need some code to refresh/ handle the thread ?


any solution or comment on it ?

thanks in advance
- kenix

by wisam91 on Tuesday, March 25th 2008 at 07:45 PM

thanks so much..really helpful!
but any one can give me the code to make a list that shows all user in the room?
& why there is some problems when trying the program in the local network..when it's connected it can only send 1 message then it crashes...
help me with that please

contact me at xiiv@live.com
thx and sorry for my english
peace

by Thiago Guerreiro on Tuesday, April 22nd 2008 at 09:27 PM

Wow...
Excellent Tutorial.

The best i find about Chat networking.

Thankz a lot and sorry 4 bad english =P


Comment Comment on this tutorial
Name: Email:
Message:
Comment Related Tutorials

C# Chat: Part 2 - Building the Chat Server

On Saturday, October 20th 2007 at 04:41 PM by Andrei Pociu in C#


Comment Related Source Code
There is no related source code.

Jobs C# Job Search
My skills include:
Enter a City:

Select a State:


Advanced Search >>
Latest Tech Bargains

Advertisement

Free Magazine Subscriptions

Today's Pictures

Today's Video

Other Resources

Latest Downloads

Latest Icons