C# Chat Application Over Asynchronous UDP Sockets – Part 2, The Client

by Dave on August 5, 2009

in Articles, Internet

Chat ClientIn this article I will be showing you how to create a client for our chat application. This is part two of the series, so if you haven’t read part one yet please do so before continuing with this article as it explains the infrastructure of the whole application. You can read part one here: C# Chat Application Over Asynchronous UDP Sockets – Part 1, The Server.

The Client

For our client to connect to the server, which is listening for incoming connections, the server must obviously be running :) . Apart from that, the client must know the IP address of the server and which port the server is listening on. We already know from part one of this article series that the server is listening on port 30,000. However we don’t know the server’s IP address so that will have to be input manually from the user interface of the client.

The client will make use of the exact same Packet class we used for the server. In fact this class could have been placed into a library and included into each project as a dll instead of replicating the code twice… but it makes no difference for the purpose of this example, plus I didn’t feel like creating a dll :) .

Anyway, our client looks something like this:

Chat Client

As can be seen in the above screenshot, there is a section where the user must input a user name and a server IP address. Once this is done the user must click the connect button to let the server know that a client is connecting. The connect code is shown below:

private void btnConnect_Click(object sender, EventArgs e)
{
    try
    {
        this.name = txtName.Text.Trim();

        // Initialise a packet object to store the data to be sent
        Packet sendData = new Packet();
        sendData.ChatName = this.name;
        sendData.ChatMessage = null;
        sendData.ChatDataIdentifier = DataIdentifier.LogIn;

        // Initialise socket
        this.clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        // Initialise server IP
        IPAddress serverIP = IPAddress.Parse(txtServerIP.Text);

        // Initialise the IPEndPoint for the server and use port 30000
        IPEndPoint server = new IPEndPoint(serverIP, 30000);

        // Initialise the EndPoint for the server
        epServer = (EndPoint)server;

        // Get packet as byte array
        byte[] data = sendData.GetDataStream();

        // Send data to server
        clientSocket.BeginSendTo(data, 0, data.Length, SocketFlags.None, epServer, new AsyncCallback(this.SendData), null);

        // Initialise data stream
        this.dataStream = new byte[1024];

        // Begin listening for broadcasts
        clientSocket.BeginReceiveFrom(this.dataStream, 0, this.dataStream.Length, SocketFlags.None, ref epServer, new AsyncCallback(this.ReceiveData), null);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Connection Error: " + ex.Message, "UDP Client", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

In the above code, the first thing we are doing is creating a Packet object and setting the data identifier to LogIn. When this packet is sent it will let the server know that a client wants to join the chat room. Next we are initialising the server and socket details and then the Packet is converted into a byte array and sent to the server. Finally the client starts listening for any broadcasts from the server and when it receives something the received data is processed with the below code:

private void ReceiveData(IAsyncResult ar)
{
    try
    {
        // Receive all data
        this.clientSocket.EndReceive(ar);

        // Initialise a packet object to store the received data
        Packet receivedData = new Packet(this.dataStream);

        // Update display through a delegate
        if (receivedData.ChatMessage != null)
            this.Invoke(this.displayMessageDelegate, new object[] { receivedData.ChatMessage });

        // Reset data stream
        this.dataStream = new byte[1024];

        // Continue listening for broadcasts
        clientSocket.BeginReceiveFrom(this.dataStream, 0, this.dataStream.Length, SocketFlags.None, ref epServer, new AsyncCallback(this.ReceiveData), null);
    }
    catch (ObjectDisposedException)
    {}
    catch (Exception ex)
    {
        MessageBox.Show("Receive Data: " + ex.Message, "UDP Client", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

The above code is very simple. Since the client can only receive a Message from the server there is no need to process the data identifier which is at the beginning of the packet. Obviously, if you want your code to be robust and capable of handling any type of exception, you should check the data identifier… just in case.

All the code in this article can be made more robust. For example, there is no check to validate the IP address entered by the user in the client UI. There are no limits to the lengths of the input fields in the UI. There is no verification that the whole packet has in fact arrived in tact and uncorrupted. I think you get the picture… there is never a limit to the amount of checks you can perform and every good developer adds at least some of these checks to make the application more robust.

Conclusion

As you can see creating a client-server chat application using UDP is not that difficult. Obviously, there are many improvements you could make to this application. For example:

  • assign a different colour to different users so as to easily identify them apart,
  • allow sending of files (this would require a change to the custom data packet, and it would be better to use TCP instead of UDP for sending files,
  • display a list of all connected users on every client UI,
  • convert the server into a Windows Service to avoid starting it up manually,
  • add support for emotions,
  • implement a system to differentiate between users with the same name,
  • etc.

I really hope you enjoyed this article. Please leave your comments below and don’t forget to download the whole code listing from below – it requires Microsoft Visual Studio 2008.

Also, if you haven’t already subscribed to my rss feed, please do so – Grab RSS Feed. And if you like you can also follow me on Twitter.

Dave

Download sourceDownload Chat Client source – 15.0 KB
Download Chat Server source – 14.8 KB



{ 16 comments… read them below or add one }

Deepak September 8, 2010 at 16:49

Hey thanks a lot…
It was helpfull:-)

Reply

Dave September 8, 2010 at 18:32

Thanks for your comment. Glad you found it useful.

Reply

Omar EL Masry September 11, 2010 at 21:34

Dear Dave,
First of all, your program is perfect. The code is simple and easily understood.

I tried using my local IP; mean in my LAN, and I tried the program…It worked.
How about the world wide IP address? (I dont know if that is what they call it, but you know what I mean :) )… I tried using it .. that didnt work …

Is your program intended to work in this case? or there is another way of doing it??

Thanks a lot :)

Reply

Dave September 12, 2010 at 17:59

Hi Omar,

I’m glad you found the code easy to follow and understand.

The program should work over the Internet, but it must be behind a public fixed IP address. However, I have not tested this, but in theory it should work.

Dave

Reply

Omar EL Masry September 12, 2010 at 21:36

Thanks Dave …
After reading your reply, I think the problem is i am using a USB modem :D … It takes a variable IP (which worked in the application) , so maybe it takes the place of the public IP

Therefore I am wondering if there is a function we can use to find out the IP my computer is currently utilizing to connect to the world over the internet

Reply

Dave September 13, 2010 at 21:50

As far as I know there is no method to get the external IP address, especially if you are behind a router. You would have to rely on external services to give you your external IP. Many people choose to parse the html content returned from a site like http://www.whatismyip.com and get the IP from there, but personally I don’t really like that technique. Having said that I don’t know of a better way. Let us know if you find a way :-)

Reply

Omar EL Masry September 17, 2010 at 04:21

You are right about the router issue … but I have another question..
can we use this program to send an image? …
I can see that before you send a packet, you convert everything using ‘BitConverter’ … If something similar can be used to send images, it would be great :)

Reply

Dave September 17, 2010 at 19:37

You can send whatever you want over sockets – it’s binary data. To send an image, or any other file for that matter, you would do something like this:

To send the file:
// Create stream to load file
FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
// Go to beginning of file
fs.Position = 0;
// Setup temporary buffer
byte[] tmpBuf = new byte[1024 * 1024];
while (fs.Position < fs.Length)
{
// Loop until we reach the end of file
int len = fs.Read(tmpBuf, 0, tmpBuf.Length);
// Write file to stream
dataStream.Write(tmpBuf, 0, len);
}
// Clean up
fs.Close();

To receive the file:
fileStream = new FileStream(file, FileMode.Append);
readLenth = 0;
while ((readLenth < dataSize) && !exitLoop)
{
// Read data from socket
bytesReceived = socket.Receive(dataBuffer, 0, dataBuffer.Length, SocketFlags.None);
// Exit loop if nothing was received
if (bytesReceived == 0)
{
exitLoop = true;
break;
}
readLenth += bytesReceived;
// Write received data to filestream
fileStream.Write(dataBuffer, 0, bytesReceived);
}
// Close filestream
socketData.FileStream.Close();

Hope that helps :-)

Reply

Omar EL Masry September 19, 2010 at 06:30

Thanks again Dave … I will try this method later, but there is something more important I would like to ask about …

I have noticed that an error occurs on sending a large array of bytes … I mean a VERY large array … something that reaches multiple kilobytes … I dont what exactly limits the buffer size …

So, is there a method to send this large array by adjusting the original chat program code instead of the one you sent last time? (I was able to adjust the original code, but I am still limited by the allowed buffer size) …

Sorry for my continuous questions :)

