UDP打孔实现UDP

2023-09-07 12:34:58 作者:醋溜仙女肉

我正在尝试完成 UDP 打孔.我的理论基于 这篇文章 和这篇 WIKI 页面,但我在 C# 编码方面遇到了一些问题.这是我的问题:

I am trying to accomplish UDP hole punching. I am basing my theory on this article and this WIKI page, but I am facing some issues with the C# coding of it. Here is my problem:

使用此处发布的代码 我现在能够连接到远程计算机并在同一端口上侦听传入连接(将 2 个 UDP 客户端绑定到同一端口).

Using the code that was posted here I am now able to connect to a remote machine and listen on the same port for incoming connections (Bind 2 UDP clients to the same port).

由于某种原因,同一个端口的两个绑定会阻止对方接收任何数据.我有一个响应我的连接的 UDP 服务器,所以如果我在将任何其他客户端绑定到端口之前先连接到它,我会得到它的响应.

For some reason the two bindings to the same port block each other from receiving any data. I have a UDP server that responds to my connection so if I connect to it first before binding any other client to the port I get its responses back.

如果我将另一个客户端绑定到该端口,则任何一个客户端都不会收到任何数据.

If I bind another client to the port no data will be received on either clients.

以下是显示我的问题的 2 段代码.第一个连接到远程服务器以在 NAT 设备上创建规则,然后在不同的线程上启动侦听器以捕获传入的数据包.然后代码将数据包发送到本地 IP,以便侦听器获取它.第二个仅将数据包发送到本地 IP 以确保其正常工作.我知道这不是真正的打孔,因为我将数据包发送给自己,而根本没有使用 NAT 设备.我现在遇到了一个问题,如果我使用 NAT 设备之外的计算机进行连接,我认为这不会有什么不同.

Following are 2 code pieces that show my problem. The first connects to a remote server to create the rule on the NAT device and then a listener is started on a different thread to capture the incoming packets. The code then sends packets to the local IP so that the listener will get it. The second only sends packets to the local IP to make sure this works. I know this is not the actual hole punching as I am sending the packets to myself without living the NAT device at all. I am facing a problem at this point, and I don't imagine this will be any different if I use a computer out side the NAT device to connect.

2012 年 2 月 4 日我尝试使用网络上的另一台计算机和 WireShark(数据包嗅探器)来测试侦听器.我看到从另一台计算机传入的数据包,但侦听器 UDP 客户端(udpServer)或发送方 UDP 客户端(客户端)没有收到.

2/4/2012 I tried using another computer on my network and WireShark (packet sniffer) to test the listener. I see the packets incoming from the other computer but are not received by the listener UDP client (udpServer) or the sender UDP client (client).

2010 年 2 月 5 日我现在添加了一个函数调用,以在初始发送和接收数据包后关闭第一个 UDP 客户端,仅在第二个 UDP 客户端上监听端口.这有效,我可以从该端口上的网络内部接收数据包.我现在将尝试从网络外部发送和接收数据包.我一有发现就会发布我的发现.

2/5/2010 I have now added a function call to close the first UDP client after the initial sending and receiving of packets only living the second UDP client to listen on the port. This works and I can receive packets from inside the network on that port. I will now try to send and receive packets from outside the network. I will post my findings as soon as I find something.

使用此代码我在监听客户端上获取数据:

Using this code I get data on the listening client:

static void Main(string[] args)
{
    IPEndPoint localpt = new IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545);

    ThreadPool.QueueUserWorkItem(delegate
    {
        UdpClient udpServer = new UdpClient();
        udpServer.ExclusiveAddressUse = false;
        udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpServer.Client.Bind(localpt);

        IPEndPoint inEndPoint = new IPEndPoint(IPAddress.Any, 0);
        Console.WriteLine("Listening on " + localpt + ".");
        byte[] buffer = udpServer.Receive(ref inEndPoint); //this line will block forever
        Console.WriteLine("Receive from " + inEndPoint + " " + Encoding.ASCII.GetString(buffer) + ".");
    });

    Thread.Sleep(1000);

    UdpClient udpServer2 = new UdpClient(6000);

    // the following lines work and the data is received
    udpServer2.Connect(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545);
    udpServer2.Send(new byte[] { 0x41 }, 1);

    Console.Read();
}

如果我使用下面的代码,在我的客户端和服务器之间的连接和数据传输之后,正在监听的 UDP 客户端将不会收到任何东西:

If I use the following code, after the connection and data transfer between my client and server, the listening UDP client will not receive anything:

