需要帮助设置用于 p2p 数据传输的 udp 打孔。无法让 STUN 工作

Posted

技术标签:

【中文标题】需要帮助设置用于 p2p 数据传输的 udp 打孔。无法让 STUN 工作【英文标题】:Need help setting udp hole punching for p2p data transfer. Can't get STUN to work 【发布时间】:2015-11-25 15:59:04 【问题描述】:

我正在尝试将 UDP 数据包从一个客户端发送到另一个客户端。让 C1 成为我用来接收数据的客户端,C2 成为将发送数据的客户端。我在做什么:

    向 STUN 服务器询问两个客户端的公共 IP 地址和端口。

    我通知每个客户端其他 IP 地址和公共端口。

    C1 在其公共地址和端口上向 C2 发送一个数据包,并开始侦听数据包。同时 C2 开始发送包含我要传输的数据的数据包。

C1 只是挂起接收一个永远不会到达的数据包。我是不是做错了 UDP 打孔?

我正在使用同一网络上的同一台计算机上的两个客户端对此进行测试,但服务器位于具有公共 IP 的另一个网络上的服务器上。我想说的是,在我测试时,两个客户端的公共地址是相同的,这是一个问题吗?

更新:

我现在知道我的路由器允许发夹。我在路由器上转发了 UDP 端口,并使用公共 IP 地址将数据包从一个客户端发送到另一个客户端。

这是我用来测试是否可以在 C# 中打孔的代码:

static UdpClient udpClient = new UdpClient(30001);
static IPAddress remoteIp;
static int remotePort;

static void Main(string[] args)

        // Send packet to server so it can find the client's public address and port 
        byte[] queryServer = Encoding.UTF8.GetBytes("something");
        udpClient.Send(testMsg, testMsg.Length, new IPEndPoint(IPAddress.Parse("199.254.173.154"), 30000));

        // Wait for a response from the server with the other client's public address and port
        IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);           
        byte[] dataJsonBytes = udpClient.Receive(ref sender);
        JObject json = JObject.Parse(Encoding.UTF8.GetString(dataJsonBytes));
        remoteIp = IPAddress.Parse(json["IP"].ToString());
        remotePort = Int32.Parse(json["port"].ToString());                                                    

        // Start spamming the other client with packets and waiting for response
        Thread sendingThread = new Thread(sendPacket);
        Thread receivingThread = new Thread(receivePacket);

        sendingThread.Start();
        receivingThread.Start();

        Console.ReadLine();
    

    private static void sendPacket()
    
        byte[] udpMessage = Encoding.UTF8.GetBytes("Forcing udp hole punching!!\n");
        while (true)
                       
            udpClient.Send(udpMessage, udpMessage.Length, new IPEndPoint(remoteIp, remotePort));
            Thread.Sleep(100);
        
    

    private static void receivePacket()
    
        IPEndPoint senderEp = new IPEndPoint(IPAddress.Any, 0);
        udpClient.Client.ReceiveTimeout = 500;
        while (true)
        
            try
            
                byte[] receivedData = udpClient.Receive(ref senderEp);
                Console.Write(Encoding.UTF8.GetString(receivedData));
            
            catch (Exception)
            
                Console.Write("Timeout!!\n");
            
        
    

更新 2:

在@Tahlil 的帮助下,我发现用于检查 NAT 类型的算法存在缺陷,我试图在对称 NAT 后面进行打孔。所以问题是没有注意到我的代码有错误......我建议所有面临同样问题的人确定地断言 NAT 类型。

感谢大家的帮助。

【问题讨论】:

【参考方案1】:

当然 C2 会挂起。因为 C2 NAT 很可能在 C2 发送数据包之前拒绝了 C1 发送的原始数据包。在所有这些中构建一些重试逻辑来克服这些问题。

另外,如果 C1 和 C2 在同一个子网上,那么您的 NAT 可能只是不允许发夹。 “发夹”是通过您的外部地址在内部发送的能力。并非所有 NAT 都支持这一点。准备好尝试连接内部候选地址以及 STUN 发现的外部地址。

无论如何,从我之前给出的答案开始。

https://***.com/a/8524609/104458

