Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UDP Hole Punching on Android; UDP Server

I'm currently trying to implement udp hole punching on Android for my udp server. Things should work like this:

  1. The client (behind a nat; maybe 3G,..) sends a DatagramPacket to the server (the server has a public ip; port is also known to be 45555). The client repeats to send a Datagram with a given delay
  2. Once the server received a Datagram, it sends Datagrams ("signals") back every 500ms.
  3. If hole punching worked, the client should receive those signals

Here is my current Client implementation (Android):

    //in onCreate()
    DatagramSocket socket = new DatagramSocket(46222);
    socket.setSoTimeout(2000);
    final Thread t = new Thread(new Runnable(){

        @Override
        public void run() {
            int delay = Integer.parseInt(e2.getText().toString());//e1 and e2 are EditTexts
            String ip = e1.getText().toString();
            try {
                DatagramPacket packet = new DatagramPacket(new byte[1],1, InetAddress.getByName(ip), 45555);
                while(!cleanUp){//cleanUp is set to true in onPause()
                    lock.lock(); //Lock lock = new ReentrantLock();
                    socket.send(packet);
                    lock.unlock();
                    Thread.sleep(delay);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                if(socket!=null)
                    socket.close();
            }
        }

    });
    final Thread t2 = new Thread(new Runnable(){

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                DatagramPacket packet = new DatagramPacket(new byte[1],1);
                while(!cleanUp){
                    lock.lock();
                    try{
                        socket.receive(packet);
                    }catch(SocketTimeoutException e){
                        lock.unlock();
                        Thread.sleep(15);
                        continue;
                    }
                    lock.unlock();
                    final String s = tv.getText().toString()+"signal\n";
                    MainActivity.this.runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            tv.setText(s);//tv is a TextView
                        }

                    });
                    Thread.sleep(10);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            finally{
                if(socket!=null)
                    socket.close();
            }
        }

    });
    //start both threads

Here is the server-side implementation (Java):

//int static void main(String[] args):
final Thread t = new Thread(new Runnable(){

        @Override
        public void run() {
            try {
                DatagramPacket packet = new DatagramPacket(new byte[1],1, addr, port);
                DatagramSocket socket = new DatagramSocket();
                System.out.println("send");
                while(true){
                    socket.send(packet);
                    Thread.sleep(500);
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    });
    final Thread t2 = new Thread(new Runnable(){

        @Override
        public void run() {
            try {
                DatagramPacket packet = new DatagramPacket(new byte[1],1);
                DatagramSocket socket = new DatagramSocket(45555);
                socket.receive(packet);
                addr = packet.getAddress(); //private static field InetAddress addr
                port = packet.getPort();
                System.out.println(addr+":"+ packet.getPort()); //field int port
                t.start();
                while(true){
                    socket.receive(packet);
                    System.out.println("idle");
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    });
    t2.start();

Everything works when client and server are in the same private network. To imitate the public server I run the server-side code on my computer and set up a port on my router (which has a public ip)*. The clients will send its packets to the public ip of the router. But in both cases ( my smartphone is connected to internet via my wlan network / 3G or E) no signals are received (the server receives the datagrams of the client)

So why does the hole punching process not work?

regards

*: The router will forward any udp packets sent to its port 45555 to my computer

like image 843
user2224350 Avatar asked Sep 04 '15 12:09

user2224350


1 Answers

Different sockets binds to different private port and use different public port of your NAT. You are receiving and sending through different sockets. Send with the same socket through which you received the data. Else Your sending socket is using different public port of your router to send data to your clients NAT. This packet your clients NAT discards because it came from same IP but unknown port.

like image 112
Tahlil Avatar answered Oct 13 '22 13:10

Tahlil