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

    Rack de servidores que representa un endpoint creado con xmlrpc.server en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    xmlrpc.server: crea servidores XML-RPC

    Aprende xmlrpc.server en Python para crear servidores XML-RPC, registrar funciones, limitar métodos y rutas y evitar exposición insegura.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026
    Código de error sobre datos binarios que representa fallos manejados con urllib.error en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    urllib.error en Python: maneja errores HTTP

    Aprende urllib.error en Python para manejar URLError, HTTPError, descargas incompletas, retries selectivos y diagnósticos de red claros.

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Teclas con la palabra HTML que representan entidades HTML en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    html.entities: convierte entidades HTML

    Aprende html.entities en Python para consultar entidades HTML, convertir nombres y code points y no confundir decodificación con sanitización.

    Ler mais

    Tempo de leitura: 7 minutos
    21/08/2026
    Carpeta con archivos que representa tipos MIME identificados con mimetypes en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes: detecta tipos MIME de archivos

    Aprende mimetypes en Python para identificar tipos de archivo, validar cargas y definir Content-Type con más seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    20/08/2026
    Código HTML en una pantalla que representa análisis con html.parser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    html.parser en Python: analiza HTML

    Aprende html.parser en Python para extraer texto, enlaces y metadatos, procesar HTML por bloques y no confundir parsing con sanitización.

    Ler mais

    Tempo de leitura: 4 minutos
    20/08/2026
    Persona usando un portátil en una sesión web que representa cookies con http.cookiejar en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    http.cookiejar en Python: gestiona cookies

    Aprende http.cookiejar en Python para mantener sesiones, aplicar políticas, persistir cookies de forma segura e integrar urllib.request.

    Ler mais

    Tempo de leitura: 5 minutos
    20/08/2026