我有一个异步UDP服务器类,其中一个套接字绑定在IPAddress.Any上,我想知道收到的数据包被发送到哪个IP地址(...或接收到)。似乎我不能只使用Socket.LocalEndPoint属性,因为它总是返回0.0.0.0(这是有意义的,因为它绑定到...)。
以下是我目前正在使用的代码的有趣部分:
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.
}
如前所述,这总是打印一行如下:
从127.0.0.1:1678收到32个字节到0.0.0.0:12345
我希望它类似于:
从127.0.0.1:1678收到32个字节到127.0.0.1:12345
提前感谢您对此的任何想法! --Adam
更新
好吧,我找到了一个解决方案,虽然我不喜欢它...基本上,我没有打开绑定到IPAddress.Any的单个udp套接字,而是为每个可用的IPAddress创建一个唯一的套接字。因此,新的Starter功能如下所示:
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);
}
}
这只是为了说明这个概念,这个代码最大的问题就是每个套接字试图使用相同的缓冲区......这通常是一个坏主意......
必须有一个更好的解决方案;我的意思是,源和目标是UDP数据包头的一部分!哦,好吧,我想我会继续这样做,直到有更好的东西。
答案 0 :(得分:14)
我遇到了同样的问题。我没有看到使用ReceiveFrom
或其异步变体检索收到的数据包的目标地址的方法。
但是......如果您使用ReceiveMessageFrom
或其变体,则会获得IPPacketInformation
(ReceiveMessageFrom
和EndReceiveMessageFrom
的参考,或作为SocketAsyncEventArgs
传递给ReceiveMessageFromAsync
中的回调。该对象将包含接收数据包的IP地址和接口号。
(注意,此代码尚未经过测试,因为我使用了ReceiveMessageFromAsync
而不是伪造的假开始/结束调用。)
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看到的任何数据包的有效信息。 (在实践中,这不是一个问题 - 但是如果/如果它曾经发生过,原因将是一个谜。最好完全防止它。)
答案 1 :(得分:0)
我认为如果你绑定到127.0.0.1而不是IPAddress.Any你会得到你想要的行为。
0.0.0.0故意意味着“每个可用的IP地址”,并且它在字面上是由于您的绑定语句。
答案 2 :(得分:0)
从该套接字获取地址的一种方法是将其连接到发件人。 一旦你这样做,你将能够获得本地地址(或至少一个可路由到发送者),但是,你只能从连接的端点接收消息。
要取消连接,您需要再次使用连接,这次指定一个系列为AF_UNSPEC
的地址。不幸的是,我不知道如何在C#中实现这一目标。
(免责声明:我从来没有写过一行C#,这一般适用于Winsock)
答案 3 :(得分:0)
关于缓冲区问题,请尝试以下方法:
创建一个名为StateObject的类,用于存储您希望在回调中拥有的任何数据,并使用缓冲区,如果您需要,还包括套接字(因为我看到您当前正在将udpSock作为stateObject传递)。将新创建的对象传递给异步方法,然后您将在回调中访问它。
public void Starter(){
StateObject state = new StateObject();
//set any values in state you need here.
//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(state.buffer, 0, state.buffer.Length, SocketFlags.None, ref ncEP, DoReceiveFrom, state);
//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);
StateObject objState = new StateObject();
s.BeginReceiveFrom(objState.buffer, 0, objState.buffer.length, SocketFlags.None, ref newClientEP, DoReceiveFrom, objState);
}
}
在搜索这个问题时,我发现:
http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.beginreceivefrom.aspx
然后,您可以像正在使用udpSock和缓冲区一样从AsyncState转换StateObject,以及您需要的任何其他数据将存储在那里。
我想现在唯一的问题是如何以及在何处存储数据,但由于我不知道您的实现,我无法帮助那里。
答案 4 :(得分:-1)
亚当
这未经过测试......让我们试试
private void DoReceiveFrom(IAsyncResult iar){
//Get the received message.
Socket recvSock = (Socket)iar.AsyncState;
//EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
Socket clientEP = recvSock.EndAccept(iar);
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)recvSock.RemoteEndPoint).Address,
((IPEndPoint)recvSock.RemoteEndPoint).Port,
((IPEndPoint)recvSock.LocalEndPoint).Address,
((IPEndPoint)recvSock.LocalEndPoint).Port);
//Do other, more interesting, things with the received message.
*/
Console.WriteLine("Recieved {0} bytes from {1}:{2} to {3}",
msgLen,
((IPEndPoint)recvSock.RemoteEndPoint).Address,
((IPEndPoint)recvSock.RemoteEndPoint).Port,
clientEP.RemoteEP.ToString();
}