servidor programar por example enviar datos comunicar como cliente aplicaciones aplicacion c# sockets networking udp hole-punching

c# - programar - Perforación de agujero UDP. Haga que el servidor hable con el cliente



servidor udp c# (4)

Parece que puede conectarse con el servidor por primera vez. Después de una conexión exitosa, debe cerrar y desconectar la conexión cada vez. Consulte este código de ejemplo http://codeidol.com/csharp/csharp-network/Connectionless-Sockets/A -Simple-UDP-Application /

He estado leyendo mucho sobre cómo implementar la perforación UDP, pero por alguna razón no puedo hacerlo funcionar.

Para aquellos que no están familiarizados con lo que es perforar udp aquí, es mi propia definición:

El objetivo es poder transferir datos entre dos clientes (Cliente A y B) con la ayuda de un servidor. Entonces, 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 brinda esa información a ambos clientes. Una vez que ambos clientes tienen esa información sobre el otro, 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 (udp hole punching). Antes de hacerlo, creo que será útil poder conectarse desde el servidor al cliente . Para hacerlo, tengo previsto enviar al servidor la información sobre el cliente. Una vez que el servidor recibe esa información intenta 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 agujero udp.

Así es como tengo las cosas configuradas:

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

Entonces 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 NAT
  • Envíe información al servidor sobre qué puertos está escuchando el cliente.
  • Una vez que el servidor recibe esa información intenta 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 con éxito en la línea: sending_socket.Receive(buffer);

Aspectos a tener en cuenta: si en el servidor de la segunda parte utilicé el listner de listner instancias en lugar de crear la nueva variable sendSocket y enviar los bytes a través de esa variable, el cliente puede recibir los datos que se envían. Recuerde que la segunda parte del servidor va a ser implementada por un segundo cliente B, por eso estoy inicializando variables 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. Puedo establecer variables privadas con reflexión, así 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, lo que permite que la otra computadora envíe datos al cliente.

Entonces mi pregunta es por qué el listener objetos original puede enviar datos, pero newClient no puede? El cliente sigue esperando en la línea sending_socket.Receive(buffer); incluso después de que el servidor ejecute la línea: newClient.Send(dataToSend, dataToSend.Length); . el cliente recibe con éxito los datos cuando el oyente envía los datos pero no el cliente nuevo. ¿Por qué es esto si ambas variables tienen el mismo IP y puerto de destino? ¿Cómo difieren las variables?

Nota: Si el servidor y el cliente están en la misma red, entonces la copia funciona y la variable newClient puede 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. vamos a llamarlos enrutador X y enrutador Y. También necesita una llamada de Servidor que S. y un cliente C. para que S pueda conectarse a uno de los puertos LAN de X. C se puede conectar a uno de los puertos LAN de Y. Finalmente, conecte el puerto WAN de Y a uno de los puertos LAN de X.


¿Ha considerado utilizar UPnP en el cliente para configurar NAT transversal para permitir paquetes entrantes en un puerto en particular? El cliente solo necesitaría comunicar el IP y el puerto de entrada al servidor y esperar a que el servidor envíe paquetes. http://en.wikipedia.org/wiki/Universal_Plug_and_Play


¡Finalmente encontré la respuesta! Aquí está la implementación con solo un cliente y un servidor. Mi próximo intento será usar 3 computadoras. de todos modos espero que esto ayude:

Código del servidor:

class Program { static byte[] dataToSend = new byte[] { 1, 2, 3, 4, 5 }; // get the ip and port number where the client will be listening on static IPEndPoint GetClientInfo() { // wait for client to send data using (UdpClient listener = new UdpClient(11000)) { IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 11000); byte[] receive_byte_array = listener.Receive(ref groupEP); return groupEP; } } static void Main(string[] args) { var info = GetClientInfo(); // get client info /* NOW THAT WE HAVE THE INFO FROM THE CLIENT WE ARE GONG TO SEND DATA TO IT FROM SCRATCH!. NOTE THE CLIENT IS BEHIND A NAT AND WE WILL STILL BE ABLE TO SEND PACKAGES TO IT */ // create a new client. this client will be created on a // different computer when I do readl udp punch holing UdpClient newClient = ConstructUdpClient(info); // send data newClient.Send(dataToSend, dataToSend.Length); } // Construct a socket with the info received from the client static UdpClient ConstructUdpClient(IPEndPoint clientInfo) { var ip = clientInfo.Address.ToString(); var port = clientInfo.Port; // this is the part I was missing!!!! // the local end point must match. this should be the ip this computer is listening on // and also the port UdpClient client = new UdpClient(new IPEndPoint( IPAddress.Any, 11000)); // lastly we are missing to set the end points. (ip and port client is listening on) // the connect method sets the remote endpoints client.Connect(ip, port); return client; } }

codigo del cliente:

string ipOfServer = "192.168.0.139"; 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); // <----- we can receive data now!!!!!


Hmm, creo que estás confundiendo varias cosas aquí. Por un lado, realmente se llama perforación UDP . Déjame intentar explicar cómo debería funcionar esto.

Los enrutadores NAT suelen realizar asignaciones de puertos cuando reenvían paquetes desde la red privada interna a Internet externa.

Digamos que creó un socket UDP en una máquina detrás de NAT, y envió un datagrama a un IP / puerto externo. Cuando el paquete IP que transporta ese datagrama abandona la máquina de envío, su encabezado IP tiene el campo de dirección de origen establecido en la dirección IP privada local no enrutable globalmente (como 192.168.1.15 ) y el encabezado UDP tiene el campo de puerto de origen establecido en cualquier puerto fue asignado al socket (ya sea explícitamente a través del enlace, o implícitamente elegido por el sistema operativo desde los puertos efímeros ). Llamaré a este número de puerto fuente P1 .

Luego, cuando el enrutador NAT envía ese paquete a la red externa, sobrescribe la dirección IP de origen a su propia dirección IP externa (de lo contrario, no hay manera de enrutar los paquetes), y a menudo sobrescribe el puerto UDP de origen a algún otro valor (tal vez porque otro host en la red privada usa el mismo puerto de origen, lo que crea ambigüedad). La correlación entre el puerto de origen original y el nuevo número de puerto (etiquetémoslo como P2 ) se conserva en el enrutador para que coincida con los paquetes de devolución. Esta asignación también puede ser específica de la dirección IP de destino y el puerto UDP de destino .

Entonces ahora ha "perforado un agujero" en el enrutador: los paquetes UDP devueltos al enrutador al puerto P2 se reenvían a la máquina interna en el puerto UDP P1 . De nuevo, dependiendo de la implementación de NAT, esto podría restringirse solo a los paquetes de la dirección IP objetivo original y el puerto UDP objetivo.

Para la comunicación de cliente a cliente, tendrá que indicar el puerto / IP externo de uno a otro a través del servidor, con la esperanza de que el enrutador NAT asigne los mismos puertos de origen interno a los mismos puertos de origen externos. Entonces los clientes se enviarán paquetes el uno al otro usando esos.

Espero que esto ayude.