RSS

Basic Client/Server Chat Application in C#

26 Jan

Welcome to the Basic Client/Server Chat Application in C#. In this tutorial I will provide the basics for a simple chat application in C# utilizing TCPClient, StreamReader, and the StreamWriter Classes in the .Net Framework.

In this application you have 3 components, the server (a class file), the communication component (a class file) and the client application. We will look at all 3 of these components individually, and how the can combine to create your basic chat application. The first component, the chat server, is where the messages are sent back and forth between the client and the server. Before writing any methods you need to add the following references to your class.

1 using System.IO;
2 using System.Net;
3 using System;
4 using System.Threading;
5 using Chat = System.Net;
6 using System.Collections;

I know some of you are going to look at the 5th reference and ask questions regarding Chat = System.Net. When adding references in C# you are allowed to add aliases to your references, thus allowing you to have multiple uses of the same Namespace at the same time, acting as 2 different objects.

NOTE: To use Aliases for the Namespace reference it has to be in conjunction with the Using Statement.

The first thing we do in our Server class is create 3 global variables, 2 are Hashtable variables, and the third is a TCPListener variable, which is used to listen for connections from TCP Clients.

1 System.Net.Sockets.TcpListener chatServer;
2 public static Hashtable nickName;
3 public static Hashtable nickNameByConnect;

These three variables will be used throughout our ChatServer.cs class file. Next, is the Public ChatServer() method, this is where we start the chat server and connect. We will then use our TCPListener object to check if there are any pending connection requests. If there are pending requests we then create a new connection, let the user know they’re connected, then create our DoCommunication Object.

We’ll get to the DoCommunication object later in this tutorial. Here is the code for this method

01 public ChatServer()
02 {
03 //create our nickname and nickname by connection variables
04 nickName = new Hashtable(100);
05 nickNameByConnect = new Hashtable(100);
06 //create our TCPListener object
07 chatServer = new System.Net.Sockets.TcpListener(4296);
08 //check to see if the server is running
09 //while (true) do the commands
10 while (true)
11 {
12 //start the chat server
13 chatServer.Start();
14 //check if there are any pending connection requests
15 if (chatServer.Pending())
16 {
17 //if there are pending requests create a new connection
18 Chat.Sockets.TcpClient chatConnection = chatServer.AcceptTcpClient();
19 //display a message letting the user know they're connected
20 Console.WriteLine("You are now connected");
21 //create a new DoCommunicate Object
22 DoCommunicate comm = new DoCommunicate(chatConnection);
23 }
24 }
25 }

Next, since this is a basic chat application, we need a method for sending our messages to all that are connected. Here we create a StreamWriter object, used to write our messages to the chat window, a TcpClient Array, to hold all the TcpClients for all connected users, then we copy the users nickname to the chat server window. After that we create a loop, looping through all the TcpClients, we check if the message eing sent is empty or that index of our TcpClient array is empty. From there we send our message to the chat window, and flush to make sure the buffer is empty.

In your Catch, of our Try…Catch block, is where we handle the Exception that is caused when a user leaves or disconnects. We display a message letting the users know that that person has disconnected, we remove that nickname from the list, then dispose of that users TcpClient instance. Here is the code for this method

01 public static void SendMsgToAll(string nick, string msg)
02 {
03 //create a StreamWriter Object
04 StreamWriter writer;
05 ArrayList ToRemove = new ArrayList(0);
06 //create a new TCPClient Array
07 Chat.Sockets.TcpClient[] tcpClient = new Chat.Sockets.TcpClient[ChatServer.nickName.Count];
08 //copy the users nickname to the CHatServer values
09 ChatServer.nickName.Values.CopyTo(tcpClient, 0);
10 //loop through and write any messages to the window
11 for (int cnt = 0; cnt < tcpClient.Length; cnt++)
12 {
13 try
14 {
15 //check if the message is empty, of the particular
16 //index of out array is null, if it is then continue
17 if (msg.Trim() == "" || tcpClient[cnt] == null)
18 continue;
19 //Use the GetStream method to get the current memory
20 //stream for this index of our TCPClient array
21 writer = new StreamWriter(tcpClient[cnt].GetStream());
22 //white our message to the window
23 writer.WriteLine(nick + ": " + msg);
24 //make sure all bytes are written
25 writer.Flush();
26 //dispose of the writer object until needed again
27 writer = null;
28 }
29 //here we catch an exception that happens
30 //when the user leaves the chatroow
31 catch (Exception e44)
32 {
33 e44 = e44;
34 string str = (string)ChatServer.nickNameByConnect[tcpClient[cnt]];
35 //send the message that the user has left
36 ChatServer.SendSysMsg("** " + str + " ** Has Left The Room.");
37 //remove the nickname from the list
38 ChatServer.nickName.Remove(str);
39 //remove that index of the array, thus freeing it up
40 //for another user
41 ChatServer.nickNameByConnect.Remove(tcpClient[cnt]);
42 }
43 }
44 }

