UDP 打孔 Java 示例



我想在具有静态IP的服务器的帮助下与两个客户端进行UDP打孔。服务器在端口 7070 和 7071 上等待两个客户端。之后,它将IP地址和端口相互发送。这部分工作正常。但我无法在两个客户之间建立通信。我在不同的 Wifi 网络和 3G 移动网络中尝试了代码。客户端程序引发 IO 异常"无到主机的路由"。客户端代码用于两个客户端。一次使用端口 7070 执行,一次使用 7071 执行。

你认为我已经正确实施了UDP打孔概念吗?有什么想法可以让它工作吗?首先是服务器代码,然后是客户端代码。

谢谢你的帮助。

服务器代码:

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();
}

客户端代码

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();
}

更新代码通常有效。我现在已经在两个不同的家庭网络中尝试过它,它正在工作。但它在我的3G或大学网络中不起作用。在 3G 中,我验证了 NAT 是否再次将两个端口(客户端端口和路由器分配的端口)映射在一起,即使在关闭并打开 clientSocket 之后也是如此。有没有人知道为什么它不起作用?

UDP打孔无法通过所有类型的NAT实现。没有为所有类型的 NAT 定义通用或可靠的方法。对称NAT甚至非常困难。

根据 NAT 行为,发送 UDP 数据包的不同设备的端口映射可能不同。例如,如果 A 向 B 发送 UDP 数据包,它可能会获得某个端口,例如 50000。但是,如果 A 向 C 发送 UDP 数据包,那么它可能会得到不同的映射,例如 50002。因此,在您的情况下,向服务器发送数据包可能会为客户端提供一些端口,但向其他客户端发送数据包可能会提供其他端口。

您将在此处阅读有关NAT行为的更多信息:

https://www.rfc-editor.org/rfc/rfc4787

https://www.rfc-editor.org/rfc/rfc5128

UDP 打孔在 3G 上没有通过

对于对称NAT(连接到不同移动网络的3G网络),您需要进行多UDP打孔。

看:

  1. https://drive.google.com/file/d/0B1IimJ20gG0SY2NvaE4wRVVMbG8/view?usp=sharing

  2. http://tools.ietf.org/id/draft-takeda-symmetric-nat-traversal-00.txt

  3. https://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf

    1. http://journals.sfu.ca/apan/index.php/apan/article/view/75/pdf_31

要么通过TURN服务器中继所有数据。

您正确地使用会合服务器根据UDP连接通知每个节点其他IP/端口。但是,使用公共 IP 和端口(即通过连接获得的组合)意味着在两个主机都存在于同一专用网络上的情况下,NAT 需要发夹转换,这有时不受支持。

要解决此问题,您可以将节点认为自己在消息中拥有的IP和端口发送到服务器(私有IP/端口),并将其包含在每个节点在另一个节点上接收的信息中。然后尝试在公共组合(您正在使用的组合)和我刚刚提到的组合上建立连接,只使用第一个成功建立的连接。

最新更新