UDP Hole Punching Java Example

18.4k views Asked by At

I want to do UDP Hole Punching with two clients with the help of a server with a static IP. The server waits for the two clients on port 7070 and 7071. After that it sends the IP address and port to each other. 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. The client program throws the IO-Exception "No route to host". The client code is used for both clients. Once executed with port 7070 and once with 7071.

Do you think I've implemented the UDP hole punching concept correctly? 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. 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. 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. Has anyone an idea why it isn't working then?

3

There are 3 answers

0
Kunjan Thadani On

UDP hole punching can't be achieved with all types of NAT. There is no universal or reliable way defined for all types of NAT. It is even very difficult for symmetric NAT.

Depending on the NAT behaviour, the port mapping could be different for different devices sending the UDP packets. 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.

You shall read more about NAT behaviour here:

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

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

UDP hole punching not going through on 3G

0
AudioBubble On
0
nrmad On

You rightly use a rendezvous server to inform each node of the others IP / port based on the UDP connection. 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.

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. 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.