¿Cómo puedo obtener socket connect () no bloqueantes?

Tengo un problema bastante simple aquí. Necesito comunicarme con muchos hosts simultáneamente, pero realmente no necesito ninguna sincronización porque cada solicitud es bastante autosuficiente.

Por eso, elegí trabajar con sockets asíncronos, en lugar de hilos de spam. Ahora tengo un pequeño problema:

Las cosas asíncronas funcionan como un encanto, pero cuando me conecto a 100 hosts y obtengo 100 tiempos de espera (timeout = 10 secs), espero 1000 segundos, solo para descubrir que todas mis conexiones han fallado.

¿Hay alguna forma de obtener también conexiones de socket no bloqueantes? Mi zócalo ya está configurado como No bloqueado, pero las llamadas a connect () siguen bloqueando.

Reducir el tiempo de espera no es una solución aceptable.

Estoy haciendo esto en Python, pero creo que el lenguaje de progtwigción no importa en este caso.

¿Realmente necesito usar hilos?

También debe paralelizar las conexiones, ya que los sockets se bloquean cuando se establece un tiempo de espera. Alternativamente, no podría establecer un tiempo de espera y utilizar el módulo de selección.

Puede hacer esto con la clase dispatcher en el módulo asyncore . Eche un vistazo al ejemplo del cliente http básico. Varias instancias de esa clase no se bloquearán entre sí en la conexión. Puede hacer esto con la misma facilidad mediante el uso de subprocesos, y creo que hace más fácil el seguimiento de los tiempos de espera de los zócalos, pero como ya está utilizando métodos asíncronos, también puede permanecer en la misma pista.

Como ejemplo, lo siguiente funciona en todos mis sistemas Linux.

import asyncore, socket class client(asyncore.dispatcher): def __init__(self, host): self.host = host asyncore.dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((host, 22)) def handle_connect(self): print 'Connected to', self.host def handle_close(self): self.close() def handle_write(self): self.send('') def handle_read(self): print ' ', self.recv(1024) clients = [] for i in range(50, 100): clients.append(client('cluster%d' % i)) asyncore.loop() 

Donde en cluster50 – cluster100, hay numerosas máquinas que no responden, o no existen. Esto comienza inmediatamente a imprimir:

 Connected to cluster50 SSH-2.0-OpenSSH_4.3 Connected to cluster51 SSH-2.0-OpenSSH_4.3 Connected to cluster52 SSH-2.0-OpenSSH_4.3 Connected to cluster60 SSH-2.0-OpenSSH_4.3 Connected to cluster61 SSH-2.0-OpenSSH_4.3 ... 

Sin embargo, esto no tiene en cuenta getaddrinfo, que tiene que bloquear. Si tiene problemas para resolver las consultas de DNS, todo tiene que esperar. Probablemente necesite reunir las consultas dns por separado y usar las direcciones IP en su ciclo asíncrono

Si desea un conjunto de herramientas más grande que asyncore, eche un vistazo a Twisted Matrix . Es un poco pesado para entrar, pero es el mejor kit de herramientas de progtwigción de red que puede obtener para Python.

Utilice el módulo de select . Esto le permite esperar a que finalice la E / S en varios sockets no bloqueadores. Aquí hay más información sobre la selección. Desde la página enlazada:

En C, la select encoding es bastante compleja. En Python, es un pedazo de pastel, pero es lo suficientemente cercano a la versión C que, si entiendes, seleccionar en Python, tendrás pocos problemas con él en C.

 ready_to_read, ready_to_write, in_error = select.select( potential_readers, potential_writers, potential_errs, timeout) 

select tres listas select : la primera contiene todos los sockets que deberías intentar leer; el segundo en todos los sockets en los que podría querer escribir, y el último (normalmente se deja vacío) en los que desea verificar si hay errores. Debe tener en cuenta que un socket puede ir a más de una lista. La llamada select está bloqueando, pero puede darle un tiempo de espera. Esto generalmente es una cosa sensata de hacer: concédale un tiempo de espera largo y agradable (digamos un minuto) a menos que tenga una buena razón para hacerlo de otra manera.

A cambio, obtendrá tres listas. Tienen los zócalos que son realmente legibles, grabables y erróneos. Cada una de estas listas es un subconjunto (posiblemente vacío) de la lista correspondiente que pasó. Y si coloca un socket en más de una lista de entrada, solo estará (como máximo) en una lista de salida.

Si un socket se encuentra en la lista legible de salida, puede estar tan cerca de estar seguro de que cada vez que entramos en este negocio que una recv en ese socket devolverá algo. La misma idea para la lista de escritura. Podrás send algo. Tal vez no todo lo que quieras, pero algo es mejor que nada. (En realidad, cualquier socket razonablemente saludable se volverá como escribible, solo significa que hay espacio disponible para el búfer de red saliente).

Si tiene un socket de “servidor”, póngalo en la lista de potenciales lectores. Si aparece en la lista legible, su aceptación funcionará (casi con seguridad). Si ha creado un nuevo socket para conectarse a otra persona, póngalo en la lista de potenciales escritores. Si aparece en la lista de escritura, tiene una probabilidad decente de que se haya conectado.

Desafortunadamente, no hay un código de ejemplo que muestre el error, por lo que es un poco difícil ver de dónde proviene este bloque.

Él hace algo como:

 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setblocking(0) s.connect(("www.nonexistingname.org", 80)) 

El módulo de socket usa getaddrinfo internamente, que es una operación de locking, especialmente cuando el nombre de host no existe. Un cliente dns compatible estándar esperará un tiempo para ver si el nombre realmente no existe o si hay solo algunos servidores dns lentos involucrados.

La solución es conectarse solo a direcciones ip o usar un cliente dns que permita solicitudes no bloqueadas, como pydns .

Uso torcido .

Es un motor de red asíncrono escrito en Python, compatible con numerosos protocolos, y puede agregar el suyo. Puede ser utilizado para desarrollar clientes y servidores. No bloquea en la conexión.

¿ Miraste el módulo asyncore ? Podría ser justo lo que necesitas.