UDP Holepunching позади NAT

Я пытаюсь реализовать простой эскиз UDP-Holepunching в Java, чтобы проверить его концепцию и позже использовать его в моем приложении C / C ++.

Концепция:

Как из Википедии, я понял концепцию так: пусть A и B будут клиентами за неопределенной сетевой структурой, а C - общедоступным общедоступным сервером.

A отправляет пакет на сервер C, сервер сохраняет свой IP-адрес и порт. C получит публичный IP-адрес NAT. Делая это, NAT перед A создаст маршрут, который будет передавать все пакеты на этом порту A.B делает то же самое, что и A, отправляя пакет на сервер C, который затем сохраняет свой адрес и порт, NAT B создает маршрут и так далее.На этом этапе C знает как адрес, так и порт каждого клиента. C отправит адрес и порт B в A и из A в B.A отправляет пакет B, который будет отклонен NAT B, но это откроет «дыру» в NAT A, пропуская дальнейшие пакеты из B.B отправляет пакет в A, который достигнет A, поскольку «дыра» ранее была «пробита». Это также откроет «дыру» в NAT B, пропуская дальнейшие пакеты от A.Теперь дырокол сделан, и А и В должны иметь возможность общаться друг с другом. P2P

Все это хорошо работает на локальном хосте (что не является большим сюрпризом), но на примере реального мира это терпит неудачу.

Проблема:

A и B могут подключаться к серверу C, который получает их пакеты, сохраняет их адрес и порт и передает их другому клиенту. Но на данный момент это не удается. А и Б не могут общаться друг с другом. Поэтому я спрашиваю себя, где я поступил неправильно. Я потратил несколько дней на поиск рабочих примеров в google и stackoverflow, но все, на что я наткнулся, это предложение использовать STUN, а это не то, чего я хочу.

Реализация:

Ниже я опубликую свой набросок на Java, так как я не знаю, есть ли у меня проблема с моей концепцией или моей реализацией.

Это код сервера:
public class Server
{
    public static void main(String[] args)
    {
        int port1 = 0, port2 = 0;
        String address1 = null, address2;
        byte[] bytes = new byte[1024];
        try
        {
            System.out.println("Server waiting");
            DatagramSocket ds = new DatagramSocket(789);
            while(!Thread.interrupted())
            {
                DatagramPacket p = new DatagramPacket(bytes, bytes.length);
                ds.receive(p);
                if(port1 == 0)
                {
                    port1 = p.getPort();
                    address1 = p.getAddress().getHostAddress();
                    System.out.println("(1st) Server received:" + new String(bytes) + " from " + address1 + " on port " + port1);
                }
                else
                {
                    port2 = p.getPort();
                    address2 = p.getAddress().getHostAddress();
                    System.out.println("(2nd) Server received:" + new String(bytes) + " from " + address1 + " on port " + port1);
                    sendConnDataTo(address1, port1, address2, port2, ds);
                    sendConnDataTo(address2, port2, address1, port1, ds);
                }
            }
            ds.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    public static void sendConnDataTo(String a1, int p1, String a2, int p2, DatagramSocket ds)
    {
        byte[] bA, bP;
        bA = a1.getBytes();
        bP = Integer.toString(p1).getBytes();
        DatagramPacket pck;
        try
        {
            pck = new DatagramPacket(bA, bA.length, InetAddress.getByName(a2), p2);
            ds.send(pck);
            pck = new DatagramPacket(bP, bP.length, InetAddress.getByName(a2), p2);
            ds.send(pck);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

Обратите внимание, что это всего лишь эскиз, а не реальное приложение. Сервер должен принимать пакеты только от двух клиентов, сохранять их адрес и порт и передавать их другому клиенту.

Это код клиента:
public class Client
{
    private DatagramSocket socket;
    private int init = 0;
    private String target;
    private int port;

    public Client()
    {
        try
        {
            socket = new DatagramSocket();
        }
        catch(SocketException e)
        {
            e.printStackTrace();
        }
        Thread in = new Thread()
        {
            public void run()
            {
                while(true)
                {
                    byte[] bytes = new byte[1024];
                    DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
                    try
                    {
                        socket.receive(packet);
                        bytes = Arrays.copyOfRange(bytes, 0, packet.getLength());
                        String s = new String(bytes);
                        System.out.println("Received: " + s);
                        if(init == 0)
                        {
                            target = s;
                            System.out.println("Target: " + target);
                            init++;
                        }
                        else if(init == 1)
                        {
                            port = Integer.parseInt(s);
                            System.out.println("Port: " + port);
                            init++;
                        }
                        else System.out.println(new String(bytes));
                    }
                    catch(IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        };
        in.start();
        connectToSupervisor();
    }

    private void connectToSupervisor()
    {
        byte[] bytes = new byte[1024];
        System.out.println("Greeting server");
        System.arraycopy("EHLO".getBytes(), 0, bytes, 0, 4);
        try
        {
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 789);
            socket.send(packet);
            System.out.println("Greetings sent...");
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        send();
    }

    private void send()
    {
        while(init != 2)
        {
            try
            {
                Thread.sleep(20L);
            }
            catch(InterruptedException e)
            {
                e.printStackTrace();
            }
        }
        System.out.println("Init completed!");
        while(true)
        {
            byte[] b2 = "Hello".getBytes();
            byte[] b1 = new byte[6];
            System.arraycopy(b2, 0, b1, 0, b2.length);
            try
            {
                DatagramPacket packet = new DatagramPacket(b1, b1.length, InetAddress.getByName(target), port);
                socket.send(packet);
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args)
    {
        new Client();
    }
}

Клиент просто отправит пакет на сервер, прослушает пакеты от него, получит данные о соединении от другого клиента и затем будет непрерывно отправлять пакеты, содержащие «Hello».

Я прошу прощения за длинный код, но я хотел, чтобы он был полным.

Я был бы рад, если бы кто-нибудь из вас мог указать мне на ошибки, которые я делаю, объяснить мне, почему это не работает, дать мне рабочий пример или, по крайней мере, указать мне альтернативу.

Ответы на вопрос(3)

Ваш ответ на вопрос