Think of a server socket as a front desk for a network service: it binds to an address and port, listens for visitors, and waits for clients to arrive. Calling accept() pauses the server until a connection appears. It then returns a new socket for communicating with that client, while the listening socket remains ready for others. section 10.4
A basic server socket follows this sequence:
- Bind the socket to an address such as 127.0.0.1 and a port such as 12345. section 10.4
- Listen for incoming client connections. section 10.4
- Call accept() and receive a connected socket plus the client address. section 10.4
- Read requests and send responses through the connected socket. section 10.4
Pizza-ordering service
A local pizza server listens on 127.0.0.1:12345. Netcat connects as a client, sends 10, and receives “Thank you for ordering 10 pizzas!” The server’s terminal appears hung while accept() waits, then reports the client connection and closes it after communication. section 10.4
One listening server socket handles clients sequentially unless the design adds concurrency. A threaded server keeps the listening socket in the main thread and gives each client connection to a separate thread. An asynchronous server instead makes socket operations nonblocking and lets an event loop resume them when I/O is ready. section 10.6 section 12.8
# Chapter 10/pizza_server.py
from socket import socket, create_server
BUFFER_SIZE = 1024 #1
ADDRESS = ("127.0.0.1", 12345) #2
class Server:
def __init__(self) -> None:
try:
print(f"Starting up at: {ADDRESS}")
self.server_socket: socket = create_server(ADDRESS) #3
except OSError:
self.server_socket.close()
print("\nServer stopped.")
def accept(self) -> socket:
conn, client_address = self.server_socket.accept() #4
print(f"Connected to {client_address}")
return conn
def serve(self, conn: socket) -> None:
try:
while True:
data = conn.recv(BUFFER_SIZE) #5
if not data: #5
break #5
try:
order = int(data.decode())
response = f"Thank you for ordering {order} pizzas!\n"
except ValueError:
response = "Wrong number of pizzas, please try again\n"
print(f"Sending message to {conn.getpeername()}")
conn.send(response.encode()) #6
finally:
print(f"Connection with {conn.getpeername()} has been closed")
conn.close() #7
def start(self) -> None:
print("Server listening for incoming connections")
try: #8
while True: #8
conn = self.accept() #8
self.serve(conn) #8
finally: #8
self.server_socket.close() #8
print("\nServer stopped.") #8
if __name__ == "__main__":
server = Server()
server.start()
Browse a compact Python server implementation showing create_server(), accept(), receiving data, and the listening-versus-connected socket distinction. section 10.4
