Mare Infinitus
Mare Infinitus

Reputation: 8192

UDP sending / receiving on a free port

In a project there is a device that listens on a specific UDP port and answers to the senders port.

For the sender and receiver I want the system to choose a free port, so I have the following code:

[Please excuse the vast masses of code, but this is the smallest example to show what behaviour occurs]

Sending code:

public class UdpSender
{
    public int Port = 0; // some initially random port
    public UdpClient UdpServer { get; set; }

    public UdpSender()
    {
        UdpServer = CreateUdpClient();

        // save the portnumber chosen by system to reuse it
        Port = ((IPEndPoint)(UdpServer.Client.LocalEndPoint)).Port;
    }

    private UdpClient CreateUdpClient()
    {
        if (UdpServer != null)
        {
            UdpServer.Close();
        }

        IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, Port);

        var udpServer = new UdpClient();
        udpServer.ExclusiveAddressUse = false;
        udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpServer.Client.Bind(localEndPoint);

        return udpServer;
    }

    public void Send(byte[] arr)
    {
        UdpServer = CreateUdpClient();

        int remotePortNumber = 6565;
        var remoteEndPoint = new IPEndPoint(IPAddress.Broadcast, remotePortNumber);

        try
        {
            UdpServer.Send(arr, arr.Length, remoteEndPoint);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }

        UdpServer.Close();
    }
}

Receiving code:

public class UDPListener
{
    private static int portNumber;
    private UdpClient udpClient = null;
    public List<DeviceData> DeviceList = new List<DeviceData>();

    public UDPListener(int port)
    {
        portNumber = port;
        IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, portNumber);

        udpClient = new UdpClient();
        udpClient.ExclusiveAddressUse = false;

        udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpClient.Client.Bind(localEndPoint);
        udpClient.Client.ReceiveBufferSize = 1 << 23;
    }

    public void StartListening()
    {
        this.udpClient.BeginReceive(Receive, new object());
    }

    private void Receive(IAsyncResult ar)
    {
        IPEndPoint ip = new IPEndPoint(IPAddress.Any, portNumber);
        byte[] bytes = udpClient.EndReceive(ar, ref ip);
        string message = Encoding.ASCII.GetString(bytes);
        DeviceList.Add(new DeviceData(message));
        StartListening();
    }
}

Bringing it together:

public class DeviceFinder
{
    public IEnumerable<DeviceData> Find()
    {
        var sender = new UdpSender();
        int port = sender.Port;

        var listener = new UDPListener(port);
        listener.StartListening();

        sender.Send(new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 });

        System.Threading.Thread.Sleep(5000); // wait some time for answers

        return listener.DeviceList;
    }
}

The Receive method is never called with this approach. But in Wireshark, I can see an answer coming from the device.

What is wrong about this approach?

Before using this approach, I have used a fixed port and the call to CreateUdpClient was added also. Seems to have something to with that, but I cannot figure it out. Before I just created a UdpClient with the fixed port just inside the receive / send method.

The previous version can be seen in this question. This works perfectly. But I fear if the fixed port is already in use, it does not work, hence this new approach.

Upvotes: 2

Views: 5328

Answers (1)

user207421
user207421

Reputation: 311052

Just specify zero as your own port number. The system will allocate one when you bind or send. The source port number will accompany the datagram to the peer, who should reply to it.

Upvotes: 5

Related Questions