socketserver no Python: crie servidores

Publicado em: 19/08/2026
Tempo de leitura: 6 minutos
Cabos Ethernet conectados representando servidores de rede com socketserver no Python

O módulo socketserver simplifica a criação de servidores de rede no Python. Em vez de repetir manualmente o ciclo de criar um socket, fazer bind, escutar conexões, aceitar clientes e instanciar handlers, você escolhe uma classe de servidor e implementa apenas o método que processa cada requisição.

Essa abstração é útil em serviços internos, ferramentas de teste, protocolos simples, agentes locais e protótipos. Ainda assim, um servidor de rede precisa de limites, timeouts, framing, concorrência e encerramento previsível. Este guia mostra como usar TCPServer, UDPServer, StreamRequestHandler, servidores com threads e os principais cuidados de produção.

Como o socketserver é organizado

O módulo fornece quatro classes concretas básicas: TCPServer, UDPServer, UnixStreamServer e UnixDatagramServer. As duas últimas usam sockets Unix e não estão disponíveis em todas as plataformas. Por padrão, os servidores processam uma requisição de cada vez.

Para atender clientes simultaneamente, existem os mixins ThreadingMixIn e ForkingMixIn, além de classes prontas como ThreadingTCPServer e ThreadingUDPServer. O modelo com fork depende de POSIX e cria um novo processo, enquanto o modelo com threads compartilha memória e exige sincronização para estado mutável.

Antes de avançar, vale revisar o guia de cliente TCP com sockets no Python. socketserver não altera as propriedades de TCP ou UDP; apenas organiza o código do servidor.

Primeiro servidor TCP com StreamRequestHandler

StreamRequestHandler cria os objetos rfile e wfile, permitindo ler e escrever como em arquivos binários. O exemplo abaixo usa uma linha terminada por \n como framing.

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"erro: mensagem muito grande\n")
            return

        if not line.endswith(b"\n"):
            self.wfile.write(b"erro: mensagem incompleta\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()

O limite de leitura é indispensável. Sem ele, um cliente pode enviar dados indefinidamente sem completar a linha, fazendo o processo acumular memória. Também é importante definir a codificação quando o protocolo usa texto, em vez de chamar decode() sem tratamento de erro.

TCP é um fluxo, não um conjunto de mensagens

Uma chamada sendall() do cliente não corresponde necessariamente a uma chamada recv() no servidor. Os bytes podem chegar divididos ou agrupados. Por isso, o protocolo precisa definir framing: linha terminada por delimitador, tamanho prefixado, formato binário de tamanho fixo ou fechamento da conexão.

O artigo sobre struct e dados binários no Python mostra como criar cabeçalhos com tamanho explícito. Para protocolos textuais simples, uma linha limitada costuma ser suficiente.

Servidor concorrente com threads

TCPServer é síncrono. Enquanto um cliente está sendo atendido, os demais aguardam. Para conexões com I/O ou clientes lentos, use 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()

daemon_threads=False faz o Python aguardar as threads ativas antes de sair. block_on_close=True mantém o encerramento previsível. Se você escolher threads daemon, documente que requisições podem ser interrompidas durante o desligamento.

O guia de threading no Python explica locks, condições de corrida e quando threads ajudam. O GIL não elimina race conditions em estruturas compartilhadas.

Estado compartilhado e locks

Cada conexão recebe uma nova instância do handler, mas todas podem acessar self.server. Ao compartilhar contadores ou caches, proteja atualizações compostas.

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"requisição {current}\n".encode("utf-8"))

Evite manter sessões importantes apenas em memória quando usar processos, múltiplas instâncias ou reinicializações. Prefira armazenamento externo apropriado quando o estado precisa sobreviver ou ser compartilhado.

Timeout por conexão

Clientes lentos podem manter threads ocupadas. Configure timeout no socket aceito no método 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")

O atributo server.timeout afeta handle_request(), não o loop de serve_forever() nem automaticamente cada cliente. Para timeouts de sessão, configure o socket da conexão.

Backpressure e fila de conexões

request_queue_size controla aproximadamente quantas conexões aguardam aceitação quando o servidor está ocupado. Aumentar o valor não substitui limites de concorrência; apenas desloca a espera.

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

ThreadingTCPServer cria uma thread por conexão e não impõe um pool fixo. Um atacante pode abrir muitas conexões e consumir threads. Em serviço exposto, combine firewall, proxy, limites do sistema operacional, timeouts e, quando necessário, uma arquitetura com pool ou event loop.

Filtrar clientes com verify_request()

O método verify_request() pode rejeitar uma requisição antes do 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

Uma allowlist por IP é apenas uma camada. Endereços podem mudar, proxies alteram a origem observada e redes internas também podem ser comprometidas. Para autenticação forte, use TLS com certificados, HMAC ou credenciais do protocolo. O guia de ssl e TLS no Python cobre a proteção do canal.

Servidor UDP

UDP trabalha com datagramas independentes, que podem ser perdidos, duplicados ou chegar fora de ordem.

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

Não confie no endereço de origem UDP como autenticação. Evite respostas muito maiores que as solicitações para não criar um vetor de amplificação. Protocolos importantes precisam de IDs, proteção contra replay e tratamento de perda.

Tratamento de erros

Por padrão, uma exceção no handler é impressa em stderr e o servidor continua. Sobrescreva handle_error() para integrar logs estruturados sem expor payloads.

import logging

logger = logging.getLogger(__name__)

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

O guia de logging no Python ajuda a organizar níveis, handlers e dados de contexto. Não registre segredos nem mensagens completas por padrão.

Encerramento correto

serve_forever() para somente após uma chamada a shutdown(). Essa chamada precisa vir de outra thread; chamá-la dentro da mesma thread do loop causa 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()

Em uma aplicação real, trate sinais no processo principal, pare de aceitar novas conexões, aguarde o trabalho em andamento até um prazo e então libere recursos.

IPv6

Para escutar em IPv6, ajuste address_family.

import socket

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

Teste dual stack em cada sistema, pois o comportamento de IPv4 mapeado pode variar. Valide endereços com o módulo ipaddress quando aplicar regras.

Quando usar socketserver?

Use o módulo para protocolos pequenos, ferramentas internas, mocks, servidores locais e aprendizado. Para HTTP público, prefira um framework e servidor WSGI/ASGI. Para milhares de conexões duradouras, considere asyncio ou uma arquitetura orientada a eventos. Para lógica pesada de CPU, filas externas e processos controlados oferecem limites melhores que uma thread por conexão.

Erros comuns

Os problemas mais frequentes são ler sem limite, assumir que um recv() contém uma mensagem inteira, usar o servidor síncrono com clientes lentos, compartilhar estado sem lock, criar threads ilimitadas, omitir timeout, confiar apenas no IP, chamar shutdown() na thread errada e expor um servidor de demonstração à internet.

Boas práticas

Defina framing, tamanho máximo e timeout. Escolha conscientemente entre síncrono, threads, processos ou event loop. Proteja estado compartilhado. Use TLS e autenticação quando necessário. Implemente logs sem payload sensível, métricas de conexões e encerramento testável. Coloque serviços públicos atrás de controles de rede e limites adicionais.

Conclusão

socketserver reduz bastante o código necessário para criar servidores TCP e UDP, mas não remove as responsabilidades de um protocolo de rede. Limites, framing, concorrência, autenticação e desligamento continuam essenciais. Para serviços pequenos e controlados, o módulo oferece uma estrutura clara; para cargas públicas e complexas, use abstrações com pools, event loops e infraestrutura de produção.

Consulte a documentação oficial de socketserver e o RFC 9293 sobre TCP.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Sala de servidores iluminada representando conexões TLS seguras com ssl no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ssl no Python: conexões TLS seguras

    Aprenda ssl no Python para criar clientes e servidores TLS, validar certificados, configurar versões mínimas, CA e autenticação mútua.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Tela de verificação de conta representando autenticação de mensagens com HMAC no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    hmac no Python: autentique mensagens

    Aprenda HMAC no Python para assinar e validar webhooks, arquivos e mensagens com SHA-256, chaves seguras e comparação resistente a

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Leitor de impressão digital representando verificação de hashes com hashlib no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    hashlib no Python: hashes seguros

    Aprenda hashlib no Python para calcular SHA-256, verificar arquivos, usar BLAKE2, derivar chaves e evitar erros comuns de segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Código binário projetado representando conversões com binascii no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    binascii no Python: binário e ASCII

    Aprenda binascii no Python para converter hexadecimal, Base64 e quoted-printable, calcular CRC e validar dados binários com segurança.

    Ler mais

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

    codecs no Python: domine encodings

    Aprenda codecs no Python para trabalhar com encodings, handlers de erro, BOM, streams incrementais e migrar codecs.open para open.

    Ler mais

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

    base64 no Python: codifique dados

    Aprenda base64 no Python para codificar bytes, usar Base64 URL-safe, validar padding, aplicar limites e diferenciar encoding de criptografia.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026