The next method we introduce is a way to send a system message, this method is almost identical to the SendMsgToAll method, except here we dont dispose of the TcpClient instance, since the message is being sent by the system, not a user.

01 public static void SendSystemMessage(string msg)
02 {
03 //create our StreamWriter object
04 StreamWriter writer;
05 ArrayList ToRemove = new ArrayList(0);
06 //create our TcpClient array
07 Chat.Sockets.TcpClient[] tcpClient = new Chat.Sockets.TcpClient[ChatServer.nickName.Count];
08 //copy the nickname value to the chat servers list
09 ChatServer.nickName.Values.CopyTo(tcpClient, 0);
10 //loop through and write any messages to the window
11 for (int i = 0; i < tcpClient.Length; i++)
12 {
13 try
14 {
15 //check if the message is empty, of the particular
16 //index of out array is null, if it is then continue
17 if (msg.Trim() == "" || tcpClient[i] == null)
18 continue;
19 //Use the GetStream method to get the current memory
20 //stream for this index of our TCPClient array
21 writer = new StreamWriter(tcpClient[i].GetStream());
22 //send our message
23 writer.WriteLine(msg);
24 //make sure the buffer is empty
25 writer.Flush();
26 //dispose of our writer
27 writer = null;
28 }
29 catch (Exception e44)
30 {
31 e44 = e44;
32 ChatServer.nickName.Remove(ChatServer.nickNameByConnect[tcpClient[i]]);
33 ChatServer.nickNameByConnect.Remove(tcpClient[i]);
34 }
35 }
36 }

Believe it or not, thats the entirety of the ChatServer Class, simple isnt it. Working with Tcp objects can be fun, as you can do so much with them. In this simple application you could add the functionality to send files back and forth between users, and more. That may be the end of the ChatServer Class, but its not the end of creating our application.

The next component to look at is the DoCommunicate Class. This is the component that does the work for our server. For a chat application to work efficiently, and work as people expect a chat application to work, it needs to be a multi-threaded application. Meaning each user is running in their own thread, which allows for the messages to be sent and received in real time. Multi threading gives the illusion that multiple activities are happening at the same time.

The main purpose of multi threading is to improve performance. With each user in the chat application operating on their own thread, users don’t have to wait for one user to be finished to send their message, they’re able to send them simultaneously. C# has some powerful items in the System.Threading Namespace, which is used for, you guessed it, running multiple threads and synchronizing them.

For our DoCommunicate.cs class file we need the following references

1 using System.IO;
2 using System.Net;
3 using System;
4 using System.Threading;
5 using Chat = System.Net;
6 using System.Collections;
7 using PC;

Once again we add an alias to an instance of the System.Net Namespace reference, this prevents namespace collisions in our class. Like the ChatServer class, the first thing we do in our class is create some global variables, 4 of them:

In this method is where the new Thread is created and started, allowing this user to react in real time in the application.

1 public DoCommunicate(System.Net.Sockets.TcpClient tcpClient)
2 {
3 //create our TcpClient
4 client = tcpClient;
5 //create a new thread
6 Thread chatThread = new Thread(new ThreadStart(startChat));
7 //start the new thread
8 chatThread.Start();
9 }

Notice when we create our new Thread we pass it a method called startChat. We’ll get to this method momentarily, but first we need to do a couple things that startChat relies on. Once the thread is created and
started, we need to get the nickname the user wishes to use. For this we use the GetNick method we created. Here we simply ask the user what their nickname is, then return that value to the startChat method.

1 private string GetNick()
2 {
3 //ask the user what nickname they want to use
4 writer.WriteLine("What is your nickname? ");
5 //ensure the buffer is empty
6 writer.Flush();
7 //return the value the user provided
8 return reader.ReadLine();
9 }

Now lets look at the aforementioned startChat method. Here we create our StreamReader and StreamWriter objects and set the global string variable nickName to the value returned from the GetNick method. Next thing we do is check to ensure that the nickname provided by the user doesn’t already exist, if it does we prompt them for a nickname until we find one thats not already in use.

