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

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

DaveOnCSharp.com runs on the Thesis Theme


How smart is your Theme? How good is your support? Check out ThesisTheme for WordPress.

The Thesis Theme is one of the best Wordpress frameworks out there, especially if you understand software development, which since you're here you probably do. Thesis allows you to design post and page templates using a drag-and-drop system which gives you access to all the relevant page elements you would normally have to manually access through code. This is why understanding development techniques definitely helps, even though you don't need to write any code to get Thesis up and running.

So go check out the incredible features at DIYThemes and start using Thesis now!

29 comments… add one
  • Deepak

    Hey thanks a lot…
    It was helpfull:-)

    • Thanks for your comment. Glad you found it useful.

  • Omar EL Masry

    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 🙂

    • 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

      • Omar EL Masry

        Thanks Dave …
        After reading your reply, I think the problem is i am using a USB modem 😀 … 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

        • 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 🙂

          • Omar EL Masry

            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 🙂

            • 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 🙂

  • Omar EL Masry

    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 🙂

  • Jayden

    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.

    • Hi Jayden. Thanks for your comments.

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

      • Jayden

        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 :\

  • Khant Thu Linn

    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?

  • David

    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…

  • Issa

    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.

  • Tohir

    Hi…Dave
    I need your help,
    Can you guide me to build Webcam Streaming Client-Server over internet in C# windows form?

    I hope you can
    Thanks in advance

  • Rehab

    thank u soooooooooooooooooooooooooooo much
    i try it now ^_*

  • Giovanni de Bona

    Hi, the article helped me a lot!
    But I need to send a message to all connected clients without function ‘Receive’, just a ‘Sent’ function.
    How can I send a message to all clients via a button?

  • Kiran Ravi

    Nice app

  • // 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();

  • WSN

    Hello Dave, this example and explanation is very good!

    I think that those who publish on the website of Microsoft, should learn from this website.

    =D

  • WEN

    hi, how should I send message to one client not all client?

    thanks

  • Hi Dave, this is an excellent program, but I am having a small problem whose solution must be very easy…. I added a textbox and a send button on the Server Form, and I want to broadcast that textbox contant to all clients. please can you help. very urgent.
    Thank you very much for this wonderful code.

  • Bagus Yoni

    Thx dave it was helpfull..
    So, can we encrypted while sending chat?

  • hello sir in this project there is no any error but it does not show conversation and i creat client form and server form different i tried to disable windows firewall but no output plz help me fast

  • jala

    hy
    i wanna do private chat with specific user
    can you plz help me ??

  • maria

    your code really works, thanks but i have query
    if server and client application is running and due to some reason clients pc internet is lost i want the server application to say client is ofline without client application is closed
    need it urgently plz do help

Leave a Comment