Skip to main content
This example demonstrates how to build a simple multi-user chat server and client using repod. Users can set nicknames, send messages, and see who’s currently connected.

What This Example Demonstrates

  • Basic server/client communication patterns
  • Broadcasting messages to all connected clients
  • Managing player lists and connection events
  • Using Network_ methods to handle different message types
  • Running a client pump loop with threading for input handling

Complete Code

"""Chat server example using repod."""

from __future__ import annotations

import sys

from repod import Channel, Server


class ClientChannel(Channel["ChatServer"]):
    """Server-side channel for a connected chat client."""

    nickname: str

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.nickname = "Anonymous"

    def on_close(self) -> None:
        self.server.del_player(self)

    def Network_message(self, data: dict) -> None:
        self.server.broadcast(
            {
                "action": "message",
                "text": data["text"],
                "nickname": self.nickname,
            }
        )

    def Network_nickname(self, data: dict) -> None:
        self.nickname = data["nickname"]
        self.server.send_players()


class ChatServer(Server[ClientChannel]):
    """Chat server that manages connected clients."""

    channel_class = ClientChannel

    def __init__(self, host: str = "127.0.0.1", port: int = 5071) -> None:
        super().__init__(host, port)
        self.players: dict[ClientChannel, str] = {}

    def on_connect(self, channel: ClientChannel, addr: tuple[str, int]) -> None:
        self._add_player(channel)

    def _add_player(self, player: ClientChannel) -> None:
        self.players[player] = player.nickname
        print(f"Player connected: {player.addr}")
        self.broadcast(
            {
                "action": "system",
                "text": f"{player.nickname} joined the chat",
            }
        )
        self.send_players()

    def del_player(self, player: ClientChannel) -> None:
        """Remove a player and notify others."""
        if player in self.players:
            nickname = self.players.pop(player)
            print(f"Player disconnected: {player.addr}")
            self.broadcast(
                {
                    "action": "system",
                    "text": f"{nickname} left the chat",
                }
            )
            self.send_players()

    def send_players(self) -> None:
        """Broadcast the current player list."""
        self.broadcast(
            {
                "action": "players",
                "list": [p.nickname for p in self.players],
            }
        )

    def broadcast(self, data: dict) -> None:
        """Send a message to all connected players."""
        for player in self.players:
            player.send(data)


if __name__ == "__main__":
    host = "127.0.0.1"
    port = 5071

    if len(sys.argv) > 1:
        host = sys.argv[1]
    if len(sys.argv) > 2:
        port = int(sys.argv[2])

    print(f"Chat server running on {host}:{port}")
    print("Press Ctrl+C to stop")
    ChatServer(host, port).launch()

How to Run

1

Start the server

python examples/chat/server.py
Or specify a custom host and port:
python examples/chat/server.py 0.0.0.0 8080
2

Run one or more clients

In separate terminals:
python examples/chat/client.py
3

Chat away

Enter your nickname when prompted, then type messages to broadcast to all connected users.
The client uses a separate thread for reading user input to avoid blocking the pump() loop. This pattern is useful when integrating with game engines like pygame that need a continuous update loop.

Key Takeaways

  • Network methods: Define Network_<action> methods to automatically handle messages with {"action": "<action>"}
  • Broadcasting: Use a custom broadcast() method to send data to all connected clients
  • Player management: Track connected clients in a dictionary and clean up on disconnect via on_close()
  • Threading pattern: The client demonstrates how to use threading to handle input while maintaining a pump loop
  • Type safety: The Channel and Server classes use generic types for better IDE support

Build docs developers (and LLMs) love