Click here to Skip to main content
Click here to Skip to main content

A Chat Application Using Asynchronous UDP sockets

By , 29 Dec 2006
 

Introduction

In the previous article, I discussed a chat application using TCP sockets. In this part, I will show how to do it using UDP sockets.

Differences between UDP and TCP

TCP is connection oriented, and provides error and flow control. UDP provides no such services, and relies on the application layer for them. UDP allows sending a packet with or without checksum; no connection is maintained, so each packet is sent independently. If a packet gets lost or the packets arrive out of order, then the application should detect and remedy the situation on its own. Also, UDP doesn’t give the security features of TCP like the three-way handshake.

So what is in UDP that TCP doesn’t do? Firstly, UDP supports multicast – sending a single packet to multiple machines. This is useful as it saves bandwidth, each packet is transmitted only once and the entire network receives it. UDP is also used in places where the overhead (delay) involved with TCP is expensive.

Some applications of UDP are in VoIP, streaming of audio and video, DNS, TFTP, SNMP, online gaming, and etcetera.

Asynchronous UDP sockets

Asynchronous UDP sockets have a Begin and End appended to the standard socket functions, like BeginSendTo, BeginReceiveFrom, EndSendTo, and EndReceiveFrom. Let's take a look at one of them:

IAsyncResult BeginReceiveFrom(byte[] buffer, int offset, int size,
 SocketFlags sockflag, ref EndPoint ep, AsyncCallback callback, object state)

The BeginReceiveFrom() method accepts data from any remote host on a connectionless socket. Notice that the BeginReceiveFrom() method is similar to the BeginReceive() method, except that it specifies a reference to an EndPoint object. The EndPoint object defines the remote host IP address and the port number that sent the data.

The AsyncCallback function is called when the function completes. Just as events can trigger delegates, .NET also provides a way for methods to trigger delegates. The .NET AsyncCallback class allows methods to start an asynchronous function and supply a delegate method to call when the asynchronous function completes.

The state object is used to pass information between the BeginAccept and the corresponding AsyncCallback function.

A sample BeginReceiveFrom() method would look like this:

sock.BeginReceive(data, 0, data.Length, SocketFlags.None, 
                  ref iep, new AsyncCallback(ReceiveData), sock);

The corresponding EndReceiveFrom() method is placed in the appropriate AsyncCallback method:

void ReceiveData(IasyncResult iar)
{
    Socket remote = (Socket)iar.AsyncState;
    int recv = remote.EndReceiveFrom(iar);
    string stringData = Encoding.ASCII.GetString(data, 0, recv);
    Console.WriteLine(stringData);
}

The EndReceiveFrom() method returns the number of bytes read from the socket, and places the received data in the data buffer defined in the BeginReceiveFrom() method. To access this data, the data buffer should be accessible from both the methods.

Getting Started

The architecture of the application using TCP sockets and the one using UDP sockets is very similar. Both applications use the same data structures to communicate between the server and the client.

For exchange of messages between the client and the server, both of them use the following trivial commands:

//The commands for interaction between 
//the server and the client
enum Command
{
  //Log into the server
    Login,      
  //Logout of the server
    Logout,
  //Send a text message to all the chat clients     
    Message,    
  //Get a list of users in the chat room from the server
    List
}

The data structure used to exchange between the client and the server is shown below. Sockets transmit and receive data as an array of bytes, the overloaded constructor and the ToByte member function performs this conversion.

//The data structure by which the server 
//and the client interact with each other
class Data
{
    //Default constructor
    public Data()
    {
        this.cmdCommand = Command.Null;
        this.strMessage = null;
        this.strName = null;
    }

    //Converts the bytes into an object of type Data
    public Data(byte[] data)
    {
        //The first four bytes are for the Command
        this.cmdCommand = (Command)BitConverter.ToInt32(data, 0);

        //The next four store the length of the name
        int nameLen = BitConverter.ToInt32(data, 4);

        //The next four store the length of the message
        int msgLen = BitConverter.ToInt32(data, 8);

        //Makes sure that strName has been 
        //passed in the array of bytes
        if (nameLen > 0)
            this.strName = 
              Encoding.UTF8.GetString(data, 12, nameLen);
        else
            this.strName = null;

        //This checks for a null message field
        if (msgLen > 0)
            this.strMessage = 
              Encoding.UTF8.GetString(data, 12 + nameLen, msgLen);
        else
            this.strMessage = null;
    }

