12
votes

I have a very strange problem. I'm not able to find the IP Address of the client which my server receives data from. Below is my UDP Listener class.

The IPPacketInformation does not contain the IP. The clientEndPoint which I reference in my EndReceiveMessageFrom does neither.

When I

Console.Writeline(((IPEndPoint)clientEndPoint).Address); 

I get the IP Address of the server. I have the server hosted on my own machine so I get my own IP Address. When I try to access clientEndPoint.remoteEndPoint it throws an error because the socket isn't connected (due to being UDP).

So basically, a client from an external IP is able to send data, but I can't answer the client since I'm not able to retrieve it's IP. Any help is appreciated!

public class UdpListener
{
    private Socket s;
    public byte[] ReceiveBuffer = new byte[2048];

    public UdpListener()
    {
        s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true);
        s.Bind(new IPEndPoint(IPAddress.Any, 36200));
    }

    public void Listen()
    {
        try
        {
            EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
            s.BeginReceiveMessageFrom(ReceiveBuffer, 0, ReceiveBuffer.Length, SocketFlags.None, ref remoteEndPoint, Recv, s);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
            throw;
        }
    }

    private void Recv(IAsyncResult res)
    {
        try
        {
            Socket receiveSocket = (Socket)res.AsyncState;

            EndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, 0);
            IPPacketInformation packetInfo;
            SocketFlags flags = SocketFlags.None;
            int udpMessageLength = receiveSocket.EndReceiveMessageFrom(res, ref flags, ref clientEndPoint, out packetInfo);
            byte[] udpMessage = new byte[udpMessageLength];
            Array.Copy(ReceiveBuffer, udpMessage, udpMessageLength);

            Console.WriteLine(
                "{0} bytes received from {1} to {2}",
                ReceiveBuffer,
                clientEndPoint,
                packetInfo.Address
            );

            EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, ((IPEndPoint)receiveSocket.LocalEndPoint).Port);
            s.BeginReceiveMessageFrom(ReceiveBuffer, 0, ReceiveBuffer.Length, SocketFlags.None, ref remoteEndPoint, Recv, s);

            //Process data
            RaiseDataReceived(new ReceivedDataArgs(packetInfo.Address, ((IPEndPoint)clientEndPoint).Port, udpMessage));
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
            throw;
        }
    }

    public delegate void DataReceived(ReceivedDataArgs args);

    public event DataReceived DataReceivedEvent;

    private void RaiseDataReceived(ReceivedDataArgs args)
    {
        DataReceivedEvent?.Invoke(args);
    }
}

I've tried to disable my firewall but that does not help. I've also done Port Forwarding on my router thus I can receive data from external clients.

EDIT to clarify the problem:

Server hosted on my machine behind a NAT with public IP 214.214.214.214.

The client is another machine, behind another NAT with public IP 910.910.910.910.

The client sends a message to the server, server receives it and able to read the content. When server get the IPEndPoint of the client, the displayed IP is 214.214.214.214 (IP of the server, not the Client)

EDIT Maybe I should say that I wasn't able to receive messages from clients on external networks until I ordered a "Dynamic IP" from my ISP. Still can't get the public IP of the source.

EDIT When using Wireshark and sniff a packet sent from external client to my server I can see it's the wrong src IP as well. In the picture below the src IP is my server IP and not the IP of the client that sent the data.

enter image description here

2
Why are you listening to Port Zero? IPEndPoint(IPAddress.Any, 0);jdweng
Im listening on port 36200. If you mean the remoteEndPoint or clientEndPoint port 0 the endPoint get's overridden whenever a message is receivedMatMat
Have you considered using UdpClient?Fildor
I have tried that. Same result. I've also tried Receive, ReceiveFrom, ReceiveAsync.MatMat
Are you sure the ovderriding of the port number is working. I never heard of a port number changing once the Listener is started.jdweng

2 Answers

8
votes

After several days of reading everything I possibly could on routers, network and writing different examples of Receive, ReceiveFrom, ReceiveMessageFrom, BeginReceive, BeginReceiveFrom, BeginReceiveMessageFrom, and ReceiveAsync - I've solved my issue.

I changed my router. I can now get the source IP of the external client using a very old router. The router I was using before was a new Docsis 3.1.

Why it works with an old router, I don't know, but for some reason, the Docsis 3.1 changed the source IP to its own IP before letting the UDP message to my machine.

1
votes

With using the UdpClient class you can actually retrieve the remote endpoint when receiving a message.

I have used a solution like the following to solve this task:

public void StartServer()
{
    var udpServer = new UdpClient(new IPEndPoint(IPAddress.Any, ConnectionInformation.DETECTION_PORT));
    udpServer.BeginReceive(new AsyncCallback(detectionCallback), udpServer);
}

private void detectionCallback(IAsyncResult ar)
{
    var client = (ar.AsyncState as UdpClient);
    if (client.Client == null) return;

    var endPoint = new IPEndPoint(IPAddress.Any, ConnectionInformation.DETECTION_PORT);
    var bytes = client.EndReceive(ar, ref endPoint);

    Debug.WriteLine($"Detection request from: {endPoint}");
}