socketserver en Python: crea servidores

Publicado el: 19/08/2026
Tempo de leitura: 5 minutos
Cables Ethernet conectados que representan servidores de red con socketserver en Python

El módulo socketserver simplifica las partes repetitivas de un servidor de red. En lugar de crear un socket, hacer bind, escuchar, aceptar clientes y construir manualmente un handler para cada solicitud, eliges una clase de servidor e implementas el método que procesa la petición.

Esta abstracción resulta útil en servicios internos, servidores de prueba, protocolos pequeños, agentes locales y aprendizaje. Aun así, todo servidor necesita framing, límites de tamaño, timeouts, una política de concurrencia, autenticación y apagado predecible. Esta guía cubre TCPServer, UDPServer, StreamRequestHandler, servidores con hilos y límites de producción.

Cómo se organiza socketserver

Las cuatro clases concretas básicas son TCPServer, UDPServer, UnixStreamServer y UnixDatagramServer. Las variantes Unix no están disponibles en todas las plataformas. Por defecto, estas clases procesan una solicitud cada vez.

La concurrencia se añade con ThreadingMixIn o ForkingMixIn. También existen clases listas como ThreadingTCPServer y ThreadingUDPServer. El modelo fork depende de POSIX y separa la memoria; los hilos comparten estado y requieren sincronización.

Antes de avanzar, revisa la guía del cliente TCP con sockets en Python. socketserver organiza el servidor, pero no cambia las propiedades de TCP o UDP.

Primer servidor TCP con StreamRequestHandler

StreamRequestHandler expone rfile y wfile como archivos binarios con buffer. El ejemplo usa un salto de línea como delimitador del mensaje.

import socketserver

MAX_LINE = 10_000

class EchoHandler(socketserver.StreamRequestHandler):
    def handle(self) -> None:
        line = self.rfile.readline(MAX_LINE + 1)

        if len(line) > MAX_LINE:
            self.wfile.write(b"error: mensaje demasiado grande\n")
            return

        if not line.endswith(b"\n"):
            self.wfile.write(b"error: mensaje incompleto\n")
            return

        response = line.rstrip(b"\r\n").upper() + b"\n"
        self.wfile.write(response)

HOST, PORT = "127.0.0.1", 9000

with socketserver.TCPServer((HOST, PORT), EchoHandler) as server:
    server.serve_forever()

El límite de lectura es imprescindible. Sin él, un cliente puede enviar datos indefinidamente sin completar la línea y consumir memoria. Si el protocolo usa texto, define encoding y manejo de errores explícitamente.

TCP es un flujo

Una llamada sendall() del cliente no corresponde necesariamente a una llamada recv() del servidor. Los bytes pueden llegar divididos o agrupados. Cada protocolo debe definir framing: delimitador, prefijo de longitud, registro fijo o cierre de conexión.

La guía de struct y datos binarios en Python muestra cabeceras con longitud explícita. Para protocolos textuales pequeños, una línea limitada suele bastar.

Servidor concurrente con hilos

TCPServer procesa un cliente cada vez. Un cliente lento puede bloquear a los demás. Para conexiones orientadas a I/O, usa ThreadingTCPServer.

class SafeThreadingTCPServer(socketserver.ThreadingTCPServer):
    allow_reuse_address = True
    daemon_threads = False
    block_on_close = True

with SafeThreadingTCPServer((HOST, PORT), EchoHandler) as server:
    server.serve_forever()

Con daemon_threads=False, Python espera a los handlers activos antes de salir. block_on_close=True mantiene una limpieza predecible. Si eliges hilos daemon, documenta que pueden interrumpirse durante el apagado.

La guía de threading en Python explica locks y condiciones de carrera. El GIL no protege automáticamente operaciones compuestas sobre estado compartido.

Estado compartido y locks

Cada petición crea una nueva instancia del handler, pero todas acceden a self.server. Protege actualizaciones compuestas a contadores o cachés.

import threading

class CountingServer(socketserver.ThreadingTCPServer):
    daemon_threads = False

    def __init__(self, address, handler):
        super().__init__(address, handler)
        self.total_requests = 0
        self.counter_lock = threading.Lock()

class CountingHandler(socketserver.StreamRequestHandler):
    def handle(self) -> None:
        with self.server.counter_lock:
            self.server.total_requests += 1
            current = self.server.total_requests

        self.wfile.write(f"solicitud {current}\n".encode("utf-8"))

No mantengas sesiones críticas solo en memoria si habrá procesos, múltiples instancias o reinicios. Usa almacenamiento externo cuando el estado deba sobrevivir o compartirse.

Timeout por conexión

Los clientes lentos pueden ocupar hilos indefinidamente. Configura un timeout en el socket aceptado desde setup().

class TimedHandler(socketserver.StreamRequestHandler):
    timeout_seconds = 10

    def setup(self) -> None:
        super().setup()
        self.request.settimeout(self.timeout_seconds)

    def handle(self) -> None:
        try:
            line = self.rfile.readline(4097)
        except TimeoutError:
            return

        if not line or len(line) > 4096:
            return

        self.wfile.write(b"ok\n")

El atributo server.timeout afecta handle_request(); no impone automáticamente timeout a cada cliente ni al bucle de serve_forever().

Backpressure y cola de conexiones

request_queue_size controla aproximadamente cuántas conexiones esperan mientras el servidor está ocupado. Aumentarlo no limita la concurrencia; solo cambia dónde esperan los clientes.

class LimitedServer(socketserver.ThreadingTCPServer):
    request_queue_size = 32
    allow_reuse_address = True

ThreadingTCPServer crea un hilo por solicitud y no ofrece un pool fijo. Un servicio público necesita límites del sistema, firewall, timeouts, control de conexiones y, a menudo, proxy o arquitectura orientada a eventos.

Filtrar solicitudes

verify_request() permite rechazar un cliente antes del handler.

import ipaddress

ALLOWED = ipaddress.ip_network("10.10.0.0/16")

class InternalServer(socketserver.ThreadingTCPServer):
    def verify_request(self, request, client_address) -> bool:
        client_ip = ipaddress.ip_address(client_address[0])
        return client_ip in ALLOWED

Una allowlist por IP es solo una capa. Los proxies modifican la dirección observada y una red interna puede estar comprometida. Para autenticación fuerte, usa TLS, HMAC o credenciales del protocolo. Consulta la guía de ssl y TLS en Python.

Servidor UDP

UDP procesa datagramas independientes que pueden perderse, duplicarse o llegar desordenados.

class UDPHandler(socketserver.BaseRequestHandler):
    def handle(self) -> None:
        data, udp_socket = self.request

        if len(data) > 1024:
            return

        response = data.strip().upper()
        udp_socket.sendto(response, self.client_address)

with socketserver.ThreadingUDPServer((HOST, 9001), UDPHandler) as server:
    server.serve_forever()

No uses la dirección de origen como autenticación. Evita respuestas mucho mayores que la petición para prevenir amplificación. Los protocolos importantes necesitan IDs y protección contra replay.

Manejo de errores

Por defecto, una excepción en handle() imprime un traceback y el servidor continúa. Sobrescribe handle_error() para logs estructurados sin exponer payloads.

import logging
logger = logging.getLogger(__name__)

class LoggedServer(socketserver.ThreadingTCPServer):
    def handle_error(self, request, client_address) -> None:
        logger.exception(
            "fallo al procesar cliente",
            extra={"client_ip": client_address[0]},
        )

Registra metadatos e identificadores de correlación, no tokens ni cuerpos completos.

Apagado correcto

serve_forever() se detiene tras llamar a shutdown(). Esa llamada debe realizarse desde otro hilo; hacerlo desde el mismo hilo del bucle produce deadlock.

import threading

server = SafeThreadingTCPServer((HOST, PORT), EchoHandler)
thread = threading.Thread(target=server.serve_forever)
thread.start()

try:
    thread.join()
except KeyboardInterrupt:
    server.shutdown()
    server.server_close()
    thread.join()

En producción, deja de aceptar trabajo, espera las solicitudes activas hasta un plazo y libera los recursos.

IPv6

import socket

class IPv6TCPServer(socketserver.ThreadingTCPServer):
    address_family = socket.AF_INET6

El comportamiento dual stack varía entre sistemas. Prueba direcciones IPv4 mapeadas y normaliza los clientes antes de aplicar políticas.

Cuándo usar socketserver

Es apropiado para protocolos pequeños, herramientas internas, agentes locales, mocks y aprendizaje. Para HTTP público usa un framework y servidor WSGI o ASGI. Para miles de conexiones duraderas, considera asyncio. Para CPU intensiva, procesos limitados y colas externas ofrecen mejor aislamiento.

Errores frecuentes

Los fallos habituales son leer sin límite, asumir que un receive equivale a un mensaje, usar el servidor síncrono con clientes lentos, modificar estado sin lock, crear hilos ilimitados, omitir timeouts, confiar solo en IP, llamar shutdown() desde el hilo incorrecto y exponer un servidor de demostración a internet.

Buenas prácticas

Define framing, tamaños máximos y timeouts. Elige conscientemente entre sincronía, hilos, procesos o event loop. Protege estado compartido. Añade TLS y autenticación. Implementa logs seguros, métricas y apagado probado. Coloca servicios públicos detrás de controles y límites adicionales.

Conclusión

socketserver elimina mucho código repetitivo de servidores TCP y UDP, pero no elimina las responsabilidades del protocolo. Framing, límites, concurrencia, autenticación y apagado siguen siendo esenciales. Es claro y eficaz para servicios controlados; cargas públicas necesitan pools, event loops e infraestructura de producción.

Consulta la documentación oficial de socketserver y el RFC 9293 sobre TCP.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Sala de servidores iluminada que representa conexiones TLS seguras con ssl en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ssl en Python: conexiones TLS seguras

    Aprende ssl en Python para crear clientes y servidores TLS, validar certificados y hostname, configurar CA, versiones mínimas y mTLS.

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Pantalla de verificación de cuenta que representa autenticación de mensajes con HMAC en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HMAC en Python: autentica mensajes

    Aprende HMAC en Python para firmar y validar webhooks, archivos y mensajes con SHA-256, claves seguras y comparación resistente a

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Lector de huella digital que representa verificación de hashes con hashlib en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    hashlib en Python: hashes seguros

    Aprende hashlib en Python para calcular SHA-256, verificar archivos, usar BLAKE2, derivar claves y evitar errores comunes de seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Código binario proyectado que representa conversiones con binascii en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    binascii en Python: binario y ASCII

    Aprende binascii en Python para convertir hexadecimal, Base64 y quoted-printable, calcular CRC y validar datos binarios de forma segura.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    codecs en Python: domina encodings

    Aprende codecs en Python para usar encodings, handlers, BOM, streams incrementales y migrar codecs.open a open.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    A person reads 'Python for Unix and Linux System Administration' indoors.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    base64 en Python: codifica datos

    Aprende base64 en Python para codificar bytes, usar Base64 URL-safe, validar padding, aplicar límites y diferenciar encoding de cifrado.

    Ler mais

    Tempo de leitura: 5 minutos
    18/08/2026