2008-08-27 20 views
9
  1. Tiene múltiples adaptadores de red.
  2. Enlace un socket UDP a un puerto local, sin especificar una dirección.
  3. Recibe paquetes en uno de los adaptadores.

¿Cómo se obtiene la dirección IP local del adaptador que recibió el paquete?Cómo obtener su propia dirección IP (local) desde un socket udp (C/C++)

La pregunta es, "¿Cuál es la dirección IP del adaptador del receptor?" no la dirección del remitente que obtenemos en el

receive_from(..., &senderAddr, ...); 

llamada.

Respuesta

3

Puede enumerar todos los adaptadores de red, obtener sus direcciones IP y comparar la parte cubierta por la máscara de subred con la dirección del remitente.

igual:

IPAddress FindLocalIPAddressOfIncomingPacket(senderAddr) 
{ 
    foreach(adapter in EnumAllNetworkAdapters()) 
    { 
     adapterSubnet = adapter.subnetmask & adapter.ipaddress; 
     senderSubnet = adapter.subnetmask & senderAddr; 
     if(adapterSubnet == senderSubnet) 
     { 
      return adapter.ipaddress; 
     } 
    } 
} 
+0

Solo si el paquete se originó en la subred del adaptador. No si fue enrutado para llegar allí ... –

+0

Sí, Len tiene razón. Si el paquete llegó a través de NAT en su enrutador local, verá su IP local (algo así como 10.0.0.2) –

+0

¿No es cierto que las transmisiones no se enrutan (solo se envían localmente)? IIRC el método debería funcionar así. –

-1
 
ssize_t 
    recvfrom(int socket, void *restrict buffer, size_t length, int flags, 
     struct sockaddr *restrict address, socklen_t *restrict address_len); 

    ssize_t 
    recvmsg(int socket, struct msghdr *message, int flags); 

[..] 
    If address is not a null pointer and the socket is not connection-oriented, the 
    source address of the message is filled in. 

código real:

int nbytes = recvfrom(sock, buf, MAXBUFSIZE, MSG_WAITALL, (struct sockaddr *)&bindaddr, &addrlen);

fprintf(stdout, "Read %d bytes on local address %s\n", nbytes, inet_ntoa(bindaddr.sin_addr.s_addr));

esperanza esto ayuda.

+1

la dirección de origen es la dirección remota –

3

día G,

supongo que usted ha hecho su enlazar mediante INADDR_ANY para especificar la dirección.

Si este es el caso, la semántica de INADDR_ANY es tal que se crea un socket UDP en el puerto especificado en todas sus interfaces. El socket va a enviar todos los paquetes a todas las interfaces en el puerto especificado.

Al enviar utilizando este conector, se utiliza la interfaz con el número más bajo. El campo de dirección del remitente saliente se establece en la dirección IP de la primera interfaz de salida utilizada.

La primera interfaz de salida se define como la secuencia cuando se realiza un ifconfig -a. Probablemente será eth0.

HTH.

alegrías, Rob

3

La solución proporcionada por timbo supone que los intervalos de direcciones son únicas y no superpuesta. Si bien este suele ser el caso, no es una solución genérica.

Existe una excelente implementación de una función que hace exactamente lo que está buscando proporcionado en el libro de Steven "Programación de red Unix" (sección 20.2) Esta es una función basada en recvmsg(), en lugar de recvfrom() . Si su socket tiene habilitada la opción IP_RECVIF, entonces recvmsg() devolverá el índice de la interfaz en la que se recibió el paquete. Esto se puede usar para buscar la dirección de destino.

El código fuente está disponible here.La función en cuestión es 'recvfrom_flags()'

+0

Voy a investigar eso. Tal vez esa es una mejor solución. – Christopher

-1

Prueba esto:

gethostbyname("localhost"); 
+0

Esto solo obtendrá lo que localhost está configurado para resolver como (que debería ser 127.0.0.1) ... no el extremo receptor del paquete UDP. – Urkle

0

Desafortunadamente las llamadas a la API sendto y recvfrom se dividen fundamentalmente cuando se utiliza con los sockets a "Cualquier IP" porque no tienen campo para información local de IP.

Entonces, ¿qué puedes hacer al respecto?

  1. Puede adivinar (por ejemplo, en función de la tabla de enrutamiento).
  2. Puede obtener una lista de direcciones locales y enlazar un socket separado a cada dirección local.
  3. Puede utilizar API más nuevas que admitan esta información. Hay dos partes para esto, en primer lugar debe usar la opción relavent socket (ip_recvif para IPv4, ipv6_recvif para IPv6) para indicarle a la pila que desea esta información. Luego debe usar una función diferente (recvmsg en Linux y varios otros sistemas unix, WSArecvmsg en Windows) para recibir el paquete.

Ninguna de estas opciones es excelente. Adivinar obviamente producirá respuestas equivocadas a la vez. La vinculación de sockets separados aumenta la complejidad de su software y causa problemas si la lista de direcciones locales cambia su programa se está ejecutando. Las API más nuevas son la solución técnica correcta, pero pueden reducir la portabilidad (en particular, parece que WSArecvmsg no está disponible en Windows XP) y pueden requerir modificaciones en la biblioteca de contenedor que está utilizando.

Editar parece que estaba equivocado, parece que la documentación de MS es engañosa y que WSArecvmsg está disponible en Windows XP. Consulte https://stackoverflow.com/a/37334943/5083516

Cuestiones relacionadas