Los sockets son uno de los componentes clave en la comunicación en redes, permitiendo que las aplicaciones intercambien datos a través de una red local o global. Este artículo presenta los conceptos teóricos y ejemplos de código necesarios para comenzar a trabajar con sockets, enfocados principalmente en Python.
¿Qué es un socket?
Un socket es un punto final de comunicación entre dos sistemas. Se utiliza para enviar y recibir datos a través de redes usando protocolos como TCP (Protocolo de Control de Transmisión) y UDP (Protocolo de Datagrama de Usuario).
- TCP: Protocolo orientado a la conexión que garantiza la entrega de los datos en el orden correcto.
- UDP: Protocolo sin conexión que no garantiza la entrega de los datos, pero es más rápido.
Elementos Básicos de un Socket
- Dirección IP: Identifica el dispositivo en la red.
- Puerto: Define un punto específico para la comunicación.
- Protocolo: Determina cómo se transmiten los datos (TCP o UDP).
Modelo Cliente-Servidor
El modelo cliente-servidor es la base de muchas aplicaciones de red. En este modelo:
- El servidor escucha conexiones en un puerto específico y espera solicitudes.
- El cliente se conecta al servidor, envía solicitudes y recibe respuestas.
Código Base para un Cliente-Servidor TCP
Servidor Básico
Este servidor escucha en un puerto y responde a los mensajes del cliente convirtiéndolos a mayúsculas:
import socket
class BasicServer:
def __init__(self, host='127.0.0.1', port=65432):
self.host = host
self.port = port
def start_server(self):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((self.host, self.port))
s.listen()
print(f"Servidor escuchando en {self.host}:{self.port}")
conn, addr = s.accept()
with conn:
print(f"Conexión establecida con {addr}")
while True:
data = conn.recv(1024)
if not data:
break
response = data.decode('utf-8').upper()
conn.sendall(response.encode('utf-8'))
if __name__ == "__main__":
BasicServer().start_server()
Cliente Básico
El cliente se conecta al servidor y envía un mensaje:
import socket
class BasicClient:
def __init__(self, host='127.0.0.1', port=65432):
self.host = host
self.port = port
def start_client(self, message):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((self.host, self.port))
s.sendall(message.encode('utf-8'))
data = s.recv(1024)
print(f"Respuesta del servidor: {data.decode('utf-8')}")
if __name__ == "__main__":
BasicClient().start_client("Hola, servidor")
Servidor con Múltiples Clientes usando Hilos
Para manejar varias conexiones simultáneas:
import socket
import threading
class EchoServer:
def __init__(self, host='127.0.0.1', port=65432):
self.host = host
self.port = port
def handle_client(self, conn, addr):
print(f"Conexión establecida con {addr}")
with conn:
while True:
data = conn.recv(1024)
if not data or data.decode('utf-8').strip().upper() == 'QUIT':
break
conn.sendall(data.upper())
print(f"Conexión cerrada con {addr}")
def start_server(self):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((self.host, self.port))
s.listen()
print(f"Servidor de eco escuchando en {self.host}:{self.port}")
while True:
conn, addr = s.accept()
threading.Thread(target=self.handle_client, args=(conn, addr)).start()
if __name__ == "__main__":
EchoServer().start_server()
Servidor y Cliente UDP con Broadcast
Servidor UDP
El servidor escucha mensajes broadcast:
import socket
class UDPServer:
def __init__(self, port=65432):
self.port = port
def start_server(self):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.bind(('', self.port))
print(f"Servidor UDP escuchando en puerto {self.port}")
while True:
data, addr = s.recvfrom(1024)
print(f"Mensaje recibido de {addr}: {data.decode('utf-8')}")
s.sendto("Mensaje recibido".encode('utf-8'), addr)
if __name__ == "__main__":
UDPServer().start_server()
Cliente UDP
El cliente envía mensajes broadcast:
import socket
class UDPClient:
def __init__(self, broadcast_ip='255.255.255.255', port=65432):
self.broadcast_ip = broadcast_ip
self.port = port
def start_client(self, message):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(message.encode('utf-8'), (self.broadcast_ip, self.port))
s.settimeout(2.0)
try:
while True:
data, addr = s.recvfrom(1024)
print(f"Respuesta del servidor {addr}: {data.decode('utf-8')}")
except socket.timeout:
print("Tiempo de espera agotado.")
if __name__ == "__main__":
UDPClient().start_client("Mensaje de prueba")
Prácticas Recomendadas
- Usa siempre bloques
try/except
para manejar errores. - Configura direcciones IP y puertos de manera dinámica para pruebas.
- Documenta tu código para facilitar la comprensión.
Con esta teoría y ejemplos, estarás listo para trabajar con sockets en aplicaciones de red. La clave es practicar y experimentar con diferentes configuraciones y escenarios.