简体   繁体   English

UDP 打孔 Java 示例

[英]UDP Hole Punching Java Example

I want to do UDP Hole Punching with two clients with the help of a server with a static IP.我想在具有静态 IP 的服务器的帮助下对两个客户端进行 UDP 打孔。 The server waits for the two clients on port 7070 and 7071. After that it sends the IP address and port to each other.服务器在端口 7070 和 7071 上等待两个客户端。之后它会相互发送 IP 地址和端口。 This part is working fine.这部分工作正常。 But I'm not able to establish a communication between the two clients.但是我无法在两个客户端之间建立通信。 I tried the code in different Wifi networks and in 3G mobile network.我在不同的 Wifi 网络和 3G 移动网络中尝试了代码。 The client program throws the IO-Exception "No route to host".客户端程序抛出 IO 异常“No route to host”。 The client code is used for both clients.客户端代码用于两个客户端。 Once executed with port 7070 and once with 7071.一次在端口 7070 上执行,一次在 7071 上执行。

Do you think I've implemented the UDP hole punching concept correctly?你认为我已经正确地实现了 UDP 打孔概念吗? Any ideas to make it work?有什么想法可以让它发挥作用吗? Here's the server code first, followed by the client code.首先是服务器代码,然后是客户端代码。

Thank you for help.谢谢你的帮助。

Code of server:服务器代码:

public class UDPHolePunchingServer {

    public static void main(String args[]) throws Exception {

    // Waiting for Connection of Client1 on Port 7070
    // ////////////////////////////////////////////////

    // open serverSocket on Port 7070
    DatagramSocket serverSocket1 = new DatagramSocket(7070);

    System.out.println("Waiting for Client 1 on Port "
            + serverSocket1.getLocalPort());

    // receive Data
    DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
    serverSocket1.receive(receivePacket);

    // Get IP-Address and Port of Client1
    InetAddress IPAddress1 = receivePacket.getAddress();
    int port1 = receivePacket.getPort();
    String msgInfoOfClient1 = IPAddress1 + "-" + port1 + "-";

    System.out.println("Client1: " + msgInfoOfClient1);

    // Waiting for Connection of Client2 on Port 7071
    // ////////////////////////////////////////////////

    // open serverSocket on Port 7071
    DatagramSocket serverSocket2 = new DatagramSocket(7071);

    System.out.println("Waiting for Client 2 on Port "
            + serverSocket2.getLocalPort());

    // receive Data
    receivePacket = new DatagramPacket(new byte[1024], 1024);
    serverSocket2.receive(receivePacket);

    // GetIP-Address and Port of Client1
    InetAddress IPAddress2 = receivePacket.getAddress();
    int port2 = receivePacket.getPort();
    String msgInfoOfClient2 = IPAddress2 + "-" + port2 + "-";

    System.out.println("Client2:" + msgInfoOfClient2);

    // Send the Information to the other Client
    // /////////////////////////////////////////////////

    // Send Information of Client2 to Client1
    serverSocket1.send(new DatagramPacket(msgInfoOfClient2.getBytes(),
            msgInfoOfClient2.getBytes().length, IPAddress1, port1));

    // Send Infos of Client1 to Client2
    serverSocket2.send(new DatagramPacket(msgInfoOfClient1.getBytes(),
            msgInfoOfClient1.getBytes().length, IPAddress2, port2));

    //close Sockets
    serverSocket1.close();
    serverSocket2.close();
}

Code of client客户代码

public class UDPHolePunchingClient {

    public static void main(String[] args) throws Exception {
    // prepare Socket
    DatagramSocket clientSocket = new DatagramSocket();

    // prepare Data
    byte[] sendData = "Hello".getBytes();

    // send Data to Server with fix IP (X.X.X.X)
    // Client1 uses port 7070, Client2 uses port 7071
    DatagramPacket sendPacket = new DatagramPacket(sendData,
            sendData.length, InetAddress.getByName("X.X.X.X"), 7070);
    clientSocket.send(sendPacket);

    // receive Data ==> Format:"<IP of other Client>-<Port of other Client>"
    DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
    clientSocket.receive(receivePacket);

    // Convert Response to IP and Port
    String response = new String(receivePacket.getData());
    String[] splitResponse = response.split("-");
    InetAddress ip = InetAddress.getByName(splitResponse[0].substring(1));

    int port = Integer.parseInt(splitResponse[1]);

    // output converted Data for check
    System.out.println("IP: " + ip + " PORT: " + port);

    // close socket and open new socket with SAME localport
    int localPort = clientSocket.getLocalPort();
    clientSocket.close();
    clientSocket = new DatagramSocket(localPort);

    // set Timeout for receiving Data
    clientSocket.setSoTimeout(1000);

    // send 5000 Messages for testing
    for (int i = 0; i < 5000; i++) {

        // send Message to other client
        sendData = ("Datapacket(" + i + ")").getBytes();
        sendPacket = new DatagramPacket(sendData, sendData.length, ip, port);
        clientSocket.send(sendPacket);

        // receive Message from other client
        try {
            receivePacket.setData(new byte[1024]);
            clientSocket.receive(receivePacket);
            System.out.println("REC: "
                    + new String(receivePacket.getData()));

        } catch (Exception e) {
            System.out.println("SERVER TIMED OUT");
        }
    }

    // close connection
    clientSocket.close();
}

UPDATE The code is generally working. UPDATE代码通常可以正常工作。 I've tried it in two different home networks now and it's working.我现在已经在两个不同的家庭网络中进行了尝试,并且可以正常工作。 But it isn't working in my 3G or university network.但它在我的 3G 或大学网络中不起作用。 In 3G, I verified that the NAT is mapping the two ports (the client port and by the router assigned port) together again, even after closing and opening the clientSocket.在 3G 中,我验证了 NAT 再次将两个端口(客户端端口和路由器分配的端口)映射在一起,即使在关闭和打开 clientSocket 之后也是如此。 Has anyone an idea why it isn't working then?有谁知道为什么它不起作用?

UDP hole punching can't be achieved with all types of NAT.所有类型的 NAT 都无法实现 UDP 打孔。 There is no universal or reliable way defined for all types of NAT.没有为所有类型的 NAT 定义通用或可靠的方法。 It is even very difficult for symmetric NAT.对称 NAT 甚至是非常困难的。

Depending on the NAT behaviour, the port mapping could be different for different devices sending the UDP packets.根据 NAT 行为,发送 UDP 数据包的不同设备的端口映射可能不同。 Like, If A sends a UDP packet to B, it may get some port like 50000. But if A sends a UDP packet to C, then it may get a different mapping like 50002. So, in your case sending a packet to server may give a client some port but sending a packet to other client may give some other port.比如,如果 A 向 B 发送一个 UDP 数据包,它可能会得到一些像 50000 这样的端口。但是如果 A 向 C 发送一个 UDP 数据包,那么它可能会得到一个不同的映射,比如 50002。所以,在你的情况下,向服务器发送一个数据包可能给一个客户端一些端口,但向其他客户端发送数据包可能会给一些其他端口。

You shall read more about NAT behaviour here:您应在此处阅读有关 NAT 行为的更多信息:

http://tools.ietf.org/html/rfc4787 http://tools.ietf.org/html/rfc4787

https://tools.ietf.org/html/rfc5128 https://tools.ietf.org/html/rfc5128

UDP hole punching not going through on 3G UDP 打孔未通过 3G

You rightly use a rendezvous server to inform each node of the others IP / port based on the UDP connection.您正确地使用集合服务器根据 UDP 连接通知每个节点其他 IP/端口。 However using the public IP and port which is the combination which will is obtained by the connection as you have, means that in scenarios where both hosts exist on the same private network hairpin translation is required by the NAT which is sometimes not supported.但是,使用公共 IP 和端口(这是通过您拥有的连接获得的组合)意味着在两个主机都存在于同一专用网络上的情况下,NAT 需要有时不支持的发夹转换。

To remedy this you can send the IP and port your node believes itself to have in the message to the server (private ip / port) and include this in the information each node receives on the other.为了解决这个问题,您可以将您的节点认为自己在消息中拥有的 IP 和端口发送到服务器(私有 IP/端口),并将其包含在每个节点接收到的信息中。 Then attempt a connection on both the public combination (the one you are using) and the one I just mentioned and just use the first one which is successfully established.然后尝试在公共组合(您正在使用的组合)和我刚刚提到的组合上建立连接,并仅使用成功建立的第一个组合。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM