Robert Whitney
Robert Whitney

Reputation: 11

How to properly listen for UDP packets while doing other things in Visual C#

Alright, so I'm pretty new to C# and I'm definitely pretty new to graphical programming. I'm using Visual Studio 2015 and writing my application in C#. I have this hunk of code that I've been toying around with for a while. Essentially my program will send the HELLO, to the server, but the server isn't sending HELLO back. I have no firewall in the middle of client and server right now, but the process is getting hung waiting for the reply back. I honestly don't even want to do it this way, I want the listener to always run in the background while the user does other stuff so that my program functions, well normal. So I come to you oh great Stackoverflow... because I am definitely doing it wrong! Could someone please point me the right direction?

Current Code:

    private void button1_Click(object sender, EventArgs e)
    {
        byte[] data = new byte[512];
        byte[] result;
        SHA512 shaM = new SHA512Managed();
        result = shaM.ComputeHash(Encoding.UTF8.GetBytes(this.password.Text));
        var hash = BitConverter.ToString(result).Replace("-", "");
        this.send_message("127.0.0.1", 10545, 10545, "HELLO");
        this.send_message("127.0.0.1", 10545, 10545, "AUTH:" + this.login.Text + ":" + hash);

        //ListenForData.Start();

    }

    private void send_message(string server, int localPort, int remotePort, string message)
    {
        label4.Text = "Listening on port:" + localPort;
        IPEndPoint lep = new IPEndPoint(IPAddress.Any, localPort);
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        IPAddress loginServer = IPAddress.Parse(server.ToString());
        byte[] sendbuf = Encoding.ASCII.GetBytes(message);
        IPEndPoint ep = new IPEndPoint(loginServer, remotePort);
        s.SendTo(sendbuf, ep);

        try
        {
            UdpClient udpClient = new UdpClient(lep);
            byte[] bytes = udpClient.Receive(ref lep);
            label4.Text = ep.ToString();
        } catch ( Exception ex )
        {
            Console.WriteLine(ex.ToString());
        }

    }

UDPATE:

   private static void UDPListener(object obj)
    {
        Task.Run(async () =>
        {
            using (var udpClient = new UdpClient(10545))
            {
                string rMessage = "";
                string[] rArgs = new string[0];
                while (true)
                {
                    //IPEndPoint object will allow us to read datagrams sent from any source.
                    var receivedResults = await udpClient.ReceiveAsync();
                    rMessage += Encoding.ASCII.GetString(receivedResults.Buffer);
                    rArgs = rMessage.Split(new char[] { ':' });
                    if( rArgs[0] == "HELLO")
                    {
                        Console.Write("Received HELLO from server.");
                        byte[] data = new byte[512];
                        byte[] result;
                        SHA512 shaM = new SHA512Managed();
                        result = shaM.ComputeHash(Encoding.UTF8.GetBytes(obj.password.Text));
                        var hash = BitConverter.ToString(result).Replace("-", "");
                        send_message("127.0.0.1", 10545, 10545, "AUTH:" + obj.login.Text + ":" + hash);
                    }
                }
            }
        });
    }

Upvotes: 0

Views: 4111

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70652

You can safely ignore the advice from Blindy. The UdpClient.ReceiveAsync() method is specifically designed around the Task paradigm, and is much more efficient than dedicating a thread to receiving data. Because the Task is awaitable, it's also easier to integrate the ReceiveAsync() approach with a GUI program (e.g. Winforms or WPF). That said, for all that to work, you want to execute the ReceiveAsync() call in the UI thread, rather than in another Task.

Unfortunately, lacking a good, minimal, complete code example that clearly illustrates your question, it's not possible to say for sure how that would look. But it most likely would involve making your UDPListener() method an async method and calling it from the UI thread. Also, since you say the method can't access non-static members, the obvious solution to that is to make the method itself non-static. E.g.:

private static async Task UDPListener(object obj)
{
    using (var udpClient = new UdpClient(10545))
    {
        string rMessage = "";
        string[] rArgs = new string[0];
        while (true)
        {
            //IPEndPoint object will allow us to read datagrams sent from any source.
            var receivedResults = await udpClient.ReceiveAsync();
            rMessage += Encoding.ASCII.GetString(receivedResults.Buffer);
            rArgs = rMessage.Split(new char[] { ':' });
            if( rArgs[0] == "HELLO")
            {
                Console.Write("Received HELLO from server.");
                byte[] data = new byte[512];
                byte[] result;
                SHA512 shaM = new SHA512Managed();
                result = shaM.ComputeHash(Encoding.UTF8.GetBytes(obj.password.Text));
                var hash = BitConverter.ToString(result).Replace("-", "");
                send_message("127.0.0.1", 10545, 10545, "AUTH:" + obj.login.Text + ":" + hash);
            }
        }
    }
}

It is not clear from your updated question whether you have fixed your failed to receive a reply yet. But if you have not changed your send_message() method, it has some obvious problems, especially in the context of creating a separate UdpClient instance to receive datagrams.

The most obvious issue is that you do not create the UdpClient instance which you're expecting to use to receive the response until after you have sent the message. This is wrong for two reasons:

  1. It's entirely possible that the remote endpoint will send the response before you've created the socket. If that happens, the datagram will just be dropped.
  2. More importantly, the usual design for a server is to send a response to the remote endpoint that sent it the message in the first place. I.e. in this case that would be the socket s. Even if you did create the udpClient object before calling s.SendTo(), the reply would actually be sent to the s socket, not the udpClient.Client socket.

Again, without a good code example it's not possible to know what your server actually does. But whether it behaves like a normal server, or is some non-standard implementation, the code you've posted cannot reliably receive a response from the server.

You should fix your design so that your client creates only a single socket (e.g. an initial UdpClient instance). You would prepare for communication by calling ReceiveAsync(), and only once you've done that, thus ensuring you're ready to receive a response, then you can send data.

Make sure that you create only this single object.

Note also that client-side implementations typically do not bind to a specific port. Instead, you let the OS assign a port. Only the server needs to bind to a specific port, so that inbound requests from unknown endpoints can know to what port to send their request. The server's receive operation will include the port number of the client, so the server will know to what port to send its response, without the client having selected any special port number.

Finally, I strongly recommend you study existing socket programming tutorials, and especially the Winsock Programmer's FAQ. None of the material there is directly applicable to the .NET socket API, but most of the issues you will have trouble with are exactly the kinds of things documented there. It is not possible to use the .NET socket API without also having a good basic comprehension of socket programming generally.

If the above does not get you headed in the right direction, please make sure that any future questions you ask include a good code example, as described in the link I provided above. Note that for any networking question, a complete code example includes both the client and server. It is not possible for anyone to fully understand your question, never mind to test and fix your code example, without implementations of both.

Upvotes: 3

Related Questions