C#获取UDP服务器套接字接收数据的客户端IP地址

13

我有一个非常奇怪的问题。我无法找到我的服务器接收数据的客户端的IP地址。以下是我的UDP监听器类。

IPPacketInformation不包含IP地址。我在EndReceiveMessageFrom中引用的clientEndPoint也没有。

当我...

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

我得到了服务器的IP地址。我将服务器托管在自己的机器上,所以我获得了自己的IP地址。当我尝试访问clientEndPoint.remoteEndPoint时,它会抛出一个错误,因为套接字没有连接(由于是UDP)。
基本上,来自外部IP的客户端能够发送数据,但是我无法回答客户端,因为我无法检索它的IP地址。任何帮助都将不胜感激!
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);
    }
}

我已经尝试禁用防火墙,但没有帮助。我还在路由器上进行了端口转发,因此可以从外部客户端接收数据。

编辑以澄清问题:

服务器托管在我的机器上,位于一个具有公共IP 214.214.214.214的NAT后面。

客户端是另一台机器,位于另一个具有公共IP 910.910.910.910的NAT后面。

客户端向服务器发送消息,服务器接收并能够读取内容。当服务器获取客户端的IPEndPoint时,显示的IP是214.214.214.214(服务器的IP,而不是客户端的IP)

编辑也许我应该说,在我从ISP那里订购“动态IP”之前,我无法从外部网络的客户端接收消息。仍然无法获得源的公共IP。

编辑当使用Wireshark嗅探从外部客户端发送到我的服务器的数据包时,可以看到它是错误的src IP。在下面的图片中,src IP是我的服务器IP,而不是发送数据的客户端的IP。

enter image description here


你为什么要监听零端口?IPEndPoint(IPAddress.Any, 0); - jdweng
我正在监听36200端口。如果您指的是remoteEndPoint或clientEndPoint端口为0,则每当接收到消息时,endPoint都会被覆盖。 - MatMat
你考虑过使用UdpClient吗? - Fildor
我已经尝试过了。结果一样。我还尝试了Receive,ReceiveFrom和ReceiveAsync。 - MatMat
当在同一台机器上运行时,它完全正常工作。问题是,当您在另一台机器上运行客户端并且位于另一个路由器后面时 - 服务器端不会收到客户端IP,而是我的自己的IP。 - MatMat
显示剩余5条评论
2个回答

8

经过几天的阅读和研究路由器、网络相关知识以及编写不同的Receive, ReceiveFrom, ReceiveMessageFrom, BeginReceive, BeginReceiveFrom, BeginReceiveMessageFromReceiveAsync示例,我终于解决了我的问题。

我更换了我的路由器。现在,我可以使用一个非常古老的路由器获取外部客户端的源IP。之前使用的路由器是一个新的Docsis 3.1。

为什么旧的路由器能够正常工作,我不知道,但是出于某种原因,Docsis 3.1在将UDP消息传递到我的机器之前会将源IP更改为其自己的IP。


我感同身受。在使用C#套接字时,有关数据包的实际处理方式的信息非常有限。不同的答案建议使用“多播”进行通信,但在经过数天的故障排除后,您会发现并非所有路由器都支持igmp数据包分辨率。此外,当您创建UdpClient时,实际上发生了什么,例如它绑定在哪个网络接口上。或是何时使用Receive与BeginReceive。 - Adam Polak Moetsi

1
使用UdpClient类,您可以在接收消息时实际上检索远程端点。
我使用以下解决方案来解决此任务:
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}");
}

尝试使用服务器公共 IP:214.214.214.214 和客户端公共 IP:910.910.910.910 运行此示例。当从客户端发送消息到服务器时,服务器上打印的 endPoint 包含 "214.214.214.214:57650" 而不是 "910.910.910.910:57650"。 - MatMat
1
客户端公共IP:910.910.910.910?这怎么可能是客户端IP?它不是一个有效的IP地址。 - TheSoftwareJedi
1
这只是一个示例,因为我不会提供我的IP。 - MatMat
不幸的是,这是一个天真的解决方案。在IPAddress.Any上创建UdpClient如果选择了错误的网络接口将无法提供正确的功能。有些计算机可能有USB WiFi dongles,或者有多个网络接口。 - Adam Polak Moetsi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接