2011-09-07 22 views
9

Estoy usando una secuencia de comandos Python para enviar paquetes UDP a un servidor que registrará mi secuencia de comandos para recibir notificaciones del servidor. El protocolo requiere que envíe mi propia dirección IP y el puerto en el que deseo recibir estas notificaciones, por lo que esta debería ser una dirección accesible desde la red del servidor.Python: obtenga la dirección IP local utilizada para enviar datos IP a una dirección IP remota específica

Las soluciones deben al menos funcionar en Windows XP y superior y preferiblemente Mac OS X, ya que esta es mi plataforma de desarrollo.

El primer paso es obtener la dirección IP de cualquiera de mis interfaces. Actualmente estoy usando el enfoque comúnmente sugerido para resolver el nombre de host propio de la máquina:

def get_local_address(): 
    return socket.gethostbyname(socket.gethostname()) 

Esto solo funciona a veces. Actualmente devuelve 172.16.249.1, que es la dirección de una interfaz virtual utilizada por VM Ware, por lo que este es un problema.

Mucho mejor sería una forma de obtener la dirección IP de la interfaz predeterminada, que debería ser la correcta la mayor parte del tiempo.

Aún mejor sería una forma de obtener la dirección IP real utilizada para conectarse al servidor mediante un protocolo orientado a la conexión como TCP. De hecho, me puedo conseguir esa dirección, pero no sin intentar abrir la conexión:

def get_address_to_connect_to(server_addr): 
    non_open_port = 50000 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

    try: 
     s.connect((server_addr, non_open_port)) 
    except socket.error: 
     pass 
    else: 
     s.close() # just in case 

    return s.getsockname()[0] 

Esto bloqueará hasta que un tiempo de espera de TCP se ha alcanzado en el caso de que el servidor es inalcanzable o un cortafuegos mal en el medio es el bloqueo de ICMP paquetes ¿Hay una mejor manera de obtener esa información?

Respuesta

11

Tuve que resolver el mismo problema una vez, y pasé un tiempo considerable tratando de encontrar una mejor manera, sin éxito. También comencé con gethostname, pero descubrí, como lo hiciste, que no siempre devuelve el resultado correcto, y que incluso puede arrojar una excepción en los casos en que hay un problema con el archivo de hosts.

La única solución que pude encontrar que funciona de manera confiable en todas las plataformas es probar la conexión, como lo está haciendo. Una mejora en su código es usar UDP en lugar de TCP, es decir, SOCK_DGRAM en lugar de SOCK_STREAM. Eso evita el tiempo de espera de la conexión y la función se completa inmediatamente.

Si está implementando este código en ubicaciones de clientes que podrían estar ejecutando firewalls, asegúrese de documentar el hecho de que lo hace. De lo contrario, pueden ver una advertencia de firewall para una conexión saliente al puerto 50000, no comprender su propósito, y considerarlo un error.

Editar: aquí es más o menos el código que utilicé:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

try: 
    s.connect((host, 9)) 
    client = s.getsockname()[0] 
except socket.error: 
    client = "Unknown IP" 
finally: 
    del s 
return client 

Creo que está recibiendo 0.0.0.0 porque estás cerrando la toma de corriente antes de llamar getsockname. Otro consejo es mi uso del puerto 9; es el puerto de descarte del UDP RFC863, y por lo tanto un poco menos extraño que algún puerto aleatorio de alto número.

+0

¡Ah, dulce empatía! Con 'SOCK_DGRAM', ¡sería casi perfecto! Pero 's.getsockname()' returns '('0.0.0.0', 51770)' en mi caso. He intentado 's = socket.socket (socket.AF_INET, socket.SOCK_DGRAM); s.sendto (b'hello ', (' 1.2.3.4 ', 50000)); s.getsockname() [0] '. ¡Sería fantástico si añadieras tu código a tu respuesta! – Feuermurmel

+0

Ver mis ediciones a la respuesta. – DNS

+0

Eso funciona perfectamente; Incluso usa el [Discard Protocol] (http://en.wikipedia.org/wiki/Discard_Protocol) ¡No lo sabía! Reemplacé el 'del s' con una declaración' con'. – Feuermurmel

0

¿El lado emisor necesita saber su dirección IP?

¿El cliente no puede simplemente escuchar los datos que intenta suscribir (en todas las interfaces) después de enviar el UDP 'registrarse' y hacer que el servidor simplemente comience a enviar los datos a la dirección desde la que se originó el datagrama?

+0

¡Herejía! Ese servicio podría hacer muchas cosas para hacerme la vida más fácil. Pero el mismo problema aparece en la implementación de SIP que también es parte del proyecto.Tiene que enviar su propia dirección IP en el encabezado 'Contact' de una solicitud' REGISTER'. – Feuermurmel