static void Main(string[] args)
{
    IPEndPoint localpt = new IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545);

    //if the following lines up until serverConnect(); are removed all packets are received correctly
    client = new UdpClient();
    client.ExclusiveAddressUse = false;
    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    client.Client.Bind(localpt);
    remoteServerConnect(); //connection to remote server is done here
                           //response is received correctly and printed to the console

    ThreadPool.QueueUserWorkItem(delegate
    {
        UdpClient udpServer = new UdpClient();
        udpServer.ExclusiveAddressUse = false;
        udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpServer.Client.Bind(localpt);

        IPEndPoint inEndPoint = new IPEndPoint(IPAddress.Any, 0);
        Console.WriteLine("Listening on " + localpt + ".");
        byte[] buffer = udpServer.Receive(ref inEndPoint); //this line will block forever
        Console.WriteLine("Receive from " + inEndPoint + " " + Encoding.ASCII.GetString(buffer) + ".");
    });

    Thread.Sleep(1000);

    UdpClient udpServer2 = new UdpClient(6000);

    // I expected the following line to work and to receive this as well
    udpServer2.Connect(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545);
    udpServer2.Send(new byte[] { 0x41 }, 1);

    Console.Read();
}

推荐答案

如果我理解正确,您是在尝试使用中介服务器在不同 NAT 后面的 2 个客户端之间进行点对点通信?

If i understand correctly, you are trying to communicate peer-to-peer between 2 clients each behind a different NAT, using a mediation server for hole punching?

几年前我在c#中做过同样的事情,我还没有找到代码,但如果你愿意,我会给你一些指导:

Few years ago i did the exact same thing in c#, i haven't found the code yet, but ill give you some pointers if you like:

首先,我不会在 udpclient 上使用 Connect() 函数,因为 UDP 是一个无连接协议,这个函数真正做的只是隐藏 UDP 套接字的功能.

First, I wouldn't use the Connect() function on the udpclient, since UDP is a connectionless protocol, all this function really does is hide the functionality of a UDP socket.

您应该执行以下步骤:

在服务器上打开一个 UDP 套接字,其端口未被防火墙阻止,在特定端口(例如绑定此套接字到所选端口,例如 23000)在第一个客户端上创建一个 UDP 套接字,并在 23000 向服务器发送一些内容.不要绑定此套接字.当使用udp发送数据包时,windows会自动为socket分配一个空闲端口从其他客户端执行相同操作服务器现在已经从 2 个不同地址和 2 个不同端口的 2 个客户端接收到 2 个数据包.测试服务器是否可以在相同的地址和端口上发回数据包.(如果这不起作用,您做错了什么或您的 NAT 无法正常工作.如果您可以在不打开端口的情况下玩游戏,那么您知道它的工作原理:D)服务器现在应该将其他客户端的地址和端口发送给每个连接的客户端.客户端现在应该能够使用 UDP 将数据包发送到从服务器接收到的地址. Open a UDP socket on a server with it's ports not blocked by a firewall, at a specific port (eg Bind this socket to a chosen port for example 23000) Create a UDP socket on the first client, and send something to the server at 23000. Do not bind this socket. When a udp is used to send a packet, windows will automatically assign a free port to the socket Do the same from the other client The server has now received 2 packets from 2 clients at 2 different adresses with 2 different ports. Test if the server can send packets back on the same address and port. (If this doesn't work you did something wrong or your NAT isn't working. You know its working if you can play games without opening ports :D) The server should now send the address and port of the other clients to each connected client. A client should now be able to send packets using UDP to the adresses received from the server.

您应该注意,nat 上使用的端口可能与您的客户端 PC 上的端口不同!服务器应该将此外部端口分发给客户端.您必须使用外部地址和外部端口发送到!

You should note that the port used on the nat is probably not the same port as on your client pc!! The server should distribute this external port to clients. You must use the external adresses and the external ports to send to!

另请注意,您的 NAT 可能不支持这种端口转发.一些 NAT 将分配端口上的所有传入流量转发给您的客户端,这是您想要的.但是一些 nat 会对传入的数据包地址进行过滤,因此它可能会阻止其他客户端数据包.但在使用标准个人用户路由器时,这种情况不太可能发生.

Also note that your NAT might not support this kind of port forwarding. Some NAT's forward all incoming traffic on a assigned port to you client, which is what you want. But some nats do filtering on the incoming packets adresses so it might block the other clients packets. This is unlikely though when using a standard personal user router.