我一直在阅读很多关于如何实现 UDP 打孔的内容,但由于某种原因,我无法使其工作。
对于那些不熟悉什么是 udp 打孔的人,这是我自己的定义:
目标是能够在服务器的帮助下在两个客户端(客户端 A 和客户端 B)之间传输数据。所以客户端 A 连接到服务器并发送它的信息。客户 B 也是如此。服务器具有必要的信息,以便客户端 A 能够向客户端 B 发送数据,反之亦然。因此,服务器将该信息提供给两个客户端。一旦两个客户端都获得了关于彼此的信息,就可以在没有服务器帮助的情况下开始在这些客户端之间发送和接收数据。
我的目标是能够做我刚才描述的事情(udp 打孔)。在这样做之前,我认为能够从服务器连接到客户端会很有帮助。为此,我计划向服务器发送有关客户端的信息。一旦服务器收到该信息,就会尝试从头开始连接到客户端。一旦我能够执行,我应该拥有开始实施真正的 udp 打孔所需的一切。
以下是我的设置方式:
顶部路由器将服务器和底部路由器连接到 LAN 端口。底部路由器 (NAT) 通过其 WAN 端口连接到顶部路由器。客户端计算机连接到底部路由器的 LAN 端口之一。
因此,在该连接中,客户端能够看到服务器,但服务器无法看到客户端。
所以我用伪代码做的算法是:
- 客户端连接到服务器。
- 客户端向服务器发送一些 UDP 包,以便在 NAT 上打开一些端口
- 向服务器发送有关客户端正在侦听的端口的信息。
- 一旦服务器收到该信息,就会尝试从头开始连接到客户端。
这是代码中的实现:
服务器:
static void Main()
{
/* Part 1 receive data from client */
UdpClient listener = new UdpClient(11000);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 11000);
string received_data;
byte[] receive_byte_array = listener.Receive(ref groupEP);
received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
// get info
var ip = groupEP.Address.ToString();
var port = groupEP.Port;
/* Part 2 atempt to connect to client from scratch */
// now atempt to send data to client from scratch once we have the info
Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint endPointClient = new IPEndPoint(IPAddress.Parse(ip), port);
sendSocket.SendTo(Encoding.ASCII.GetBytes("Hello"), endPointClient);
}
客户:
static void Main(string[] args)
{
/* Part 1 send info to server */
Socket sending_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress send_to_address = IPAddress.Parse("192.168.0.132");
IPEndPoint sending_end_point = new IPEndPoint(send_to_address, 11000);
sending_socket.SendTo(Encoding.ASCII.GetBytes("Test"), sending_end_point);
// get info
var port = sending_socket.LocalEndPoint.ToString().Split(':')[1];
/* Part 2 receive data from server */
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, int.Parse(port));
byte[] buffer = new byte[1024];
sending_socket.Receive(buffer);
}
由于某种原因,它工作了几次!当客户端成功在线接收数据时起作用:sending_socket.Receive(buffer);
注意事项:
如果在第二部分的服务器上我使用实例变量listner
而不是创建新变量sendSocket
并通过该变量发送字节,则客户端能够接收正在发送的数据。请记住,服务器的第二部分将由第二个客户端 B 实现,这就是我再次从头开始初始化变量的原因......
编辑:
这是看待同一问题的不同方式。当我初始化一个新对象而不是使用同一个对象时,客户端不会收到响应。
我有一个 UdpClient 类型的对象。我能够将该对象的数据发送给其他对等方。如果我创建另一个具有相同属性的相同类型的对象并尝试发送数据它不起作用!我可能缺少初始化一些变量。我可以通过反射设置私有变量,所以我应该没有问题。无论如何,这里是服务器代码:
public static void Main()
{
// wait for client to send data
UdpClient listener = new UdpClient(11000);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 11000);
byte[] receive_byte_array = listener.Receive(ref groupEP);
// connect so that we are able to send data back
listener.Connect(groupEP);
byte[] dataToSend = new byte[] { 1, 2, 3, 4, 5 };
// now let's atempt to reply back
// this part does not work!
UdpClient newClient = CopyUdpClient(listener, groupEP);
newClient.Send(dataToSend, dataToSend.Length);
// this part works!
listener.Send(dataToSend, dataToSend.Length);
}
static UdpClient CopyUdpClient(UdpClient client, IPEndPoint groupEP)
{
var ip = groupEP.Address.ToString();
var port = groupEP.Port;
var newUdpClient = new UdpClient(ip, port);
return newUdpClient;
}
客户端代码基本上将数据发送到服务器,然后等待响应:
string ipOfServer = "192.168.0.132";
int portServerIsListeningOn = 11000;
// send data to server
Socket sending_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress send_to_address = IPAddress.Parse(ipOfServer);
IPEndPoint sending_end_point = new IPEndPoint(send_to_address, portServerIsListeningOn);
sending_socket.SendTo(Encoding.ASCII.GetBytes("Test"), sending_end_point);
// get info
var port = sending_socket.LocalEndPoint.ToString().Split(':')[1];
// now wait for server to send data back
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, int.Parse(port));
byte[] buffer = new byte[1024];
sending_socket.Receive(buffer); // <----- keeps waiting in here :(
请注意,客户端位于路由器(NAT)后面,否则我不会遇到这个问题。我想要复制 udpClient 的原因是我可以将该变量发送到另一台计算机,从而使另一台计算机能够将数据发送到客户端。
所以我的问题是 为什么原始对象listener
能够发送数据但newClient
不能?sending_socket.Receive(buffer);
即使在服务器执行 line: 之后,客户端仍然在排队等待newClient.Send(dataToSend, dataToSend.Length);
。当侦听器发送数据而不是newClient时,客户端成功接收数据。如果两个变量具有相同的目标 IP 和端口,为什么会这样?变量有何不同?
注意:如果服务器和客户端在同一个网络上,则复制工作并且变量newClient
能够将数据发送到客户端。要模拟此问题,客户端必须位于 NAT(路由器)之后。这种网络的一个例子可能包括两个路由器。我们称它们为路由器 X 和路由器 Y。您还需要一个称为 S 的服务器和一个客户端 C。因此 S 可以连接到 X 的 LAN 端口之一。C 可以连接到 Y 的 LAN 端口之一。最后将 Y 的 WAN 端口连接到 X 的 LAN 端口之一。