"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 elevar "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()
