socket en Python: redes TCP y UDP

Publicado el: 27/08/2026
Tempo de leitura: 6 minutos
Close-up of electric plug and socket with vibrant lighting, showcasing technology and energy concepts.

El módulo socket proporciona la interfaz de bajo nivel para comunicación de red en Python. Permite crear clientes y servidores TCP, intercambiar datagramas UDP, trabajar con IPv4, IPv6 y sockets locales, configurar timeouts, resolver nombres y acceder a opciones del sistema operativo. Bibliotecas de HTTP, SMTP, bases de datos y mensajería se construyen sobre conceptos de transporte similares.

Los sockets transportan bytes, no mensajes Python. La aplicación debe definir framing, encoding, límites, deadlines, autenticación y manejo de desconexión. Para un protocolo estándar, prefiere una biblioteca madura de alto nivel. Usa socket al implementar o estudiar un protocolo, integrar un servicio especializado o necesitar control detallado.

Familias de direcciones y tipos

AF_INET representa IPv4, AF_INET6 representa IPv6 y AF_UNIX, cuando existe, ofrece comunicación local por pathname. TCP normalmente usa SOCK_STREAM; UDP utiliza SOCK_DGRAM.

import socket

cliente = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    cliente.settimeout(5)
    cliente.connect(("example.com", 80))
finally:
    cliente.close()

Prefiere un context manager para cerrar el descriptor incluso cuando ocurre una excepción.

Conecta con create_connection

socket.create_connection() resuelve el host e intenta direcciones adecuadas, simplificando clientes TCP.

import socket