Once they provide a valid nickname we add their nickname to the server, preventing another user from using it, then we send a system message letting the other users know there is a new user. From there we create a new Thread, which calls the runChat method. Lets first look at the startChat method

01 private void startChat()
02 {
03 //create our StreamReader object to read the current stream
04 reader = new System.IO.StreamReader(client.GetStream());
05 //create our StreamWriter objec to write to the current stream
06 writer = new System.IO.StreamWriter(client.GetStream());
07 writer.WriteLine("Welcome to PCChat!");
08 //retrieve the users nickname they provided
09 nickName = GetNick();
10 //check is the nickname is already in session
11 //prompt the user until they provide a nickname not in use
12 while (PC.ChatServer.nickName.Contains(nickName))
13 {
14 //since the nickname is in use we display that message,
15 //then prompt them again for a nickname
16 writer.WriteLine("ERROR - Nickname already exists! Please try a new one");
17 nickName = GetNick();
18 }
19 //add their nickname to the chat server
20 PC.ChatServer.nickName.Add(nickName, client);
21 PC.ChatServer.nickNameByConnect.Add(client, nickName);
22 //send a system message letting the other user
23 //know that a new user has joined the chat
24 PC.ChatServer.SendSystemMessage("** " + nickName + " ** Has joined the room");
25 writer.WriteLine("Now Talking.....\r\n-------------------------------");
26 //ensure the buffer is empty
27 writer.Flush();
28 //create a new thread for this user
29 Thread chatThread = new Thread(new ThreadStart(runChat));
30 //start the thread
31 chatThread.Start();
32 }

The last method in our DoCommunicate.cs Class is the runChat method called by the new thread in startChat. This is simply for reading the current stream and sending our messages to the chat window.

01 private void runChat()
02 //use a try...catch to catch any exceptions
03 {
04 try
05 {
06 //set out line variable to an empty string
07 string line = "";
08 while (true)
09 {
10 //read the curent line
11 line = reader.ReadLine();
12 //send our message
13 PC.ChatServer.SendMsgToAll(nickName, line);
14 }
15 }
16 catch (Exception e44)
17 {
18 Console.WriteLine(e44);
19 }
20 }

That is the end of our DoCommunicate class. So far you have seen how to create a chat server, a class to handle the work of the chat application. You have learned about TcpClients, TcpListeners, StreamReaders, StreamWriters, and Threads. We discussed the purpose of a multi threaded application, and how to create one, and you have learned about adding an alias to your reference to prevent namespace collision in your application.

Now that we have our chat server completely defined, we need a client application to chat with. In this application I have a single form, ChatClient, but I did this a little differently. I didn’t add any controls via drag and drop, I added them at runtime, personally I wouldn’t recommend this for new programmers.

First thing i our client application is a Windows API call, the reference we need is the ExitProcess function. That looks like this

1 [DllImport("kernel32.dll")]
2 private static extern void ExitProcess(int a);

In void Main is where I create a new form, add my controls, set the properties of the window, WindowState, Text, and my TcpClient and call the Connect method of the System.Net.Sockets.TcpClient Class.

With the Connect method you provide the IP address, or host name, along with the port number to connect to, then it connects you to that information. Since this is a basic application, that information is hard coded into the application, with a real application you would have an area to give the user the option to specify which chat server they wish to connect to. Aside from the Main method we have three more methods:

  • ChatClient_Closing: This handles what needs to be done once the user closes the application. This all happens as the form is closing.
  • key_up: This is what sends our message to the chat window. Since I do it on the key up event, they will see what you’re typing as you trype. For an actual application this functionality would be added to a Send button, or when the user hits Enter.
  • Run: This is the running of the chat application, reading the current stream and appending it to the current contents of the chat window, and placing the cursor at the end of the text already in the textbox you’re typing your message into

How I’m appending the text to the current contents of the chat window is by using the AppendText Method of the TextBox Class.

First lets look at the code for the Closing Event of the form.

1 private static void ChatClient_Closing(object s, CancelEventArgs e)
2 {
3 e.Cancel = false;
4 //exit the application
5 Application.Exit();
6 //call the ExitProcess API
7 ExitProcess(0);
8 }

When the form closes, it calls the Application.Exit Method, then the call to the ExitProcess Function.

Next we have the code for the Control.KeyUp Event, which is what sends our messages to the chat window. In this method, we create a StreamWriter for writing to the current stream. To do this we call the GetStream Method of the System.Net.Sockets.TcpClient class. GetStream retrieves the current NetworkStream, used for sending and receiving messages across a network.

