C# UDP Socket:获取接收者地址

C# UDP Socket: Get receiver address(C# UDP Socket:获取接收者地址)

本文介绍了C# UDP Socket:获取接收者地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个异步 UDP 服务器类,其套接字绑定在 IPAddress.Any 上,我想知道接收到的数据包发送到哪个 IPAddress(...或在哪个 IPAddress 上接收).看来我不能只使用 Socket.LocalEndPoint 属性,因为它总是返回 0.0.0.0 (这是有道理的,因为它绑定到那个......).

I have an asynchronous UDP server class with a socket bound on IPAddress.Any, and I'd like to know which IPAddress the received packet was sent to (...or received on). It seems that I can't just use the Socket.LocalEndPoint property, as it always returns 0.0.0.0 (which makes sense as it's bound to that...).

以下是我目前正在使用的代码中有趣的部分:

Here are the interesting parts of the code I'm currently using:

private Socket udpSock;
private byte[] buffer;
public void Starter(){
    //Setup the socket and message buffer
    udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    udpSock.Bind(new IPEndPoint(IPAddress.Any, 12345));
    buffer = new byte[1024];

    //Start listening for a new message.
    EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
    udpSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, udpSock);
}

private void DoReceiveFrom(IAsyncResult iar){
    //Get the received message.
    Socket recvSock = (Socket)iar.AsyncState;
    EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
    int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP);
    byte[] localMsg = new byte[msgLen];
    Array.Copy(buffer, localMsg, msgLen);

    //Start listening for a new message.
    EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
    udpSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, udpSock);

    //Handle the received message
    Console.WriteLine("Recieved {0} bytes from {1}:{2} to {3}:{4}",
                      msgLen,
                      ((IPEndPoint)clientEP).Address,
                      ((IPEndPoint)clientEP).Port,
                      ((IPEndPoint)recvSock.LocalEndPoint).Address,
                      ((IPEndPoint)recvSock.LocalEndPoint).Port);
    //Do other, more interesting, things with the received message.
}

如前所述,这总是打印如下一行:

As mentioned earlier this always prints a line like:

Received 32 bytes from 127.0.0.1:1678 to 0.0.0.0:12345
Received 32 bytes from 127.0.0.1:1678 to 0.0.0.0:12345

我希望它是这样的:

Received 32 bytes from 127.0.0.1:1678 to 127.0.0.1:12345
Received 32 bytes from 127.0.0.1:1678 to 127.0.0.1:12345

提前感谢您对此的任何想法!--亚当

Thanks, in advance, for any ideas on this! --Adam

更新

好吧,我找到了一个解决方案,虽然我不喜欢它......基本上,我不是打开一个绑定到 IPAddress.Any 的单个 udp 套接字,而是为每个可用的 IPAddress 创建一个唯一的套接字.因此,新的 Starter 函数如下所示:

Well, I found a solution, though I don't like it... Basically, instead of opening a single udp socket bound to IPAddress.Any, I create a unique socket for every available IPAddress. So, the new Starter function looks like:

public void Starter(){
    buffer = new byte[1024];

    //create a new socket and start listening on the loopback address.
    Socket lSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    lSock.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);

    EndPoint ncEP = new IPEndPoint(IPAddress.Any, 0);
    lSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref ncEP, DoReceiveFrom, lSock);

    //create a new socket and start listening on each IPAddress in the Dns host.
    foreach(IPAddress addr in Dns.GetHostEntry(Dns.GetHostName()).AddressList){
        if(addr.AddressFamily != AddressFamily.InterNetwork) continue; //Skip all but IPv4 addresses.

        Socket s = new Socket(addr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
        s.Bind(new IPEndPoint(addr, 12345));

        EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
        s.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, s);
    }
}

这只是为了说明这个概念,这段代码的最大问题是每个套接字都试图使用相同的缓冲区......这通常是一个坏主意......

This is just to illustrate the concept, the biggest problem with this code as is, is that each socket is trying to use the same buffer... which is generally a bad idea...

必须有更好的解决方案;我的意思是,源和目标是 UDP 数据包头的一部分!哦,好吧,我想我会继续这样做,直到有更好的东西.

There has to be a better solution to this; I mean, the source and destination are part of the UDP packet header! Oh well, I guess I'll go with this until there's something better.

推荐答案

我也遇到了同样的问题.我没有看到一种方法,使用 ReceiveFrom 或其异步变体来检索接收到的数据包的目标地址.

I just had the same problem. I don't see a way, using ReceiveFrom or its async variants, to retrieve the destination address of a received packet.

但是...如果您使用 ReceiveMessageFrom 或其变体,您将获得一个 IPPacketInformation(参考 ReceiveMessageFromEndReceiveMessageFrom,或作为 SocketAsyncEventArgs 的属性传递给 ReceiveMessageFromAsync 中的回调).该对象将包含接收数据包的 IP 地址和接口号.

However...If you use ReceiveMessageFrom or its variants, you'll get an IPPacketInformation (by reference for ReceiveMessageFrom and EndReceiveMessageFrom, or as a property of the SocketAsyncEventArgs passed to your callback in ReceiveMessageFromAsync). That object will contain the IP address and interface number where the packet was received.

(请注意,此代码尚未经过测试,因为我使用的是 ReceiveMessageFromAsync 而不是假冒的 Begin/End 调用.)

(Note, this code has not been tested, as i used ReceiveMessageFromAsync rather than the fakey-fake Begin/End calls.)

private void ReceiveCallback(IAsyncResult iar)
{
    IPPacketInformation packetInfo;
    EndPoint remoteEnd = new IPEndPoint(IPAddress.Any, 0);
    SocketFlags flags = SocketFlags.None;
    Socket sock = (Socket) iar.AsyncState;

    int received = sock.EndReceiveMessageFrom(iar, ref flags, ref remoteEnd, out packetInfo);
    Console.WriteLine(
        "{0} bytes received from {1} to {2}",
        received,
        remoteEnd,
        packetInfo.Address
    );
}

注意,你显然应该调用 SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true) 作为设置套接字的一部分,在你之前 Bind ....ReceiveMessageFrom... 方法将为您设置它,但您可能只会在设置选项后 Windows 看到的任何数据包上看到有效信息.(在实践中,这不是什么大问题——但何时/如果它发生了,原因将是一个谜.最好完全阻止它.)

Note, you should apparently call SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true) as part of setting up the socket, before you Bind it. The ...ReceiveMessageFrom... methods will set it for you, but you'll probably only see valid info on any packets Windows saw after the option was set. (In practice, this isn't much of an issue -- but when/if it ever happened, the reason would be a mystery. Better to prevent it altogether.)

这篇关于C# UDP Socket:获取接收者地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:C# UDP Socket:获取接收者地址

基础教程推荐