Reply

Jayden September 27, 2010 at 22:15

Excellent work mate, exactly what I was looking for. Although, I know nothing about udp packets.

Would you mind giving me an insight on how to send a list of all the users currently connected to the clients? I tried but failed.

Reply

Dave September 28, 2010 at 22:02

Hi Jayden. Thanks for your comments.

Why did you fail – what have you tried so far?

Reply

Jayden September 28, 2010 at 22:04

Hi Dave, thanks for your reply mate.

I failed at trying to add a new section to the packet, which sends the names to the client :\

Reply

Khant Thu Linn December 7, 2010 at 04:54

Yo… Thanks you so much …because of you, I can refer your program and modify into what I need.
If possible, can I discuss with you for program? via mail?

Reply

David March 22, 2011 at 04:36

Hey, I’m trying to get this to work as a WPF app, got the chat server up and running as a WPF app, now when I try to convert the client to WPF and run it, it connects to the server and the server receives the connection from the client, but I get an error that Receive Data:Vallue cannot be null. Parameter name method.

I don’t understand the error, I mean it’s the same code as on your windows forms application, why is this exception in the ReceiveData method being thrown?

Any clue would be awsome…

Reply

Issa May 22, 2011 at 03:06

I am new in C# and I have some diffucults.
I want to send and receive image, but I don’t know how to use the code of image receive ,if you can Dave please write here the class ,where you use image receive method.

Reply

Javid December 6, 2011 at 23:43

Thanks Dave…

Reply

Leave a Comment

{ 1 trackback }

Previous post:

Next post: