UDP дырокол. Поговорите с клиентом на сервере

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

For those that are not familiar of what udp hole punching is here is my own definition:

Цель состоит в том, чтобы иметь возможность передавать данные между двумя клиентами (клиент A и клиент B) с помощью сервера. Таким образом, клиент А подключается к серверу и отправляет свою информацию. Клиент Б делает то же самое. Сервер имеет необходимую информацию, чтобы клиент А мог отправлять данные клиенту Б и наоборот. Поэтому сервер предоставляет эту информацию обоим клиентам. Как только оба клиента имеют эту информацию друг о друге, можно начать отправку и получение данных между этими клиентами без помощи сервера.

Моя цель - быть в состоянии сделать то, что я только что описал (пробивание дырок в udp).Before doing so I think it will be helpful to be able to connect from the server to the client, Для этого я планирую отправить на сервер информацию о клиенте. Как только сервер получит эту информацию, попытайтесь подключиться к клиенту с нуля. Как только я смогу выполнить это, у меня должно быть все, что мне нужно, чтобы начать осуществлять настоящую пробивку дырок в udp.

Вот как у меня все настроено:

enter image description here

Верхний маршрутизатор имеет сервер, а нижний маршрутизатор подключен к портам локальной сети. Нижний маршрутизатор (NAT) соединен с верхним маршрутизатором через его WAN-порт. И клиентский компьютер подключен к нижнему маршрутизатору к одному из его портов LAN.

Таким образом, в этом соединении клиент может видеть сервер, но сервер не может видеть клиент.

Итак, алгоритм, который я сделал в псевдокоде:

Client connects to server. Client send some UDP packages to the server in order to open some ports on the NAT Send information to the server on what ports the client is listening to. Once the server receives that info attempt to connect to the client from scratch. Here is the implementation in code:

Server:

static void Main()
{     
    /* Part 1 receive data from client */
    UdpClient listener = new UdpClient(11000);
    IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 11000);
    string received_data;
    byte[] receive_byte_array = listener.Receive(ref groupEP);       
    received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);

    // get info
    var ip = groupEP.Address.ToString();
    var port = groupEP.Port;

    /* Part 2 atempt to connect to client from scratch */
    // now atempt to send data to client from scratch once we have the info       
    Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    IPEndPoint endPointClient = new IPEndPoint(IPAddress.Parse(ip), port);
    sendSocket.SendTo(Encoding.ASCII.GetBytes("Hello"), endPointClient);
}

Client:

static void Main(string[] args)
{
    /* Part 1 send info to server */
    Socket sending_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,  ProtocolType.Udp);
    IPAddress send_to_address = IPAddress.Parse("192.168.0.132");
    IPEndPoint sending_end_point = new IPEndPoint(send_to_address, 11000);
    sending_socket.SendTo(Encoding.ASCII.GetBytes("Test"), sending_end_point);

    // get info
    var port = sending_socket.LocalEndPoint.ToString().Split(':')[1];

    /* Part 2 receive data from server */
    IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, int.Parse(port));
    byte[] buffer = new byte[1024];
    sending_socket.Receive(buffer);
} 

For some reason it worked a few times! Это работает, когда клиент успешно получает данные на линии:sending_socket.Receive(buffer);

Things to note: Если на сервере во второй части я использовал переменную экземпляраlistner вместо создания новой переменнойsendSocket и отправлять байты через эту переменную, клиент может получить отправляемые данные. Помните, что вторая часть сервера будет реализована вторым клиентом B, поэтому я заново инициализирую переменные с нуля ...

Edit:

Here is a different way of looking at the same problem. Когда я инициализирую новый объект вместо того, чтобы использовать тот же объект, клиент не получает ответ.

У меня есть объект типа UdpClient. Я могу отправить данные с этим объектом на другой узел. Если я создаю другой объект того же типа с теми же свойствами и пытаюсь отправить данные, он не работает! Я мог бы отсутствовать, чтобы инициализировать некоторые переменные. Я могу установить частные переменные с отражением, поэтому у меня не должно быть проблем. В любом случае вот код сервера:

public static void Main()
{
    // wait for client to send data
    UdpClient listener = new UdpClient(11000);
    IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 11000);        
    byte[] receive_byte_array = listener.Receive(ref groupEP);

    // connect so that we are able to send data back
    listener.Connect(groupEP);

    byte[] dataToSend = new byte[] { 1, 2, 3, 4, 5 };

    // now let's atempt to reply back

    // this part does not work!
    UdpClient newClient = CopyUdpClient(listener, groupEP);
    newClient.Send(dataToSend, dataToSend.Length);

    // this part works!
    listener.Send(dataToSend, dataToSend.Length);
}

static UdpClient CopyUdpClient(UdpClient client, IPEndPoint groupEP)
{
    var ip = groupEP.Address.ToString();
    var port = groupEP.Port;
    var newUdpClient = new UdpClient(ip, port);
    return newUdpClient;
}

код клиента в основном отправляет данные на сервер, а затем ожидает ответа:

    string ipOfServer = "192.168.0.132";
    int portServerIsListeningOn = 11000;

    // send data to server
    Socket sending_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    IPAddress send_to_address = IPAddress.Parse(ipOfServer);
    IPEndPoint sending_end_point = new IPEndPoint(send_to_address, portServerIsListeningOn);
    sending_socket.SendTo(Encoding.ASCII.GetBytes("Test"), sending_end_point);

    // get info
    var port = sending_socket.LocalEndPoint.ToString().Split(':')[1];

    // now wait for server to send data back
    IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, int.Parse(port));
    byte[] buffer = new byte[1024];
    sending_socket.Receive(buffer); // <----- keeps waiting in here :(

note that the client is behind a router (NAT) otherwise I will not have this problem. Причина, по которой я хотел бы скопировать udpClient, заключается в том, что я могу отправить эту переменную на другой компьютер, позволяя другому компьютеру отправлять данные клиенту.

So my question is  почему оригинальный объектlistener возможность отправлять данные, ноnewClient не умеет? Клиент продолжает ждать в очередиsending_socket.Receive(buffer); даже после того, как сервер выполнит строку:newClient.Send(dataToSend, dataToSend.Length);, клиент успешно получает данные, когда слушатель отправляет данные, но не newClient. Почему, если обе переменные имеют одинаковый IP-адрес и порт назначения? как переменные отличаются?

Замечания: Если сервер и клиент находятся в одной сети, то копия работает и переменнаяnewClient умеет отправлять данные клиенту. Чтобы смоделировать эту проблему, клиент должен находиться за NAT (маршрутизатором). Пример такой сети может состоять из двух маршрутизаторов. давайте назовем их маршрутизатором X и маршрутизатором Y. Вам также нужен вызов сервера, который S. и клиент C. так что S можно подключить к одному из портов LAN X. C можно подключить к одному из портов LAN Y. Наконец, подключите порт WAN Y к одному из портов LAN X.

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

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