Python's socketserver module provides a framework for building network servers without wrestling with raw sockets.
Basic TCP Server
import socketserver
class MyHandler(socketserver.BaseRequestHandler):
def handle(self):
# self.request is the TCP socket
data = self.request.recv(1024).strip()
print(f"Received: {data}")
# Send response
self.request.sendall(b"ACK: " + data)
if __name__ == "__main__":
with socketserver.TCPServer(("localhost", 9999), MyHandler) as server:
print("Server running on port 9999")
server.serve_forever()Handler Attributes
class MyHandler(socketserver.BaseRequestHandler):
def handle(self):
# Available attributes
print(self.client_address) # ('127.0.0.1', 54321)
print(self.server) # Reference to server instance
print(self.request) # The socket objectThreaded Server
Handle multiple clients concurrently:
import socketserver
import threading
class ThreadedTCPServer(socketserver.ThreadingMixIn,
socketserver.TCPServer):
allow_reuse_address = True
class Handler(socketserver.BaseRequestHandler):
def handle(self):
thread_name = threading.current_thread().name
data = self.request.recv(1024)
print(f"[{thread_name}] Handling {self.client_address}")
self.request.sendall(data.upper())
server = ThreadedTCPServer(("localhost", 9999), Handler)
with server:
server.serve_forever()Forking Server (Unix only)
Process-per-connection model:
import socketserver
class ForkingTCPServer(socketserver.ForkingMixIn,
socketserver.TCPServer):
pass
# Each connection gets its own process
# Better isolation, more overheadStream Handler for Line-Based Protocols
import socketserver
class LineHandler(socketserver.StreamRequestHandler):
def handle(self):
# self.rfile and self.wfile are file-like objects
while True:
line = self.rfile.readline()
if not line:
break
# Process line-by-line
response = line.decode().upper()
self.wfile.write(response.encode())
with socketserver.TCPServer(("localhost", 9999), LineHandler) as server:
server.serve_forever()Custom Server Configuration
import socketserver
class MyTCPServer(socketserver.TCPServer):
allow_reuse_address = True
timeout = 30 # Client timeout
def server_activate(self):
print("Server activated")
super().server_activate()
def handle_timeout(self):
print("Server timeout occurred")
class Handler(socketserver.BaseRequestHandler):
def handle(self):
self.request.settimeout(5) # Per-connection timeout
try:
data = self.request.recv(1024)
self.request.sendall(data)
except TimeoutError:
print(f"Client {self.client_address} timed out")UDP Server
Datagram-based communication:
import socketserver
class UDPHandler(socketserver.BaseRequestHandler):
def handle(self):
# UDP: self.request is (data, socket)
data, sock = self.request
print(f"Received: {data}")
# Send response back to client
sock.sendto(data.upper(), self.client_address)
with socketserver.UDPServer(("localhost", 9999), UDPHandler) as server:
server.serve_forever()Graceful Shutdown
import socketserver
import threading
import signal
class Handler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024)
self.request.sendall(data)
server = socketserver.ThreadingTCPServer(("localhost", 9999), Handler)
def shutdown_handler(signum, frame):
print("\nShutting down...")
server.shutdown()
signal.signal(signal.SIGINT, shutdown_handler)
signal.signal(signal.SIGTERM, shutdown_handler)
# Run in thread so signal handlers work
server_thread = threading.Thread(target=server.serve_forever)
server_thread.start()
server_thread.join()Request Queue Size
Control connection backlog:
import socketserver
class MyServer(socketserver.TCPServer):
request_queue_size = 100 # Default is 5
server = MyServer(("localhost", 9999), Handler)Echo Server Example
Complete working example:
import socketserver
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class EchoHandler(socketserver.StreamRequestHandler):
def setup(self):
logger.info(f"Connection from {self.client_address}")
super().setup()
def handle(self):
while True:
line = self.rfile.readline()
if not line or line.strip() == b"quit":
break
logger.info(f"Echo: {line.strip()}")
self.wfile.write(line)
def finish(self):
logger.info(f"Disconnected: {self.client_address}")
super().finish()
class ThreadedEchoServer(socketserver.ThreadingMixIn,
socketserver.TCPServer):
allow_reuse_address = True
daemon_threads = True # Exit cleanly
if __name__ == "__main__":
with ThreadedEchoServer(("0.0.0.0", 9999), EchoHandler) as server:
logger.info("Echo server running on port 9999")
try:
server.serve_forever()
except KeyboardInterrupt:
logger.info("Server stopped")Testing Your Server
import socket
def test_client():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(("localhost", 9999))
s.sendall(b"Hello, server!\n")
response = s.recv(1024)
print(f"Got: {response}")
test_client()When to Use socketserver
Good for:
- Protocol servers (chat, game servers)
- Internal services
- Simple request/response patterns
Consider alternatives for:
- High-performance needs → asyncio
- HTTP APIs → Flask, FastAPI
- Complex protocols → Twisted
The socketserver module hits the sweet spot between raw sockets and full frameworks—enough structure to be productive, enough flexibility to customize.
React to this post: