"socketserver" --- Un framework para servidores de red
******************************************************

**Código fuente:** Lib/socketserver.py

======================================================================

El módulo "socketserver" simplifica la tarea de escribir servidores de
red.

Hay cuatro clases básicas de servidores concretos:

class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)

   Utiliza el protocolo TCP de Internet, que proporciona flujos
   continuos de datos entre el cliente y el servidor. Si
   *bind_and_activate* es verdadero, el constructor automáticamente
   intenta invocar a "server_bind()" y "server_activate()". Los otros
   parámetros se pasan a la clase base "BaseServer".

class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)

   Esto utiliza datagramas, que son paquetes discretos de información
   que pueden llegar fuera de servicio o perderse durante el tránsito.
   Los parámetros son los mismos que para "TCPServer".

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)

   Estas clases que se usan con menos frecuencia son similares a las
   clases TCP y UDP, pero usan sockets de dominio Unix; no están
   disponibles en plataformas que no sean Unix. Los parámetros son los
   mismos que para "TCPServer".

Estas cuatro clases procesan solicitudes *sincrónicamente*; cada
solicitud debe completarse antes de que se pueda iniciar la siguiente.
Esto no es adecuado si cada solicitud tarda mucho en completarse,
porque requiere mucho cálculo o porque retorna muchos datos que el
cliente tarda en procesar. La solución es crear un proceso o hilo
independiente para manejar cada solicitud; las clases mixtas
"ForkingMixIn" y "ThreadingMixIn" pueden usarse para soportar el
comportamiento asincrónico.

La creación de un servidor requiere varios pasos. Primero, debes crear
una clase de controlador de solicitudes subclasificando la clase
"BaseRequestHandler" y anulando su método "handle()"; este método
procesará las solicitudes entrantes. En segundo lugar, debe crear una
instancia de una de las clases de servidor, pasándole la dirección del
servidor y la clase del controlador de solicitudes. Se recomienda
utilizar el servidor en una declaración "with". Luego llame al método
"handle_request()" o "serve_forever()" del objeto de servidor para
procesar una o muchas solicitudes. Finalmente, llame a
"server_close()" para cerrar el socket (a menos que haya usado una
"with" declaración).

Al heredar de "ThreadingMixIn" para el comportamiento de la conexión
con subprocesos, debe declarar explícitamente cómo desea que se
comporten sus subprocesos en un cierre abrupto. La clase
"ThreadingMixIn" define un atributo *daemon_threads*, que indica si el
servidor debe esperar o no la terminación del hilo. Debe establecer la
bandera explícitamente si desea que los subprocesos se comporten de
forma autónoma; el valor predeterminado es "False", lo que significa
que Python no se cerrará hasta que todos los hilos creados por
"ThreadingMixIn" hayan salido.

Las clases de servidor tienen los mismos métodos y atributos externos,
independientemente del protocolo de red que utilicen.


Notas de creación del servidor
==============================

Hay cinco clases en un diagrama de herencia, cuatro de las cuales
representan servidores síncronos de cuatro tipos:

   +------------+
   | BaseServer |
   +------------+
         |
         v
   +-----------+        +------------------+
   | TCPServer |------->| UnixStreamServer |
   +-----------+        +------------------+
         |
         v
   +-----------+        +--------------------+
   | UDPServer |------->| UnixDatagramServer |
   +-----------+        +--------------------+

Tenga en cuenta que "UnixDatagramServer" deriva de "UDPServer", no de
"UnixStreamServer" --- la única diferencia entre una IP y un servidor
de flujo Unix es la familia de direcciones, que simplemente se repite
en ambos Clases de servidor Unix.

class socketserver.ForkingMixIn
class socketserver.ThreadingMixIn

   Se pueden crear versiones de Forking y threading de cada tipo de
   servidor utilizando estas clases mixtas. Por ejemplo,
   "ThreadingUDPServer" se crea de la siguiente manera:

      class ThreadingUDPServer(ThreadingMixIn, UDPServer):
          pass

   La clase de mezcla es lo primero, ya que anula un método definido
   en "UDPServer". La configuración de los distintos atributos también
   cambia el comportamiento del mecanismo del servidor subyacente.

   "ForkingMixIn" y las clases de Forking mencionadas a continuación
   solo están disponibles en plataformas POSIX que admiten
   "os.fork()".

   "socketserver.ForkingMixIn.server_close()" espera hasta que se
   completen todos los procesos secundarios, excepto si
   "socketserver.ForkingMixIn.block_on_close" atributo es falso.

   "socketserver.ThreadingMixIn.server_close()" espera hasta que se
   completen todos los subprocesos que no son demonios, excepto si el
   atributo "socketserver.ThreadingMixIn.block_on_close" es falso. Use
   subprocesos demoníacos configurando "ThreadingMixIn.daemon_threads"
   en "Verdadero" para no esperar hasta que se completen los
   subprocesos.

   Distinto en la versión 3.7:
   "socketserver.ForkingMixIn.server_close()" y
   "socketserver.ThreadingMixIn.server_close()" ahora espera hasta que
   se completen todos los procesos secundarios y los subprocesos no
   demoníacos. Agregue un nuevo atributo de clase
   "socketserver.ForkingMixIn.block_on_close" para optar por el
   comportamiento anterior a 3.7.

class socketserver.ForkingTCPServer
class socketserver.ForkingUDPServer
class socketserver.ThreadingTCPServer
class socketserver.ThreadingUDPServer

   Estas clases están predefinidas utilizando las clases mixtas.

Para implementar un servicio, debes derivar una clase de
"BaseRequestHandler" y redefinir su método "handle()". Luego, puede
ejecutar varias versiones del servicio combinando una de las clases de
servidor con su clase de controlador de solicitudes. La clase del
controlador de solicitudes debe ser diferente para los servicios de
datagramas o flujos. Esto se puede ocultar usando las subclases del
controlador "StreamRequestHandler" o "DatagramRequestHandler".

Por supuesto, ¡todavía tienes que usar la cabeza! Por ejemplo, no
tiene sentido usar un servidor de bifurcación si el servicio contiene
un estado en la memoria que puede ser modificado por diferentes
solicitudes, ya que las modificaciones en el proceso hijo nunca
alcanzarían el estado inicial que se mantiene en el proceso padre y se
pasa a cada hijo. En este caso, puede usar un servidor de subprocesos,
pero probablemente tendrá que usar bloqueos para proteger la
integridad de los datos compartidos.

Por otro lado, si está creando un servidor HTTP donde todos los datos
se almacenan externamente (por ejemplo, en el sistema de archivos),
una clase síncrona esencialmente hará que el servicio sea "sordo"
mientras se maneja una solicitud, que puede ser durante mucho tiempo
si un cliente tarda en recibir todos los datos que ha solicitado. Aquí
es apropiado un servidor de enhebrado o bifurcación.

En algunos casos, puede ser apropiado procesar parte de una solicitud
de forma síncrona, pero para finalizar el procesamiento en un hijo
bifurcado según los datos de la solicitud. Esto se puede implementar
usando un servidor sincrónico y haciendo un fork explicito en la clase
del controlador de solicitudes el método "handle()".

Otro enfoque para manejar múltiples solicitudes simultáneas en un
entorno que no admite subprocesos ni "fork()" (o donde estos son
demasiado costosos o inapropiados para el servicio) es mantener una
tabla explícita de solicitudes parcialmente terminadas y utilizar
"selectors" para decidir en qué solicitud trabajar a continuación (o
si manejar una nueva solicitud entrante). Esto es particularmente
importante para los servicios de transmisión en los que cada cliente
puede potencialmente estar conectado durante mucho tiempo (si no se
pueden utilizar subprocesos o subprocesos). Consulte "asyncore" para
ver otra forma de gestionar esto.


Objetos de servidor
===================

class socketserver.BaseServer(server_address, RequestHandlerClass)

   Esta es la superclase de todos los objetos de servidor en el
   módulo. Define la interfaz, que se indica a continuación, pero no
   implementa la mayoría de los métodos, que se realiza en subclases.
   Los dos parámetros se almacenan en los atributos respectivos
   "server_address" y "RequestHandlerClass".

   fileno()

      Retorna un descriptor de archivo entero para el socket en el que
      escucha el servidor. Esta función se pasa comúnmente a
      "selectors", para permitir monitorear múltiples servidores en el
      mismo proceso.

   handle_request()

      Procese una sola solicitud. Esta función llama a los siguientes
      métodos en orden: "get_request()", "verify_request()", y
      "process_request()". Si el método proporcionado por el usuario
      "handle()" de la clase del controlador lanza una excepción, se
      llamará al método "handle_error()" del servidor. Si no se recibe
      ninguna solicitud en "timeout" segundos, se llamará a
      "handle_timeout()" y "handle_request()" regresará.

   serve_forever(poll_interval=0.5)

      Manejar solicitudes hasta una solicitud explícita "shutdown()".
      Sondeo de apagado cada *poll_interval* segundos. Ignora el
      atributo "timeout". También llama "service_actions()", que puede
      ser utilizado por una subclase o mixin para proporcionar
      acciones específicas para un servicio dado. Por ejemplo, la
      clase "ForkingMixIn" usa "service_actions()" para limpiar
      procesos secundarios zombies.

      Distinto en la versión 3.3: Se agregó la llamada
      "service_actions" al método "serve_forever".

   service_actions()

      Esto se llama en el ciclo "serve_forever()". Este método puede
      ser reemplazado por subclases o clases mixtas para realizar
      acciones específicas para un servicio dado, como acciones de
      limpieza.

      Nuevo en la versión 3.3.

   shutdown()

      Dile al bucle "serve_forever()" que se detenga y espere hasta
      que lo haga. "shutdown()" debe llamarse mientras
      "serve_forever()" se está ejecutando en un hilo diferente, de lo
      contrario, se bloqueará.

   server_close()

      Limpiar el servidor. Puede ser sobrescrito.

   address_family

      La familia de protocolos a la que pertenece el socket del
      servidor. Algunos ejemplos comunes son "socket.AF_INET" y
      "socket.AF_UNIX".

   RequestHandlerClass

      La clase de controlador de solicitudes proporcionada por el
      usuario; se crea una instancia de esta clase para cada
      solicitud.

   server_address

      La dirección en la que escucha el servidor. El formato de las
      direcciones varía según la familia de protocolos; consulte la
      documentación del módulo "socket" para obtener más detalles.
      Para los protocolos de Internet, esta es una tupla que contiene
      una cadena que proporciona la dirección y un número de puerto
      entero: "('127.0.0.1', 80)", por ejemplo.

   socket

      El objeto de socket en el que el servidor escuchará las
      solicitudes entrantes.

   Las clases de servidor admiten las siguientes variables de clase:

   allow_reuse_address

      Si el servidor permitirá la reutilización de una dirección. Este
      valor predeterminado es "False", y se puede establecer en
      subclases para cambiar la política.

   request_queue_size

      El tamaño de la cola de solicitudes. Si toma mucho tiempo
      procesar una sola solicitud, cualquier solicitud que llegue
      mientras el servidor está ocupado se coloca en una cola, hasta
      "request_queue_size". Una vez que la cola está llena, más
      solicitudes de clientes obtendrán un error de "Conexión
      denegada". El valor predeterminado suele ser 5, pero las
      subclases pueden anularlo.

   socket_type

      El tipo de socket utilizado por el servidor;
      "socket.SOCK_STREAM" y "socket.SOCK_DGRAM" son dos valores
      comunes.

   timeout

      Duración del tiempo de espera, medida en segundos, o "None" si
      no se desea un tiempo de espera. Si "handle_request()" no recibe
      solicitudes entrantes dentro del período de tiempo de espera, se
      llama al método "handle_timeout()".

   Hay varios métodos de servidor que pueden ser anulados por
   subclases de clases de servidor base como "TCPServer"; estos
   métodos no son útiles para los usuarios externos del objeto de
   servidor.

   finish_request(request, client_address)

      En realidad, procesa la solicitud creando instancias
      "RequestHandlerClass" y llamando a su método "handle()".

   get_request()

      Debe aceptar una solicitud del socket y retornar una tupla de 2
      que contenga el objeto de socket *nuevo* que se utilizará para
      comunicarse con el cliente y la dirección del cliente.

   handle_error(request, client_address)

      Esta función se llama si el método "handle()" de una instancia
      "RequestHandlerClass" lanza una excepción. La acción
      predeterminada es imprimir el rastreo al error estándar y
      continuar manejando más solicitudes.

      Distinto en la versión 3.6: Ahora solo se solicitan excepciones
      derivadas de la clase "Exception".

   handle_timeout()

      Esta función se llama cuando el atributo "timeout" se ha
      establecido en un valor distinto de "None" y el tiempo de espera
      ha pasado sin que se reciban solicitudes. La acción
      predeterminada para los servidores de forking es recopilar el
      estado de cualquier proceso hijo que haya salido, mientras que
      en los servidores de threading este método no hace nada.

   process_request(request, client_address)

      Llama a "finish_request()" para crear una instancia de
      "RequestHandlerClass". Si lo desea, esta función puede crear un
      nuevo proceso o hilo para manejar la solicitud; las clases
      "ForkingMixIn" y "ThreadingMixIn" hacen esto.

   server_activate()

      Lo llama el constructor del servidor para activar el servidor.
      El comportamiento predeterminado para un servidor TCP
      simplemente invoca "listen()" en el socket del servidor. Puede
      anularse.

   server_bind()

      Lo llama el constructor del servidor para vincular el socket a
      la dirección deseada. Puede anularse.

   verify_request(request, client_address)

      Debe retornar un valor booleano; si el valor es "True", la
      solicitud se procesará, y si es "False", la solicitud será
      denegada. Esta función se puede anular para implementar
      controles de acceso para un servidor. La implementación
      predeterminada siempre retorna "True".

   Distinto en la versión 3.6: Se agregó soporte para el protocolo
   *context manager*. Salir del administrador de contexto es
   equivalente a llamar a "server_close()".