with socket.create_connection(("example.com", 80), timeout=5) as sock:
    sock.sendall(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
    respuesta = sock.recv(4096)
    print(respuesta[:100])

Para HTTP de producción, utiliza una biblioteca HTTP. El ejemplo muestra únicamente la capa de transporte.

TCP es un flujo de bytes

TCP entrega un flujo ordenado. Una llamada a send() en un lado no corresponde necesariamente a una llamada a recv() en el otro. Los datos pueden llegar divididos o agrupados.

El protocolo de aplicación debe indicar dónde termina cada mensaje mediante delimitador validado, header fijo, prefijo de longitud o formato autodescriptivo.

send y sendall

send() puede transmitir solo parte del buffer y devuelve la cantidad aceptada. sendall() continúa hasta enviar todo o encontrar un error.

mensaje = b"datos importantes"
sock.sendall(mensaje)

Incluso después de sendall(), el receptor debe reconstruir el mensaje. El éxito significa que el sistema local aceptó los bytes, no que la aplicación remota los procesó.

recv y cierre ordenado

recv(tamano) devuelve hasta la cantidad solicitada. Un bytes vacío indica que el peer cerró su dirección de envío correctamente.

partes = []
while True:
    bloque = sock.recv(4096)
    if not bloque:
        break
    partes.append(bloque)
datos = b"".join(partes)

No esperes EOF cuando el protocolo mantiene la conexión abierta para múltiples mensajes; usa framing.

Framing con prefijo de longitud

Un diseño común envía primero un tamaño fijo y después el payload.

import struct


def recibir_exacto(sock, cantidad):
    partes = []
    restante = cantidad
    while restante:
        bloque = sock.recv(restante)
        if not bloque:
            raise ConnectionError("conexión cerrada")
        partes.append(bloque)
        restante -= len(bloque)
    return b"".join(partes)

header = recibir_exacto(sock, 4)
tamano = struct.unpack("!I", header)[0]
if tamano > 1_000_000:
    raise ValueError("mensaje demasiado grande")
payload = recibir_exacto(sock, tamano)

Valida el tamaño declarado antes de reservar memoria.

Servidor TCP básico

Un servidor crea el socket, asocia una dirección, escucha y acepta conexiones.

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as servidor:
    servidor.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    servidor.bind(("127.0.0.1", 9000))
    servidor.listen()
    conexion, direccion = servidor.accept()
    with conexion:
        conexion.sendall(b"hola\n")

Asociar a 127.0.0.1 restringe acceso a la máquina local. Escuchar en todas las interfaces expone el servicio y exige autenticación, firewall, límites y hardening.

listen y backlog

listen() coloca el socket en modo pasivo. El backlog influye en la cola de conexiones pendientes, aunque el comportamiento exacto depende del sistema.

Un backlog grande no arregla un servidor lento. Acepta rápidamente, limita clientes y configura timeouts.

accept devuelve otro socket

accept() devuelve un nuevo socket conectado y la dirección remota. El socket de escucha sigue disponible.

Cierra ambos de forma independiente. Filtrar conexiones aceptadas agota descriptores.

Concurrencia del servidor

Un servidor mínimo atiende un cliente por vez. Para varios clientes, usa threads, procesos, selectors o asyncio. La elección depende de cantidad de conexiones, workload, latencia y complejidad.

select en Python explica multiplexación de descriptores. No crees una thread ilimitada por conexión.

Timeouts

settimeout(segundos) limita operaciones bloqueantes.

sock.settimeout(10)
try:
    datos = sock.recv(4096)
except socket.timeout:
    registrar_aviso("cliente inactivo")

Define deadlines de conexión, lectura y escritura según el protocolo. Una conexión sin límite puede ocupar un worker indefinidamente.

Modo no bloqueante

setblocking(False) hace que las llamadas vuelvan inmediatamente y lancen BlockingIOError cuando todavía no pueden avanzar.

No construyas un busy loop. Usa selectors, select o un event loop.

UDP

UDP envía datagramas independientes sin entrega fiable, orden, retransmisión ni estado de conexión.

import socket

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
    sock.settimeout(2)
    sock.sendto(b"consulta", ("127.0.0.1", 9999))
    datos, origen = sock.recvfrom(2048)
    print(origen, datos)

Los datagramas pueden perderse, duplicarse, reordenarse o truncarse si el buffer es pequeño. La aplicación define retries e idempotencia.

Tamaño de UDP

Aunque UDP puede representar datagramas grandes, las redes reales tienen MTU y fragmentación. Los mensajes pequeños son más robustos. Usa TCP u otro protocolo para payloads grandes o fiables.

Resolución de nombres

getaddrinfo() resuelve un hostname y devuelve alternativas con familia, tipo, protocolo y dirección.

for resultado in socket.getaddrinfo("example.com", 443, type=socket.SOCK_STREAM):
    familia, tipo, protocolo, canonico, direccion = resultado
    print(familia, direccion)

DNS puede ser lento, fallar o devolver varias direcciones. No guardes una respuesta para siempre sin política.

IPv4 e IPv6

Usa getaddrinfo() e intenta las direcciones devueltas en lugar de asumir IPv4. Las tuplas IPv6 difieren y el dual stack puede requerir configuración específica.

Prueba despliegues IPv4-only, IPv6-only y dual stack.

Puertos y orden de bytes

Los puertos van de 0 a 65535 y los bajos pueden requerir privilegios. Funciones como htons(), htonl(), ntohs() y ntohl() convierten orden de bytes. Los protocolos binarios suelen usar struct con prefijo !.

SO_REUSEADDR

SO_REUSEADDR puede facilitar reinicio de servidores cuando quedan conexiones cerrándose. Su semántica varía y no significa que varios servidores independientes deban compartir una puerta.

Configura opciones antes de bind().

Keepalive

SO_KEEPALIVE habilita probes del sistema para detectar conexiones rotas, pero los intervalos por defecto pueden ser largos y el tuning es específico de plataforma.

Keepalive no sustituye heartbeats y deadlines de aplicación.

shutdown

shutdown(socket.SHUT_WR) indica que el programa no enviará más datos mientras puede seguir recibiendo. close() libera el descriptor local.

Usa half-close solo cuando el protocolo defina su significado.

TLS

Un socket TCP plano no ofrece confidencialidad, autenticación del peer ni protección frente a intermediarios. Envuélvelo con un ssl.SSLContext correctamente configurado o utiliza un cliente seguro de alto nivel.

No inventes criptografía ni desactives verificación de certificados como solución permanente.

Encoding

La red transporta bytes. Define el encoding de texto explícitamente.

mensaje = "hola".encode("utf-8")
sock.sendall(mensaje)
texto = datos.decode("utf-8", errors="strict")

Los delimitadores se definen como bytes y las secuencias inválidas necesitan una política.

Seguridad de entrada

Limita tamaño, profundidad, cantidad de campos y tiempo de procesamiento. Nunca pases datos de red directamente a eval(), shell, SQL o deserializadores inseguros.

Autentica clientes cuando sea necesario y autoriza cada operación.

Clientes lentos

Un cliente puede enviar pocos bytes lentamente y ocupar un worker. Usa deadlines, progreso mínimo, límites de conexiones y multiplexación.

Rate limiting y backpressure deben proteger memoria y servicios downstream.

Fallos transitorios

Las conexiones fallan por DNS, timeout, reset, routing, refusal o indisponibilidad temporal. Reintenta solo operaciones idempotentes, con límite y backoff exponencial.

No repitas indefinidamente ni trates un fallo de autenticación como transitorio.

Sockets locales Unix

Cuando AF_UNIX está disponible, procesos de una máquina pueden comunicarse mediante pathname con menor exposición a la red.

Gestiona permisos, elimina paths antiguos con cuidado y restringe el directorio.

Pruebas

Prueba frames divididos, varios frames juntos, EOF, timeout, reset, direcciones inválidas, IPv6, DNS múltiple, clientes lentos, payload máximo, conexiones simultáneas, shutdown y pérdida de red.

Usa sockets locales y puertos efímeros. Evita depender de servicios públicos.

Observabilidad

Registra dirección, duración, bytes, estado del protocolo y categoría de error sin guardar secretos o payloads sensibles. Métricas de conexiones activas, timeouts, resets y colas muestran saturación.

Errores comunes

Los fallos frecuentes son suponer que recv() devuelve un mensaje completo, ignorar envíos parciales, omitir timeouts, exponer el servidor accidentalmente, esperar fiabilidad de UDP, asignar una thread ilimitada por cliente, tratar DNS como identidad y enviar datos sensibles sin TLS.

Conclusión

socket ofrece control directo de red, pero exige un protocolo explícito. Define framing, límites, timeouts, seguridad, lifecycle y concurrencia antes de desplegar.

Consulta la documentación oficial de socket y select en Python para multiplexar conexiones.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    multiprocessing en Python: varios núcleos

    Aprende multiprocessing en Python con procesos, pools, queues, pipes, memoria compartida, cancelación, seguridad y shutdown correcto.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Bright yellow and blue shopping carts arranged in orderly rows outdoors.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    concurrent.futures: hilos y procesos en paralelo

    Aprende concurrent.futures en Python con threads, procesos, Future, timeouts, cancelación, backpressure y prevención de deadlocks.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    winsound en Python: audio en Windows

    Aprende winsound en Python para reproducir WAV, sonidos del sistema, beeps, loops y notificaciones asíncronas de forma segura en Windows.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    winreg en Python: Registro de Windows

    Aprende winreg en Python para leer y escribir el Registro de Windows, gestionar tipos, permisos, vistas WOW64, eliminaciones y seguridad.

    Ler mais

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

    posix en Python: llamadas Unix directas

    Entiende posix en Python, llamadas Unix, descriptores, permisos, procesos, seguridad y cuándo usar os en lugar del módulo directo.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    A male software engineer working on code in a modern office setting.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    curses en Python: interfaces de terminal

    Aprende curses en Python para crear interfaces de terminal con ventanas, colores, teclado, resize, Unicode y cleanup seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026