Confused about Sockets with UDP Protocol in C#

11,461

You have got stuff a bit wrong.

First of all, UDP is connection-less. You do not connect or disconnect. All you do is to send and receive (must specify destination each time). You should also know that the only thing UDP promises is that a complete message arrives on each read. UDP do not guarantee that your messages arrive in the correct order or that they arrive at all.

TCP on the other hand is connection-based. You connect, send/receive and finally disconnect. TCP is stream-based (while UDP is message-based) which means that you can get a half message in the first read and the other half at the second read. TCP promises you that everything will arrive and in the correct order (or will die trying ;). So using TCP means that you should have some kind of logic to know when a complete message has arrived and a buffer that you use to build the complete message.

The next big question was about blocking. Since you are new at this, I recommend that you use Threads to handle sockets. Put the listener socket in one thread and each connecting socket in a separate thread (5 connected clients = 5 threads).

I also recommend that you use TCP since it's easier to build complete messages than ordering messages and build a transaction system (which will needed if you want to make sure that all messages arrives to/from clients).

Update

You still got UDP wrong. Close doesn't do anything other than cleaning up system resources. You should do something like this instead:

public void MySimpleServer(string address, int port) 
{
    try 
    {
        byte[] bufferBytes = new byte[32];

        if(address.Equals("0.0.0.0")) {
            udpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
        } else {
            udpSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
        }

        remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
        while (serverCanRun)
        {
            int numBytesReceived = udpSocket.ReceiveFrom(bufferBytes, ref remoteEndPoint);

            // just means that one of the clients closed the connection using Shutdown.
            // doesnt mean that we cant continue to receive.
            if(numBytesReceived == 0)
                continue; 

            // same here, loop to receive from another client.
            Packet syncPacket = new Packet(bufferBytes);
            if (syncPacket.PacketType != PacketType.Control)
                continue; 

            HandlePacket(packet, endPoint);
        }
    } catch {
        if(udpSocket != null) {
            udpSocket.Close();
        }
    }
}

See? since there are no connection it's just waste of time to close a UDP socket to start listening from another one. The same socket can receive from ALL udp clients that know the correct port and address. That's what the remoteEndPoint is for. It tells which client that send the message.

Update 2

Small update to make a summary of all my comments.

UDP is connectionless. You can never detect if a connection have been established or disconnected. The Close method on a UDP socket will only free system resources. A call on client.Close() will not notify the server socket (as it will with TCP).

The best way to check if a connection is open is to create a ping/pong style of packet. i.e. the client sends a PING message and the server responds with a PONG. Remember that UDP will not try to resend your messages if they do not arrive. Therefore you need to resend the PING a couple of times before assuming that the server is down (if you do not receive a PONG).

As for clients closing you need to send your own message to the server telling it that the the client is going to stop talking to the server. For reliability the same thing goes here, keep resending the BYE message until you receive a reply.

imho it's mandatory that you implement a transactional system for UDP if you want reliability. SIP (google rfc3261) is an example of a protocol which uses transactions over UDP.

Share:
11,461
rfgamaral
Author by

rfgamaral

Updated on July 13, 2022

Comments

  • rfgamaral
    rfgamaral over 1 year

    I've just started learning Sockets through various Google searches but I'm having some problems figuring it out how to properly use Sockets in C# and I'm in the need of some help.

    I have a test application (Windows Forms) and on a different class (which is actually in it's own .dll, but that's irrelevant) I have all the server/client code for my sockets code.

    Question 1)

    On my test application, on the server part, the user can click the "start listening" button and the server part of my sockets application should start listening for connections on the specified address and port, so far so good.

    However, the application will be blocked and I can't do anything until someone connects to the server. What if no one connects? How should I handle that? I could specify a receive timeout but what then? It throws an exception, what can I do with that? What I would like is to have some sort of activity on the main application so the user knows the application didn't froze and is waiting for connections. But if a connection doesn't come, it should timeout and close everything.

    Maybe I should use asynchronous calls to send/receive methods but they seem confusing and I was not able to make it work, only synchronous work (I'll post my current code below).

    Question 2)

    Do I need to close anything when some send/receive call times out. As you'll see on my current code, I have a bunch of closes on the socket, but this doesn't feel right somehow. But it also doesn't feel right when an operation times out and I don't close the socket.

    In conclusion of my two questions.... I would like an application that doesn't block so the user knows the server is waiting for a connection (with a little marquee animation for instance). If a connection is never established after a period of time, I want to close everything that should be closed. When a connection is established or if it doesn't happen after a period of time, I would like to inform the main application of the result.

    Here's some of my code, the rest is similar. The Packet class is a custom class that represents my custom data unit, it's just a bunch of properties based on enums for now, with methods to convert them to bytes and back into properties.

    The function that starts to listen for connections is something like this:

    public void StartListening(string address, int port) {
        try {
            byte[] bufferBytes = new byte[32];
    
            if(address.Equals("0.0.0.0")) {
                udpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
            } else {
                udpSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
            }
    
            remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
    
            int numBytesReceived = udpSocket.ReceiveFrom(bufferBytes, ref remoteEndPoint);
    
            if(numBytesReceived == 0) {
                udpSocket.Close();
                return;
            }
    
            Packet syncPacket = new Packet(bufferBytes);
    
            if(syncPacket.PacketType != PacketType.Control) {
                udpSocket.Close();
                return;
            }
        } catch {
            if(udpSocket != null) {
                udpSocket.Close();
            }
        }
    }
    

    I'm sure that I have a bunch of unnecessary code but I'm new at this and I'm not sure what do, any help fixing up my code and how to solve the issues above is really appreciated.

    EDIT:

    I should probably have stated that my requirements are to use UDP and implement these things myself in the application layer. You can consider this as homework but I haven't tagged as such because the code is irrelevant and will not be part of my grade and my problem (my question) is in "how to code" as my Sockets experience is minimal and it's not taught.

    However I must say that I solved my problem for now I think... I was using threading on the demo application which was giving me some problems, now I'm using it in the protocol connections, makes more sense and I can easily change my custom protocol class properties and read those from the demo application.

    I have specified a timeout and throw a SocketException if it reaches the timeout. Whenever an exception like this is caught, the socket connection is closed. I'm just talking about the connection handshake, nothing more. If no exceptions are caught, the code probably went smooth and the connection is established.

    Please adapt your answers accordingly. Right now it doesn't make sense for me to marky any of them as the accepted answer, hope you understand.

    • Emaad Ali
      Emaad Ali about 13 years
      you running the application as thread?
    • rfgamaral
      rfgamaral about 13 years
      No. I tried but I wasn't able to achieve what I described above.
    • Emaad Ali
      Emaad Ali about 13 years
  • rfgamaral
    rfgamaral about 13 years
    Yes, I think I get it. What now if I need to communicate with between the server/client to terminate a connection? Let's assume I exchanged a few packets between the two to confirm that the connection should be terminated, how to close it really? Just call socket.Shutdown() on the client and set serverCanRun = False in the server or there's something else that needs to be done? Besides calling Close on both to free up system resources.
  • jgauffin
    jgauffin about 13 years
    There is no connection (seriously). The server can NEVER detect if a client disconnects since there are no connection between client/server in UDP. The only way you can detect it is to send a own BYE message (per your own protocol format)
  • jgauffin
    jgauffin about 13 years
    It's also very important that you understand that UDP do not do any extra effort to deliver your messages. You have to implement logic to make sure that your messages arrive and do not disappear on the way.
  • rfgamaral
    rfgamaral about 13 years
    Sure, but my question is how to close the socket connection? Not how the server/client knows that each other as disconnected.
  • jgauffin
    jgauffin about 13 years
    You can't. There is no connection. Closing the server socket just means that system resources are freed and you can not receive anything from any of your clients. Closing a client socket only means that system resources are cleaned up. If you want to automatically be able to detect if a connection is alive or closed you have to switch to TCP.
  • SilverX
    SilverX almost 12 years
    Great answer, the only problem is you should never use thread-per-client in .net if you plan to have an arbitrary (possibly) large number of clients "connected" to the server. It never ever scales very well. Having a single (or two) thread that queue's tasks for all sockets, and each of those sockets using .net's asynchronous patterns scale far more efficiently. Not to mention the Async functions will use IOCP when available!
  • jgauffin
    jgauffin almost 12 years
    @SilverX: Sorry, but UDP doesn't use connections and there are no thread per connection. The same server handles all connections. So it scales better than a TCP server with one connection per thread. The async methods are of course available and could be used to get a lot better performance. But with the question in mind I didn't think that it was necessary. Feel free to check out my griffin.networking at github you want to see a performant network library.
  • SilverX
    SilverX almost 12 years
    I know about sockets thank you. I have been working with them for many years. I put "connected" in quotes because I am aware that UDP is connectionless. I also never said anything about thread-per-connection, I said thread-per-client. Which, no matter how redundant, is still possible to write. However I was referring to your comment: "Put the listener socket in one thread and each connecting socket in a separate thread (5 connected clients = 5 threads)." and about how it performs very poorly when there are many clients. Nothing more.