【讨论】:

感谢您的 awnser selbie,我的描述有误,即使它已经发送了第一个数据包,C1 也会挂起(对不起,我编辑了它)。我以前读过你的遮阳篷,事实上它帮助我理解了这一切是如何运作的;)。我还使用 tcp 调解了所有事情,以确保事情以所需的顺序发生。我将尝试在不同的网络中设置两台计算机,看看是否是因为 NAT 不允许发夹。再次感谢您。 TCP nat 遍历的难度是 UDP NAT 遍历的两倍。在 TCP 中,您可能需要让两个端点同时通过重试逻辑相互连接。 仍然不行...我知道在我的路由器上可以进行发夹,因为如果我进行端口转发并使用它们通过的公共地址将数据包从一个客户端发送到另一个客户端。我在java中编写了一些代码,将每个客户端的公共端口和地址发送给另一个。在我的路由器中,我转发了一个端口来访问在 java 代码中打开的套接字。我将用一些代码更新我的帖子。可以的话请看一下。【参考方案2】:

某些 NAT 不允许来自未知来源的数据包。这就是为什么我们在 NAT 中打孔,以便 NAT 能够识别未知 IP 并为其创建映射。

如果 C1 和 C2 在不同的网络中。然后,如果 C2 向 C1 的公共 IP:port 发送了一个数据包,那么 C2 的 NAT 将为 C1 的公共 IP:Port 创建一个映射。之后,C2 的公共 IP:Port 中来自 C1 的任何数据都将被转发到 C2。

但我认为在您的情况下,数据包不会离开 NAT,因为它们都在同一个网络下。这就是 NAT 不创建任何映射的原因。所以你的打孔失败了。

因此,当您从 C1 向 C2 的公共 IP 地址发送数据时,您假设到那时 C2 已经为 C1 的地址打了一个洞,并且 NAT 已经创建了一个映射。但是C2的打孔失败了,因为C1在同一个网络中。

如果您在同一个网络中,为什么需要打孔?这是一个奇怪的意图,因为 C1 和 C2 可以直接向彼此的私有 IP 发送数据包并建立连接。 C1和C2在不同网络时需要打孔。

编辑:

如果两个客户端的 NAT 组合类似于 Symmetric vs Symmetric,则打孔将不起作用。所以首先检查一下两端的NAT类型是什么。

不要用数据包向另一端发送垃圾邮件以打孔。如果你用数据包淹没它,NAT 可以阻止你的 IP。从一侧打孔并使用低 TTL 值。这样用于打孔的数据包离开 NAT 但不会到达另一个 NAT。这样打孔将成功完成,并且其他侧 NAT 不会被淹没。从另一边尝试稍微晚一点发送数据包,以便从另一边完成打孔。当打孔侧收到第一个数据包时,停止打孔并开始正常发送数据包。

【讨论】:

感谢您的 awnser Tahlil。这旨在与不同 NAT 后面的客户端一起使用,但我正在使用同一网络上的计算机对其进行测试,因为这就是我所拥有的。我将根据您告诉我的内容进行一些测试(我正在尝试在不同的网络中设置两台计算机),然后我会发布结果。 请记住,C2 需要在 C1 的打孔完成后开始发送数据包。否则 C1 将不会收到任何东西,因为 C2 将被 NAT 阻止。 好吧,我实际上不知道......这可能是导致数据包被丢弃的另一件事。谢谢 所以我在另一个 NAT 后面使用了第二台计算机,但仍然不行......我发布了我用来尝试 udp 打孔的代码。如果可以的话,请看看...我真的不明白我可能做错了什么。再次感谢您的遮阳篷 :) 我知道两个 NAT 都是完整的锥形。我会用你在编辑中给我的建议再试一次。感谢您的帮助

以上是关于需要帮助设置用于 p2p 数据传输的 udp 打孔。无法让 STUN 工作的主要内容,如果未能解决你的问题,请参考以下文章

P2P 应用程序,打孔不适用于与端点无关的映射 NAT

UDP打孔混乱

UDP 打孔 - 无法到达目的地

P2P技术比较

每个 NAT 下的 P2P 网络

P2P技术比较