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 object

Threaded 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 overhead

Stream 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: