Python's socket module provides low-level access to network interfaces. While most projects use higher-level libraries like requests, understanding sockets helps debug networking issues and build custom protocols.
TCP Server Basics
import socket
# Create TCP socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Allow address reuse (avoid "address already in use")
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind to address and port
server.bind(('localhost', 9999))
# Listen for connections (backlog of 5)
server.listen(5)
print("Server listening on port 9999...")
while True:
# Accept connection
client, address = server.accept()
print(f"Connection from {address}")
# Receive data
data = client.recv(1024)
print(f"Received: {data.decode()}")
# Send response
client.send(b"Hello from server!")
# Close connection
client.close()TCP Client
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 9999))
# Send data
client.send(b"Hello from client!")
# Receive response
response = client.recv(1024)
print(f"Server said: {response.decode()}")
client.close()Context Managers
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect(('localhost', 9999))
sock.send(b"Hello!")
response = sock.recv(1024)Socket closes automatically when leaving the with block.
UDP (Connectionless)
# UDP Server
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('localhost', 9999))
while True:
data, addr = server.recvfrom(1024)
print(f"From {addr}: {data.decode()}")
server.sendto(b"Got it!", addr)# UDP Client
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.sendto(b"Hello UDP!", ('localhost', 9999))
response, _ = client.recvfrom(1024)
print(response.decode())Handling Multiple Clients
Using select for non-blocking I/O:
import socket
import select
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('localhost', 9999))
server.listen(5)
server.setblocking(False)
sockets = [server]
while True:
readable, _, _ = select.select(sockets, [], [], 1)
for sock in readable:
if sock is server:
# New connection
client, addr = server.accept()
client.setblocking(False)
sockets.append(client)
print(f"New connection from {addr}")
else:
# Data from existing client
try:
data = sock.recv(1024)
if data:
print(f"Received: {data.decode()}")
sock.send(b"Echo: " + data)
else:
# Empty data means disconnect
sockets.remove(sock)
sock.close()
except ConnectionResetError:
sockets.remove(sock)
sock.close()Timeouts
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0) # 5 second timeout
try:
sock.connect(('example.com', 80))
sock.send(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
response = sock.recv(4096)
except socket.timeout:
print("Connection timed out")
finally:
sock.close()Getting Host Information
import socket
# Hostname to IP
ip = socket.gethostbyname('example.com')
print(f"IP: {ip}")
# Full DNS lookup
info = socket.getaddrinfo('example.com', 80)
for item in info:
print(item)
# Get local hostname
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"Local: {hostname} ({local_ip})")Simple HTTP Request
import socket
def http_get(host, path='/'):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((host, 80))
request = f"GET {path} HTTP/1.1\r\n"
request += f"Host: {host}\r\n"
request += "Connection: close\r\n\r\n"
sock.send(request.encode())
response = b""
while True:
chunk = sock.recv(4096)
if not chunk:
break
response += chunk
return response.decode()
html = http_get('example.com')
print(html[:500])Binary Data
import socket
import struct
# Pack data into binary format
data = struct.pack('!HH', 1234, 5678) # Two unsigned shorts
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect(('localhost', 9999))
sock.send(data)
response = sock.recv(4)
val1, val2 = struct.unpack('!HH', response)IPv6
import socket
# IPv6 socket
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.connect(('::1', 9999)) # localhost IPv6Common Socket Options
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Reuse address
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Keep connection alive
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# Set send/receive buffer sizes
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
# Disable Nagle's algorithm (send immediately)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)Error Handling
import socket
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('nonexistent.example', 80))
except socket.gaierror:
print("DNS lookup failed")
except socket.timeout:
print("Connection timed out")
except ConnectionRefusedError:
print("Connection refused")
except socket.error as e:
print(f"Socket error: {e}")
finally:
sock.close()Summary
The socket module is Python's interface to network programming fundamentals. Use TCP (SOCK_STREAM) for reliable connections, UDP (SOCK_DGRAM) for speed without guarantees. While you'll typically use higher-level libraries, understanding sockets helps when debugging or building custom protocols.
React to this post: