# `asyncore` — controlador de socket asincrónico

Código fuente: Lib/asyncore.py

Obsoleto desde la versión 3.6: `asyncore` will be removed in Python 3.12 (see PEP 594 for details). Please use `asyncio` instead.

Nota

Este módulo solo existe para compatibilidad con versiones anteriores. Para el nuevo código recomendamos usar `asyncio`.

Este módulo proporciona la infraestructura básica para escribir servicio de socket asincrónicos, clientes y servidores.

Sólo hay dos maneras de que un programa en un solo procesador haga «más de una cosa a la vez». La programación multiproceso es la forma más sencilla y popular de hacerlo, pero hay otra técnica muy diferente, que le permite tener casi todas las ventajas de multiproceso, sin usar realmente varios subprocesos. Es realmente sólo práctico si su programa está en gran parte limitado por el I/O. Si el programa está limitado por el procesador, los subprocesos programados preventivos son probablemente lo que realmente necesita. Sin embargo, los servidores de red rara vez están limitado al procesador.

Si su sistema operativo es compatible con la llamada del sistema `select()` en su biblioteca de I/O (y casi todos lo son), puede usarla para hacer malabares con varios canales de comunicación a la vez; haciendo otro trabajo mientras su I/O está teniendo lugar en el «fondo». Aunque esta estrategia puede parecer extraña y compleja, especialmente al principio, es en muchos sentidos más fácil de entender y controlar que la programación multiproceso. El módulo `asyncore` resuelve muchos de los problemas difíciles para usted, haciendo que la tarea de construir sofisticados servidores de red de alto rendimiento y clientes sea fácil. Para aplicaciones y protocolos «conversacionales», el módulo complementario `asynchat` es invaluable.

La idea básica detrás de ambos módulos es crear uno o más canales de red, instancias de clase `asyncore.dispatcher` y `asynchat.async_chat`. La creación de los canales los agrega a un mapa global, utilizado por la función `loop()` si no lo proporciona con su propio map.

Una vez creados los canales iniciales, llamar a la función `loop()` activa el servicio de canal, que continúa hasta que se cierra el último canal (incluido el que se ha agregado al mapa durante el servicio asincrónico).

`asyncore.``loop`([timeout[, use_poll[, map[, count]]]])

  Ingresa un bucle de sondeo que termina después de que se hayan cerrado los pases de conteo o todos los canales abiertos. Todos los argumentos son opcionales. El parámetro count tiene como valor predeterminado `None`, lo que da como resultado que el bucle termine solo cuando se hayan cerrado todos los canales. El argumento timeout establece el parámetro de tiempo de espera para la llamada adecuada a `select()` o `poll()`, medida en segundos; el valor predeterminado es 30 segundos. El parámetro use_poll, si es true, indica que `poll()` debe utilizarse en lugar de `select()` (el valor predeterminado es `False`).

  El parámetro map es un diccionario cuyos elementos son los canales a observar. A medida que se cierran los canales, se eliminan del mapa. Si se omite map, se utiliza un mapa global. Los canales (instancias de `asyncore.dispatcher`, `asynchat.async_chat` y subclases de los mismos) se pueden mezclar libremente en el mapa.

class `asyncore.``dispatcher`

  La clase `dispatcher` es un contenedor fino alrededor de un objeto de socket de bajo nivel. Para hacerlo más útil, tiene algunos métodos para el control de eventos que se llaman desde el bucle asincrónico. De lo contrario, se puede tratar como un objeto de socket normal sin bloqueo.

  La activación de eventos de bajo nivel en determinados momentos o en determinados estados de conexión indica al bucle asincrónico que se han producido ciertos eventos de nivel superior. Por ejemplo, si hemos pedido un socket para conectarse a otro host, sabemos que la conexión se ha realizado cuando el socket se vuelve grabable por primera vez (en este punto sabe que puede escribir a él con la expectativa de éxito). Los eventos de nivel superior implícitos son:

  Evento | Descripción
  --- | ---
  `handle_connect()` | Implícito en el primer proceso de lectura o escritura
  `handle_close()` | Implícito en un evento de lectura sin datos disponibles
  `handle_accepted()` | Implícito en un evento de lectura en un socket de escucha

  Durante el procesamiento asincrónico, se utilizan los métodos `readable()` y `writable()` de cada canal asignado para determinar si el socket del canal deberían ser agregados a la lista de canales `seleccionados` o `encuestados` para eventos de lectura y escritura.

  Por lo tanto, el conjunto de eventos de canal es mayor que los eventos básicos del socket. El conjunto completo de métodos que se pueden invalidar en la subclase es el siguiente:

  `handle_read`()

    Se llama cuando el bucle asincrónico detecta que una llamada `read()` en el socket del canal tendrá éxito.

  `handle_write`()

    Se llama cuando el bucle asincrónico detecta que se puede escribir un socket grabable. A menudo, este método implementará el almacenamiento en búfer necesario para el rendimiento. Por ejemplo:

        def handle_write(self):
            sent = self.send(self.buffer)
            self.buffer = self.buffer[sent:]

  `handle_expt`()

    Se llama cuando hay datos fuera de banda (OOB) para una conexión de socket. Esto casi nunca sucederá, ya que OOB es tenuemente compatible y rara vez se utiliza.

  `handle_connect`()

    Se llama cuando el socket del abridor activo realmente hace una conexión. Puede enviar un banner de «bienvenido» o iniciar una negociación de protocolo con el punto de conexión remoto, por ejemplo.

  `handle_close`()

    Se llama cuando el socket está cerrado.

  `handle_error`()

    Se llama cuando se genera una excepción y no se controla de otro modo. La versión predeterminada imprime un traceback condensado.

  `handle_accept`()

    Se llama en los canales de escucha (abridores pasivos) cuando se puede establecer una conexión con un nuevo punto de conexión remoto que ha emitido una llamada `connect()` para el punto de conexión local. En desuso en la versión 3.2; use `handle_accepted()` en su lugar.

    Obsoleto desde la versión 3.2.

  `handle_accepted`(sock, addr)

    Se llama en los canales de escucha (abridores pasivos) cuando se ha establecido una conexión con un nuevo punto de conexión remoto que ha emitido una llamada `connect()` para el punto de conexión local. sock es un objeto de socket new utilizable para enviar y recibir datos en la conexión, y addr es la dirección enlazada al socket en el otro extremo de la conexión.

    Nuevo en la versión 3.2.

  `readable`()

    Se llama en cada momento alrededor del bucle asincrónico para determinar si se debe agregar el socket de un canal a la lista en la que se pueden producir eventos de lectura. El método predeterminado simplemente retorna `True`, lo que indica que, de forma predeterminada, todos los canales estarán interesados en eventos de lectura.

  `writable`()

    Se llama cada vez alrededor del bucle asincrónico para determinar si se debe agregar el socket de un canal a la lista en la que se pueden producir eventos de escritura. El método predeterminado simplemente retorna `True`, lo que indica que, de forma predeterminada, todos los canales estarán interesados en eventos de escritura.

  Además, cada canal delega o extiende muchos de los métodos de socket. La mayoría de estos son casi idénticos a sus socios de socket.

  `create_socket`(family=socket.AF_INET, type=socket.SOCK_STREAM)

    Esto es idéntico a la creación de un socket normal y usará las mismas opciones para la creación. Consulte la documentación `socket` para obtener información sobre la creación de sockets.

    Distinto en la versión 3.3: Los argumentos family y type se pueden omitir.

  `connect`(address)

    Al igual que con el objeto de socket normal, address es una tupla con el primer elemento al que se va a conectar el host y el segundo el número de puerto.

  `send`(data)

    Envía data al punto final remoto del socket.

  `recv`(buffer_size)

    Lee como máximo los bytes buffer_size desde el punto final remoto del socket. Un objeto bytes vacío implica que el canal se ha cerrado desde el otro extremo.

    Tenga en cuenta que `recv()` puede lanzar `BlockingIOError`, aunque `select.select()` o `select.poll()` ha informado del socket listo para la lectura.

  `listen`(backlog)

    Escucha las conexiones realizadas al socket. El argumento backlog especifica el número máximo de conexiones en cola y debe ser al menos 1; el valor máximo depende del sistema (normalmente 5).

  `bind`(address)

    Enlaza el socket a address. El socket no debe estar enlazado ya. (El formato de address depende de la familia de direcciones — consulte la documentación `socket` para obtener más información.) Para marcar el socket como reutilizable (estableciendo la opción `SO_REUSEADDR`), llame al método `set_reuse_addr()` del objeto `dispatcher`.

  `accept`()

    Acepta una conexión. El socket debe estar enlazado a una dirección y escuchar las conexiones. El valor retornado puede ser `None` o un par `(conn, address)` donde conn es un objeto de socket new utilizable para enviar y recibir datos en la conexión, y address es la dirección enlazada al socket en el otro extremo de la conexión. Cuando se retorna `None` significa que la conexión no se llevó a cabo, en cuyo caso el servidor debe ignorar este evento y seguir escuchando otras conexiones entrantes.

  `close`()

    Cierra el socket. Se producirá un error en todas las operaciones futuras en el objeto de socket. El punto final remoto no recibirá más datos (después de vaciar los datos en cola). Los sockets se cierran automáticamente cuando se recogen como elementos no utilizados.