    //Converts the Data structure into an array of bytes
    public byte[] ToByte()
    {
        List<byte> result = new List<byte>();

        //First four are for the Command
        result.AddRange(BitConverter.GetBytes((int)cmdCommand));

        //Add the length of the name
        if (strName != null)
            result.AddRange(BitConverter.GetBytes(strName.Length));
        else
            result.AddRange(BitConverter.GetBytes(0));

        //Length of the message
        if (strMessage != null)
            result.AddRange(
              BitConverter.GetBytes(strMessage.Length));
        else
            result.AddRange(BitConverter.GetBytes(0));

        //Add the name
        if (strName != null)
            result.AddRange(Encoding.UTF8.GetBytes(strName));

        //And, lastly we add the message 
        //text to our array of bytes
        if (strMessage != null)
            result.AddRange(Encoding.UTF8.GetBytes(strMessage));

        return result.ToArray();
    }

    //Name by which the client logs into the room
    public string strName;
    //Message text
    public string strMessage;
    //Command type (login, logout, send message, etc)
    public Command cmdCommand;
}

UDP Server

The following are some of the data members used by the server application:

//The ClientInfo structure holds the 
//required information about every
//client connected to the server
struct ClientInfo
{
    //Socket of the client
    public EndPoint endpoint;
    //Name by which the user logged into the chat room
    public string strName;
}

//The collection of all clients logged into 
//the room (an array of type ClientInfo)
ArrayList clientList;

//The main socket on which the server listens to the clients
Socket serverSocket;

byte[] byteData = new byte[1024];

One important point to note here is that, in UDP, there is no such distinction between the client and server applications. Unlike TCP, UDP servers don’t listen for incoming clients; they just look for data coming from other clients. Once we receive data, we see if it’s a message for login, logout, etc.

private void Form1_Load(object sender, EventArgs e)
{            
    try
    {
        //We are using UDP sockets
        serverSocket = new Socket(AddressFamily.InterNetwork, 
            SocketType.Dgram, ProtocolType.Udp);

        //Assign the any IP of the machine and listen on port number 1000
        IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 1000);

        //Bind this address to the server
        serverSocket.Bind(ipeServer);
        
        IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
        //The epSender identifies the incoming clients
        EndPoint epSender = (EndPoint) ipeSender;

        //Start receiving data
        serverSocket.BeginReceiveFrom (byteData, 0, byteData.Length, 
            SocketFlags.None, ref epSender, 
               new AsyncCallback(OnReceive), epSender);
    }
    catch (Exception ex) 
    { 
        MessageBox.Show(ex.Message, "SGSServerUDP", 
            MessageBoxButtons.OK, MessageBoxIcon.Error); 
    }
}

With IPAddress.Any, we specify that the server should accept client requests coming on any interface. To use any particular interface, we can use IPAddress.Parse (“192.168.1.1”) instead of IPAddress.Any. The Bind function then bounds the serverSocket to this IP address. The epSender identifies the clients from where the data is coming.

With BeginReceiveFrom, we start receiving the data that will be sent by the client. Note that we pass epSender as the last parameter of BeginReceiveFrom, the AsyncCallback OnReceive gets this object via the AsyncState property of IAsyncResult, and it then processes the client requests (login, logout, and send message to the users). Please see the code attached to understand the implementation of OnReceive.

TCP Client

Some of the data members used by the client are:

public Socket clientSocket; //The main client socket
public string strName;      //Name by which the user logs into the room
public EndPoint epServer;   //The EndPoint of the server

byte []byteData = new byte[1024];

The client firstly connects to the server:

try
{
    //Using UDP sockets
    clientSocket = new Socket(AddressFamily.InterNetwork, 
        SocketType.Dgram, ProtocolType.Udp);

    //IP address of the server machine
    IPAddress ipAddress = IPAddress.Parse(txtServerIP.Text);
    //Server is listening on port 1000
    IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 1000);

    epServer = (EndPoint)ipEndPoint;
    
    Data msgToSend = new Data ();
    msgToSend.cmdCommand = Command.Login;
    msgToSend.strMessage = null;
    msgToSend.strName = strName;

    byte[] byteData = msgToSend.ToByte();
    
    //Login to the server
    clientSocket.BeginSendTo(byteData, 0, byteData.Length, 
        SocketFlags.None, epServer, 
        new AsyncCallback(OnSend), null);
}
catch (Exception ex)
{ 
    MessageBox.Show(ex.Message, "SGSclient", 
        MessageBoxButtons.OK, MessageBoxIcon.Error); 
}

The client after connecting to the server sends a login message. And, after that, we send a List message to get the names of clients in the chat room.