Solicitar objetos de controlador
================================

class socketserver.BaseRequestHandler

   Esta es la superclase de todos los objetos de manejo de
   solicitudes. Define la interfaz, que se muestra a continuación. Una
   subclase de controlador de solicitudes concreta debe definir un
   nuevo método "handle()" y puede anular cualquiera de los otros
   métodos. Se crea una nueva instancia de la subclase para cada
   solicitud.

   setup()

      Se llama antes del método "handle()" para realizar las acciones
      de inicialización necesarias. La implementación predeterminada
      no hace nada.

   handle()

      Esta función debe realizar todo el trabajo necesario para
      atender una solicitud. La implementación predeterminada no hace
      nada. Dispone de varios atributos de instancia; la solicitud
      está disponible como "self.request"; la dirección del cliente
      como "self.client_address"; y la instancia del servidor como
      "self.server", en caso de que necesite acceder a la información
      por servidor.

      El tipo de "self.request" es diferente para datagramas o
      servicios de flujo. Para los servicios de transmisión,
      "self.request" es un objeto de socket; para servicios de
      datagramas, "self.request" es un par de string y socket.

   finish()

      Se llama después del método "handle()" para realizar las
      acciones de limpieza necesarias. La implementación
      predeterminada no hace nada. Si "setup()" lanza una excepción,
      no se llamará a esta función.

class socketserver.StreamRequestHandler
class socketserver.DatagramRequestHandler

   Estas subclases "BaseRequestHandler" anulan los métodos "setup()" y
   "finish()", y proporcionan "self.rfile" y "self.wfile" atributos.
   Los atributos "self.rfile" y "self.wfile" se pueden leer o
   escribir, respectivamente, para obtener los datos de la solicitud o
   retornar los datos al cliente.

   Los atributos "rfile" de ambas clases admiten la interfaz legible
   "io.BufferedIOBase", y "DatagramRequestHandler.wfile" admite la
   "io.BufferedIOBase" interfaz de escritura.

   Distinto en la versión 3.6: "StreamRequestHandler.wfile" también
   admite la interfaz de escritura "io.BufferedIOBase".


Ejemplos
========


"socketserver.TCPServer" Ejemplo
--------------------------------

Este es el lado del servidor:

   import socketserver

   class MyTCPHandler(socketserver.BaseRequestHandler):
       """
       The request handler class for our server.

       It is instantiated once per connection to the server, and must
       override the handle() method to implement communication to the
       client.
       """

       def handle(self):
           # self.request is the TCP socket connected to the client
           self.data = self.request.recv(1024).strip()
           print("{} wrote:".format(self.client_address[0]))
           print(self.data)
           # just send back the same data, but upper-cased
           self.request.sendall(self.data.upper())

   if __name__ == "__main__":
       HOST, PORT = "localhost", 9999

       # Create the server, binding to localhost on port 9999
       with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
           # Activate the server; this will keep running until you
           # interrupt the program with Ctrl-C
           server.serve_forever()

Una clase de controlador de solicitudes alternativa que hace uso de
secuencias (objetos similares a archivos que simplifican la
comunicación al proporcionar la interfaz de archivo estándar):

   class MyTCPHandler(socketserver.StreamRequestHandler):

       def handle(self):
           # self.rfile is a file-like object created by the handler;
           # we can now use e.g. readline() instead of raw recv() calls
           self.data = self.rfile.readline().strip()
           print("{} wrote:".format(self.client_address[0]))
           print(self.data)
           # Likewise, self.wfile is a file-like object used to write back
           # to the client
           self.wfile.write(self.data.upper())

La diferencia es que la llamada "readline()" en el segundo controlador
llamará a "recv()" varias veces hasta que encuentre un carácter de
nueva línea, mientras que la llamada única "recv()" en el primer
controlador simplemente retornará lo que se ha enviado desde el
cliente en una llamada "sendall()" .

Este es el lado del cliente:

   import socket
   import sys

   HOST, PORT = "localhost", 9999
   data = " ".join(sys.argv[1:])

   # Create a socket (SOCK_STREAM means a TCP socket)
   with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
       # Connect to server and send data
       sock.connect((HOST, PORT))
       sock.sendall(bytes(data + "\n", "utf-8"))

       # Receive data from the server and shut down
       received = str(sock.recv(1024), "utf-8")

   print("Sent:     {}".format(data))
   print("Received: {}".format(received))

La salida del ejemplo debería verse así:

Servidor:

   $ python TCPServer.py
   127.0.0.1 wrote:
   b'hello world with TCP'
   127.0.0.1 wrote:
   b'python is nice'

Cliente:

   $ python TCPClient.py hello world with TCP
   Sent:     hello world with TCP
   Received: HELLO WORLD WITH TCP
   $ python TCPClient.py python is nice
   Sent:     python is nice
   Received: PYTHON IS NICE


"socketserver.UDPServer" Ejemplo
--------------------------------

Este es el lado del servidor:

   import socketserver

   class MyUDPHandler(socketserver.BaseRequestHandler):
       """
       This class works similar to the TCP handler class, except that
       self.request consists of a pair of data and client socket, and since
       there is no connection the client address must be given explicitly
       when sending data back via sendto().
       """

       def handle(self):
           data = self.request[0].strip()
           socket = self.request[1]
           print("{} wrote:".format(self.client_address[0]))
           print(data)
           socket.sendto(data.upper(), self.client_address)

   if __name__ == "__main__":
       HOST, PORT = "localhost", 9999
       with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
           server.serve_forever()

Este es el lado del cliente:

   import socket
   import sys

   HOST, PORT = "localhost", 9999
   data = " ".join(sys.argv[1:])

   # SOCK_DGRAM is the socket type to use for UDP sockets
   sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

   # As you can see, there is no connect() call; UDP has no connections.
   # Instead, data is directly sent to the recipient via sendto().
   sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
   received = str(sock.recv(1024), "utf-8")

   print("Sent:     {}".format(data))
   print("Received: {}".format(received))

La salida del ejemplo debería verse exactamente como en el ejemplo del
servidor TCP.


Mixins asincrónicos
-------------------

Para construir controladores asincrónicos, use las clases
"ThreadingMixIn" y "ForkingMixIn".

Un ejemplo para la clase "ThreadingMixIn" class

   import socket
   import threading
   import socketserver

   class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):

       def handle(self):
           data = str(self.request.recv(1024), 'ascii')
           cur_thread = threading.current_thread()
           response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
           self.request.sendall(response)

   class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
       pass

   def client(ip, port, message):
       with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
           sock.connect((ip, port))
           sock.sendall(bytes(message, 'ascii'))
           response = str(sock.recv(1024), 'ascii')
           print("Received: {}".format(response))

   if __name__ == "__main__":
       # Port 0 means to select an arbitrary unused port
       HOST, PORT = "localhost", 0

       server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
       with server:
           ip, port = server.server_address

           # Start a thread with the server -- that thread will then start one
           # more thread for each request
           server_thread = threading.Thread(target=server.serve_forever)
           # Exit the server thread when the main thread terminates
           server_thread.daemon = True
           server_thread.start()
           print("Server loop running in thread:", server_thread.name)

           client(ip, port, "Hello World 1")
           client(ip, port, "Hello World 2")
           client(ip, port, "Hello World 3")

           server.shutdown()

La salida del ejemplo debería verse así:

   $ python ThreadedTCPServer.py
   Server loop running in thread: Thread-1
   Received: Thread-2: Hello World 1
   Received: Thread-3: Hello World 2
   Received: Thread-4: Hello World 3

La clase "ForkingMixIn" se usa de la misma manera, excepto que el
servidor generará un nuevo proceso para cada solicitud. Disponible
solo en plataformas POSIX que admitan "fork()".