class `asyncore.``dispatcher_with_send`

  Una subclase `dispatcher` que agrega capacidad de salida almacenada en búfer simple, útil para clientes simples. Para un uso más sofisticado, utilice `asynchat.async_chat`.

class `asyncore.``file_dispatcher`

  Un file_dispatcher toma un descriptor de archivo o file object junto con un argumento de mapa opcional y lo ajusta para su uso con las funciones `poll()` o `loop()`. Si se proporciona un objeto de archivo o cualquier cosa con un método `fileno()`, ese método se llamará y se pasará al constructor `file_wrapper`.

  Disponibilidad: Unix.

class `asyncore.``file_wrapper`

  Un file_wrapper toma un descriptor de archivo entero y llama a `os.dup()` para duplicar el identificador de modo que el identificador original se pueda cerrar independientemente del file_wrapper. Esta clase implementa métodos suficientes emulando un socket para su uso por la clase `file_dispatcher`.

  Disponibilidad: Unix.

## Ejemplo asyncore de cliente HTTP básico

Aquí hay una llamada básica al cliente HTTP que usa la clase `dispatcher` para implementar su controlador de socket:

    import asyncore

    class HTTPClient(asyncore.dispatcher):

        def __init__(self, host, path):
            asyncore.dispatcher.__init__(self)
            self.create_socket()
            self.connect( (host, 80) )
            self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %
                                (path, host), 'ascii')

        def handle_connect(self):
            pass

        def handle_close(self):
            self.close()

        def handle_read(self):
            print(self.recv(8192))

        def writable(self):
            return (len(self.buffer) > 0)

        def handle_write(self):
            sent = self.send(self.buffer)
            self.buffer = self.buffer[sent:]

    client = HTTPClient('www.python.org', '/')
    asyncore.loop()

## Ejemplo asyncore de servidor de eco básico

Aquí hay un servidor de eco básico que utiliza la clase `dispatcher` para aceptar conexiones y distribuye las conexiones entrantes a un controlador:

    import asyncore

    class EchoHandler(asyncore.dispatcher_with_send):

        def handle_read(self):
            data = self.recv(8192)
            if data:
                self.send(data)

    class EchoServer(asyncore.dispatcher):

        def __init__(self, host, port):
            asyncore.dispatcher.__init__(self)
            self.create_socket()
            self.set_reuse_addr()
            self.bind((host, port))
            self.listen(5)

        def handle_accepted(self, sock, addr):
            print('Incoming connection from %s' % repr(addr))
            handler = EchoHandler(sock)

    server = EchoServer('localhost', 8080)
    asyncore.loop()