//Broadcast the message typed by the user to everyone
private void btnSend_Click(object sender, EventArgs e)
{
    try
    {
        //Fill the info for the message to be send
        Data msgToSend = new Data();
        
        msgToSend.strName = strName;
        msgToSend.strMessage = txtMessage.Text;
        msgToSend.cmdCommand = Command.Message;

        byte[] byteData = msgToSend.ToByte();

        //Send it to the server
        clientSocket.BeginSendTo (byteData, 0, byteData.Length, 
            SocketFlags.None, epServer, 
            new AsyncCallback(OnSend), null);

        txtMessage.Text = null;
    }
    catch (Exception)
    {
        MessageBox.Show("Unable to send message to the server.", 
            "SGSclientUDP: " + strName, MessageBoxButtons.OK, 
            MessageBoxIcon.Error);
    }  
}

The message typed by the user is sent as a Command message to the server which then sends it to all other users in the chat room.

Upon receiving a message, the client processes it accordingly (depending on whether it’s a login, logout, command, or a list message). The code for this is fairly straightforward, kindly see the attached project.

Conclusion

As you would have noticed, the UDP chat application is not very different from the TCP one; in fact, converting the TCP one into UDP was less than a day's work for me. I hope you find these articles useful. Thank you.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Hitesh Sharma
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionusing specific portmemberadam aji nugroho29 Jul '12 - 18:02 
Hi,
the endpoint for each client has come in random port, if i want to assign the port of each client to a specific port, is it possible?
 
Thank you
Suggestiononly one thing about BeginReceiveFrommemberivanmen17 Jun '12 - 8:05 
you write this:
A sample BeginReceiveFrom() method would look like this:
sock.BeginReceive(data, 0, data.Length, SocketFlags.None,
ref iep, new AsyncCallback(ReceiveData), sock);
 
and you forgot write sock.BeginReceiveFrom(....
GeneralMy vote of 5membervasistans6 Apr '12 - 3:15 
Very nice dude
QuestionGreat Examplemembersgoleary9 Mar '12 - 2:34 
Thank you Hitesh. Great example of Asynchronous ReceiveFrom and SendTo.
GeneralMy vote of 4memberJoe Sonderegger19 Oct '11 - 20:38 
The example is good. I have a few improvements:
1. Make a common DLL for common parts of server and client.
2. Separate the code from the form so that it can be reused.
3. Avoid class names such as Data as it can be confused with the built in Dot Net classes.
 
Apart from that I am very impressed.
GeneralRe: My vote of 4memberSpricer27 Dec '11 - 1:07 
yes i do agree that is nice sample buth it work only on Lan Only
Generalwhen a client deconnect, the server can't receive message anymoremembermangepain23 May '10 - 6:03 
When I launch the server and some client in the same network or not or on the internet, all work fine. But if a client disconnect, the "OnReceive" method is not fired any more. The UDP packet is received by the machine if I analyse with Wireshark, but not received by SGSServerUDP
GeneralRe: when a client deconnect, the server can't receive message anymorememberSteve3635 Sep '11 - 15:16 
In the server code, SGServerForm.cs, if you look at the OnRecieve(IAsyncResult ar) function, the author has a check at the end that says if you logged out, there's no point in listening to you again. The author obviously wasn't thinking that someone who logged out might want to log back in again. So all you have to do is comment out that check at the bottom of the OnReceive function so that the server always performs a BeginReceiveFrom call after an OnReceive event.
GeneralRe: when a client deconnect, the server can't receive message anymorememberbatsword7 Sep '11 - 3:24 
Big Grin | :-D
that's right ,some play has been modify

GeneralDisconnecting and reconnecting with same nickname doesnt work!memberzimmwarrior1 May '10 - 7:51 
Hello, thank you for providing this sourcecode.
 
I have following problem.
If I start server and client it works fine.
Now if I shut down client and start it again it doesn't log it into the server and I don't see my nickname in the nicklist and there is no message for new connection shown in the serverlog!
Please any help?
GeneralRe: Disconnecting and reconnecting with same nickname doesnt work!memberSteve3635 Sep '11 - 15:16 
In the server code, SGServerForm.cs, if you look at the OnRecieve(IAsyncResult ar) function, the author has a check at the end that says if you logged out, there's no point in listening to you again. The author obviously wasn't thinking that someone who logged out might want to log back in again. So all you have to do is comment out that check at the bottom of the OnReceive function so that the server always performs a BeginReceiveFrom call after an OnReceive event.
GeneralRe: Disconnecting and reconnecting with same nickname doesnt work!memberMember 973023614 Jan '13 - 18:20 
I believe the issue here is line 127 on the server code:
 
if (client.endpoint == epSender)
 
I don't believe that clients get removed from the list correctly. What worked for me was making it remove from the client list based on name and made sure names were unique as clients are logging in. I thought this made sense since it would be confusing if two users had the same name.
QuestionHow can you get this to work over the internet such as sending messages from one computer to another?membercodewizard5626 Mar '10 - 19:48 
I've tried doing it with my ip address and it doesn't seem to work.
Questionif client listening to multiple multicast adr, how to detect receive msg from which multicast groupmemberBatulKaleem11 Mar '10 - 5:31 
Hi,
I am creating a client application which listens to multiple multicast groups. I have been able to receive messages from all the multicast groups that the client is subscribed to. However, in my application i need to be able to seperate the messages based on which multicast group the message was received on. How can I do that?
Help greatly appreciated.
Thanks
GeneralSniffer displays OICQ protocolmembertriplebit15 Nov '09 - 23:26 
Why does the sniffer (Wireshark)displays OICQ protocol when running on client and/or server PC?
Regards
Izack
Generalthanx alotmembermr_sawari8 Oct '09 - 1:15 
thanx it,s great and so clean Article . Big Grin | :-D
GeneralQuestionmemberAlireza_13627 Oct '09 - 12:34 
Hi and thanks For Your Good article
i have a question :
please tell my why you used this code in your program:
CheckForIllegalCrossThreadCalls = false;
thanks
GeneralRe: Questionmemberzimmwarrior21 May '10 - 9:36 
This function solves an Cross Thread Call exceptions.
It's like if you delete this line when the program will try to write something into a chatlog after recieving some data program will be aborted with an exception Illegal Thread Call because you are trying to access your chatlog "your textbox" from a thread which was actually created for main program thread.
I've actually had same problem with my own chat in C# well, I somehow thought on this one chat here and used this function in my chat so I got the cross thread calls exception gone. Smile | :)
Hope I could help!
Greets Andriy!
General[Message Deleted]memberit.ragester28 Mar '09 - 5:35 
[Message Deleted]
QuestionImplementation of Asynchronous operation in web applicationmemberM. J. Jaya Chitra10 Mar '09 - 20:12 
Hi Hitesh Sharma,

I have implemented the same in the windows application and window service in asynchronous mode - It is working fine,
 
But when I use the same method in web application it gives an error and if I make it to have the communication in synchronous method it is working fine.,
 
Kindly tell me how to overcome this?
 
Thanks a lot in advance.
 
Best Regards,
M. J. Jaya Chitra

GeneralUsing UDP, Socket.ReceiveFrom, BeginReceiveFrom/EndReceiveFrom Incorrectly report remote host disconnected conditionmemberPradeepMM7 Jan '09 - 9:19 
To reproduce this error.
 
1. Start SGSServer
2. Start SGSCLient.
3. kill SGSClient.
4. Start SGSClient results in error of " An existing connection was forcibly closed by the
remote host"
 
Any workaround that you can provide?
 
Pradeep M M

GeneralRe: Using UDP, Socket.ReceiveFrom, BeginReceiveFrom/EndReceiveFrom Incorrectly report remote host disconnected conditionmemberdonperry23 Jun '09 - 15:51 
Bear in mind that UDP is connectionless. U have no reference to a connection, just Endpoints (IP:PORT)
QuestionHow To Send Data From Client To Server via Server Side ?memberYefta8 Sep '08 - 23:47 
First of all, Thanks for your Example Application about Socket Connection. But I have one problem here.
 
I have mini project about client-server connection and i'm using your concept to do a connection. The server wants to know if a client has a 'notepad.exe' application running on the client machine. I already have list of running process on client machine, and i have an algorithm to detect if Notepad is already running or not. So, I think there will be a 'Refresh' button on server in order to make Client send data to Server but still in Server Side. Do you get my point ? Sent Data is a simple data. I'm using string data(e.g. "Notepad is already running", or "Notepad has not started yet")
 
Just Like my Subject Title, I want to know How to send data from client to server via server side ? Urgently Needed
 
Thanks for any help
 
Best Regards
Yefta Adiya - Indonesia -
C# Developer
GeneralPerfectmemberalejandro29A14 Aug '08 - 3:12 
thank you very much
 
alejandro varela
 
Dios existe pero duerme...
Sus pesadillas son nuestra existencia.
(Ernesto Sabato)

QuestionSystem.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permittedmemberMember 407262230 Jul '08 - 4:09 
It seems this exception accures because both server and client programs are listening in the same time. although they are listening to different ports.
 
What i should do?

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 29 Dec 2006
Article Copyright 2006 by Hitesh Sharma
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid