Search code examples
c#socketsudpudpclient

UDP client assigns free port too late


In C#, .Net 4, I have a need to send a UDP message on a port and listen for responses on the same port.

I was using a fixed port, but a customer ran into an issue with that, so I want to use any available port. It seems like I can do that by using 0 for the port number, but it isn't working. Digging into that some more, it appears it doesn't assign the port until it is actually used, so my two bind statements might be going to different ports.

From MSDN:

"If you do not care which local port is used, you can create an IPEndPoint using 0 for the port number. In this case, the service provider will assign an available port number between 1024 and 5000. If you use the above approach, you can discover what local network address and port number has been assigned by calling the LocalEndPoint. ... If you are using a connectionless protocol, you will not have access to this information until you have completed a send or receive."

Trouble is, I want to set up my send and receive on initialization. If I wait until the first send to set up the receive, I might miss some responses. Is there a better answer than just sending a garbage message to assign the port so I can finish the initialization?

My code:

    public bool InitializeSockets()
    {
        try
        {
            IPAddress localaddr = LocalIPAddress();
            localep = new IPEndPoint(localaddr, 0);  //(was port 50000);


            //-----------------------------------------------------------------
            // set up listener port for responses coming back on the same port
            //-----------------------------------------------------------------
            listener = new UdpClient();
            listener.ExclusiveAddressUse = false;
            listener.Client.SetSocketOption(SocketOptionLevel.Socket,
                SocketOptionName.ReuseAddress, true);
            listener.Client.Bind(localep);

            detailsOutputText = "Ready to listen on " + localep;

            ustate = new UdpState();
            ustate.e = localep;
            ustate.u = listener;

            //------------------------
            // set up broadcast port
            //------------------------
            bcast = new UdpClient();
            bcast.Client.SetSocketOption(SocketOptionLevel.Socket,
                SocketOptionName.ReuseAddress, true);
            bcast.Client.Bind(localep);

            //-------------------------------
            // start listening for responses
            //-------------------------------
            msgRxCallback = listener.BeginReceive(new AsyncCallback(DiscoveryCallback), ustate);

            return true;
        }
        catch (Exception exc)
        {
            if (exc is SocketException)
            {
                // This only catches if another process has opened that port without sharing it
                // or if firewall blocks it?
                MessageBox.Show("Error opening IP address:Port : " + localep;
            }
            else
                MessageBox.Show(exc.ToString());
            return false;
        }
    }

Thanks


Solution

  • Two options:

    1. Create a configuration file that contains the port number. On initialization, read the config file and use that port number when setting up the clients. Supply a config file with a default port number, and give your customer instructions about how to change it if required.
    2. In your initialization, create a receiver, give it a very short receive timeout, and call Receive. That will cause the port to bind. You can then get the local end point and use that when you create your sender. See Can I set the timeout for UdpClient in C#? regarding setting the receive timeout.