Perforación de agujeros UDP. Haga que el servidor hable con el cliente

He estado leyendo mucho sobre cómo implementar la perforación de agujeros UDP, pero por alguna razón no puedo hacer que funcione.

Para aquellos que no están familiarizados con lo que está aquí la perforación udp hole es mi propia definición:

El objetivo es poder transferir datos entre dos clientes (Cliente A y Cliente B) con la ayuda de un servidor. Así que el cliente A se conecta al servidor y envía su información. El cliente B hace lo mismo. El servidor tiene la información necesaria para que el Cliente A pueda enviar datos al Cliente B y viceversa. Por lo tanto el servidor da esa información a ambos clientes. Una vez que ambos clientes tienen esa información, es posible comenzar a enviar y recibir datos entre esos clientes sin la ayuda del servidor.

Mi objetivo es poder hacer lo que acabo de describir (perforación de orificios de udp).Antes de hacerlo, creo que será útil poder conectarse desde el servidor al cliente. Para hacerlo, planeo enviar al servidor la información sobre el cliente. Una vez que el servidor recibe esa información, intente conectarse al cliente desde cero. Una vez que sea capaz de realizar eso, debería tener todo lo que necesito para comenzar a implementar el verdadero taladrado de udp.

Así es como tengo las cosas preparadas:

El enrutador superior tiene el servidor y el enrutador inferior conectados a los puertos LAN. El enrutador inferior (NAT) está conectado al enrutador superior a través de su puerto WAN. Y la computadora cliente está conectada al enrutador inferior a uno de sus puertos LAN.

Entonces, en esa conexión, el cliente puede ver el servidor, pero el servidor no puede ver al cliente.

Así que el algoritmo que he hecho en pseudo código es:

El cliente se conecta al servidor.El cliente envía algunos paquetes UDP al servidor para abrir algunos puertos en el NATEnvíe información al servidor sobre qué puertos está escuchando el cliente.Una vez que el servidor recibe esa información, intente conectarse al cliente desde cero.Aquí está la implementación en código:

Servidor:

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);
}

Cliente:

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);
} 

Por alguna razón funcionó algunas veces! Funciona cuando el cliente recibe datos correctamente en la línea:sending_socket.Receive(buffer);

Cosas a tener en cuenta: Si en el servidor en la segunda parte usé la variable de instancialistner en lugar de crear la nueva variablesendSocket y envíe los bytes a través de esa variable para que el cliente pueda recibir los datos que se envían. Recuerde que la segunda parte del servidor será implementada por un segundo cliente B, por eso estoy inicializando las variables nuevamente desde cero ...

Editar:

Aquí hay una forma diferente de ver el mismo problema. Cuando inicializo un nuevo objeto en lugar de usar el mismo objeto, el cliente no recibe la respuesta.

Tengo un objeto de tipo UdpClient. Puedo enviar datos con ese objeto al otro compañero. Si creo otro objeto del mismo tipo con las mismas propiedades e intento enviar datos, ¡no funciona! Puede que me falte para inicializar algunas variables. Soy capaz de establecer variables privadas con reflexión, por lo que no debería tener un problema. De todos modos aquí está el código del servidor:

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;
}

el código del cliente básicamente envía datos al servidor y luego espera una respuesta:

    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 :(

tenga en cuenta que el cliente está detrás de un enrutador (NAT), de lo contrario no tendré este problema. La razón por la que me gustaría copiar udpClient es para poder enviar esa variable a otra computadora y permitir que la otra computadora envíe datos al cliente.

Así que mi pregunta es por que es el objeto originallistener capaz de enviar datos peronewClient no es capaz de El cliente sigue esperando en línea.sending_socket.Receive(buffer); Incluso después de que el servidor ejecuta la línea:newClient.Send(dataToSend, dataToSend.Length);. el cliente recibe datos con éxito cuando el oyente envía los datos pero no newClient. ¿Por qué ocurre esto si ambas variables tienen la misma IP y puerto de destino? ¿Cómo difieren las variables?

Nota: si el servidor y el cliente están en la misma red, la copia funciona y la variablenewClient Es capaz de enviar datos al cliente. Para simular este problema, el cliente debe estar detrás de un NAT (enrutador). Un ejemplo de dicha red puede consistir en dos enrutadores. Llamémoslos enrutador X y enrutador Y. También necesita una llamada al servidor que S. y un cliente C. para que S pueda conectarse a uno de los puertos LAN de X. C se pueda conectar a uno de los puertos LAN de Y. Finalmente, conecte el puerto WAN de Y a uno de los puertos LAN de X.

Respuestas a la pregunta(4)

Su respuesta a la pregunta