01 private static void key_up(object s, KeyEventArgs e)
02 {
03 //create our textbox value variable
04 TextBox txtChat = (TextBox)s;
05 //check to make sure the length of the text
06 //in the TextBox is greater than 1 (meaning it has text in it)
07 if (txtChat.Lines.Length > 1)
08 {
09 //create a StreamWriter based on the current NetworkStream
10 StreamWriter writer = new StreamWriter(tcpClient.GetStream());
11 //write our message
12 writer.WriteLine(txtChat.Text);
13 //ensure the buffer is empty
14 writer.Flush();
15 //clear the textbox for our next message
16 txtChat.Text = "";
17 txtChat.Lines = null;
18 }
19 }

Next we have the code for our run method. This creates a StreamReader Object, using GetStream to retrieve the current NetworkStream, this will be used for reading the messages in the stream. We then append the value in the current stream, line by line, to the chat window.

01 private static void run()
02 {
03 //create our StreamReader Object, based on the current NetworkStream
04 StreamReader reader = new StreamReader(tcpClient.GetStream());
05 while (true)
06 {
07 //call DoEvents so other processes can process
08 //simultaneously
09 Application.DoEvents();
10 //create a TextBox reference
11 TextBox txtChat = (TextBox)client.Controls[0];
12 //append the current value in the
13 //current NetworkStream to the chat window
14 txtChat.AppendText(reader.ReadLine() + "\r\n");
15 //place the cursor at the end of the
16 //text in the textbox for typing our messages
17 txtChat.Selectionstart = txtChat.Text.Length;
18 }
19 }

That is the end of the tutorial Basic Client/Server Chat Application in C#. I am enclosing all three files with this tutorial. They are under the Public GNU License which means you can modify the code to suit your needs, but you need to provide a reference to the original creator of the code. Also, you are not allowed to remove the license header at the beginning of all the files in this solution.

I hope you enjoyed this tutorial, and found it useful. I will next write a tutorial for an advanced client/server chat application, to show what can be done with the techniques we learned in this tutorial.

Thank you so much for reading :)

NOTE: You’re going to want to take the ChatServer Class and possibly make an application out of that as well. I have it as a class file as I’m using a different implementation of the server.

Attached File Basic_ClientServer_Chat.zip (132.21K)

 

Referenced by: http://www.dreamincode.net/forums/topic/33396-basic-clientserver-chat-application-in-c%23/

http://www.codeproject.com/KB/IP/dotnettcp.aspx

http://www.codeproject.com/KB/IP/chatserver.aspx

About these ads
 
6 Comments

Posted by on January 26, 2011 in C#, Window Application

 

6 responses to “Basic Client/Server Chat Application in C#

  1. Sarath

    September 23, 2011 at 9:43 PM

    Hi, my name is Sarath. My team has registered into a software design contest at RUPP. We want to create a Chatting and Video Conferencing Application, but we don’t have any idea of where to start, and to do. Can u please drop me your idea, your help could support me a lot. We have only 3 months more, so hope u can respond my message soon.

    Yours,
    Sarath

     
    • sochinda

      September 28, 2011 at 6:37 AM

      Oh! I have no experience in Video Conferencing Application, but can please check this link http://xmpp.org/, i hope it can help u because XMPP is protocol that Google use to to develop Google Talk

       
  2. saffron extract weight loss does it work

    May 22, 2013 at 12:19 AM

    Acquiring more than enough natural vitamins and minerals in your physique can also make your hair shinier and a lot more workable.
    Since of this it is recommended for expecting girls, as it will help to reduced the chance of spina bifida.

     
  3. dragons of atlantis ruby hack

    January 25, 2014 at 1:15 PM

    Hi! Would you mind if I share your blog with my zynga group?
    There’s a lot of folks that I think would really appreciate your content.
    Please let me know. Thanks

     
  4. http://www.sagami-kadan.jp/userinfo.php?uid=106015

    February 15, 2014 at 8:40 AM

    I enjoy what you guys are usually up too. Such clever work and reporting!
    Keep up the terrific works guys I’ve added you guys to my
    personal blogroll.

     
  5. youtube.com

    July 21, 2014 at 4:04 AM

    If you are planning to engage in a personal injury settlement, seek the services of our experienced Los
    Angeles attorneys. It is recommended to hire a professional because the claim laws can vary greatly from state to state, and if you
    are not familiar with these nuances, you may not get your fully entitled payout.
    Far worse is the pain in personal injury cases when individual’s rights are not effectively represented and innocent parties wind up getting shafted.

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: