UDP穿透Java示例

18

我想通过一个带有静态 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之后也是如此。有人知道为什么它不起作用吗?

3个回答

5

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

3G网络下UDP打洞无法成功



0

你正确地使用了约会服务器来基于UDP连接通知每个节点其他节点的IP/端口。然而,使用公共IP和端口(这是通过连接获得的组合)意味着在两个主机存在于同一私有网络的情况下,NAT需要进行头发针转换,而这有时不被支持。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接