liveBook · Manning

Serve_forever

As explained in Python Concurrency with asyncio

serve_forever is an asynchronous server method that continuously accepts and serves incoming client connections until the server is stopped.

serve_forever runs an asynchronous server continuously, accepting and serving client connections until the server is terminated. A server returned by start_server can be used with async with so it shuts down properly when execution exits the context. section 8.5

Use serve_forever as the long-running part of an asyncio server:

  • Pass a callback or coroutine to start_server; it receives a StreamReader and StreamWriter whenever a client connects. section 8.5
  • Await server.serve_forever() inside async with server to keep the service running and close it cleanly afterward. section 8.5 section 8.6

Chat server entry point

async def start_chat_server(self, host, port): server = await asyncio.start_server(self.client_connected, host, port) async with server: await server.serve_forever() The callback can authenticate clients, create message-listening tasks, and remove idle or disconnected users. section 8.6

Listing 8.12 Creating an echo server with server objects
import asyncio
import logging
from asyncio import StreamReader, StreamWriter
 
 
class ServerState:
 
    def __init__(self):
        self._writers = []
 
    async def add_client(self, reader: StreamReader, writer: StreamWriter):  #1
        self._writers.append(writer)
        await self._on_connect(writer)
        asyncio.create_task(self._echo(reader, writer))
 
    async def _on_connect(self, writer: StreamWriter):                       #2
        writer.write(f'Welcome! {len(self._writers)} user(s) are online!\n'.encode())
        await writer.drain()
        await self._notify_all('New user connected!\n')
 
    async def _echo(self, reader: StreamReader, writer: StreamWriter):       #3
        try:
            while (data := await reader.readline()) != b'':
                writer.write(data)
                await writer.drain()
            self._writers.remove(writer)
            await self._notify_all(f'Client disconnected. {len(self._writers)} user(s) are online!\n')
        except Exception as e:
            logging.exception('Error reading from client.', exc_info=e)
            self._writers.remove(writer)
 
    async def _notify_all(self, message: str):                               #4
        for writer in self._writers:
            try:
                writer.write(message.encode())
                await writer.drain()
            except ConnectionError as e:
                logging.exception('Could not write to client.', exc_info=e)
                self._writers.remove(writer)
 
 
async def main():
    server_state = ServerState()
 
    async def client_connected(reader: StreamReader, writer:                 #5
     StreamWriter) -> None:                                 
        await server_state.add_client(reader, writer)
 
    server = await asyncio.start_server(client_connected, '127.0.0.1', 8000) #6
 
    async with server:
        await server.serve_forever()
 
 
asyncio.run(main())

Inspect a complete echo-server pattern that combines serve_forever with connection state, client callbacks, and StreamWriter management. section 8.5

How Python Concurrency with asyncio covers it

How deep it goes
Explains server lifetime, callbacks, and asynchronous cleanup, but defers low-level implementation details.
Examples
Uses echo and chat servers with connection notifications, message broadcasting, disconnection handling, and idle-client removal.
What you can do after
Operate/configure an asyncio server that runs continuously and shuts down through asynchronous context management.
How it connects
Connects server lifetime with asyncio.start_server, asynchronous context managers, StreamReader, StreamWriter, and client tasks.

This explainer was generated by Manning's knowledge graph from the text of Python Concurrency with asyncio. The section links point to the parts of the book it draws on.