A production ready protocol library for tcp, websocket and telnet servers
Project description
CHUK Protocol Server
A modern Python framework for building server applications that work across multiple transport protocols: Telnet, TCP, and WebSocket. Create a server once and make it accessible from traditional terminal clients, command-line tools, and web browsers.
Features
- Multiple Transport Protocols: Run your server on Telnet, TCP, and WebSocket simultaneously
- Unified Handler Interface: Write your application logic once, deploy everywhere
- Easy CLI Tools: Launch servers with simple commands like
guess-who-serverorsample-server echo_server - Sample Servers Included: Get started immediately with Echo and Guess Who game servers
- Configurable: YAML-based configuration with transport-specific options
- Graceful Shutdown: Proper connection handling and clean termination
- Protocol Detection: Automatic Telnet negotiation detection with fallback
- Session Management: Connection limits, timeouts, and custom welcome messages
- Async Architecture: Built on Python's asyncio for efficient handling of concurrent connections
- Proper Telnet Protocol Implementation: Full support for telnet option negotiation and subnegotiation
- Dual-Mode Operation: Supports both line mode and character mode terminal handling
- Robust Error Handling: Graceful handling of connection issues and unexpected client behavior
- Session Monitoring: Optional monitoring of WebSocket sessions for debugging and analysis
- uvx Support: Run without installation using
uvx chuk-protocol-server
Requirements
- Python 3.7+
- Dependencies:
- websockets
- pyyaml
- asyncio
Installation
# Install from PyPI
pip install chuk-protocol-server
# Or install from source
git clone https://github.com/yourusername/chuk-protocol-server.git
cd chuk-protocol-server
pip install -e .
# Optional: Install development dependencies
pip install -e ".[dev]"
After installation, the following CLI commands will be available:
chuk-protocol-server- Main CLI dispatcher for all commandssample-server- Generic launcher for sample servers and custom configsguess-who-server- Quick launch for the Guess Who game serverecho-server- Quick launch for the Echo serverserver-launcher- Low-level server launcher with advanced options
No installation needed? Use uvx to run directly:
# Launch with the main dispatcher
uvx chuk-protocol-server guess-who-server
uvx chuk-protocol-server sample-server echo_server
# Or use individual commands
uvx --from chuk-protocol-server guess-who-server
Quick Start
Try the Sample Servers
Get started immediately with our built-in sample servers:
# Launch the Guess Who game server
uv run chuk-protocol-server guess-who-server
# Launch the Echo server
uv run chuk-protocol-server echo-server
# Or use the generic launcher
uv run chuk-protocol-server sample-server guess_who_server
uv run chuk-protocol-server sample-server echo_server
# List all available sample servers
uv run chuk-protocol-server sample-server --list
No installation needed? Try with uvx:
uvx chuk-protocol-server guess-who-server
uvx chuk-protocol-server echo-server
Create Your Own Server
- Create a handler class that inherits from one of the base handlers
- Configure your server with a YAML file
- Launch your server
# Launch with a custom config
uv run chuk-protocol-server sample-server -c path/to/your/config.yaml
# Or use the low-level launcher
uv run chuk-protocol-server server-launcher -c path/to/your/config.yaml
CLI Commands
The framework provides several CLI commands for launching servers:
chuk-protocol-server - Main CLI Dispatcher
The main command that provides access to all functionality:
# Quick launch sample servers
chuk-protocol-server guess-who-server
chuk-protocol-server echo-server
# Launch any sample server by name
chuk-protocol-server sample-server echo_server
chuk-protocol-server sample-server guess_who_server
# Launch with custom config
chuk-protocol-server sample-server -c /path/to/config.yaml
# List available sample servers
chuk-protocol-server sample-server --list
# Low-level launcher
chuk-protocol-server server-launcher -c config.yaml -vv
Using with uvx (run without installation):
# Run directly from PyPI - no installation needed!
uvx chuk-protocol-server guess-who-server
uvx chuk-protocol-server sample-server echo_server
uvx chuk-protocol-server sample-server -c ./config.yaml
Individual Commands
You can also use individual commands directly:
# Quick shortcuts
guess-who-server
echo-server
# Generic launcher
sample-server echo_server
sample-server -c /path/to/config.yaml
sample-server --list
# Low-level launcher
server-launcher -c config/my_server.yaml -vv
With uvx:
uvx --from chuk-protocol-server guess-who-server
uvx --from chuk-protocol-server echo-server
Client Connections
Your server will be accessible from multiple client types:
Telnet Client
Connect to the Telnet transport using a traditional Telnet client:
telnet localhost 8023
The server will negotiate Telnet options and provide a full-featured terminal experience.
TCP Client (netcat)
Connect to the TCP transport using netcat or similar tools:
nc localhost 8024
This provides a simple line-based interface without Telnet negotiation.
WebSocket Client
Connect to the WebSocket transport using any WebSocket client:
Command-line with websocat
# Install websocat if needed (https://github.com/vi/websocat)
websocat --exit-on-eof ws://localhost:8025/ws
Browser JavaScript
const ws = new WebSocket('ws://localhost:8025/ws');
ws.onmessage = function(event) { console.log('Received:', event.data); };
ws.onopen = function() { console.log('Connected!'); };
ws.onclose = function() { console.log('Disconnected'); };
// Send a message
ws.send('hello');
Configuration
The framework uses YAML configuration files for server setup:
# Single server configuration
host: 0.0.0.0
port: 8023
transport: telnet
handler_class: sample_servers.echo_server:EchoTelnetHandler
# OR
# Multi-transport configuration
servers:
telnet:
host: "0.0.0.0"
port: 8023
transport: "telnet"
handler_class: "sample_servers.echo_server:EchoTelnetHandler"
max_connections: 100
connection_timeout: 300
welcome_message: "Welcome to the Telnet Server!"
tcp:
host: "0.0.0.0"
port: 8024
transport: "tcp"
handler_class: "sample_servers.echo_server:EchoTelnetHandler"
max_connections: 100
connection_timeout: 300
welcome_message: "Welcome to the TCP Server!"
websocket:
host: "0.0.0.0"
port: 8025
transport: "websocket"
ws_path: "/ws"
handler_class: "sample_servers.echo_server:EchoTelnetHandler"
use_ssl: false
allow_origins:
- "*"
max_connections: 100
connection_timeout: 300
welcome_message: "Welcome to the WebSocket Server!"
enable_monitoring: true
monitor_path: "/monitor"
Configuration Options
| Option | Description | Default |
|---|---|---|
| host | Bind address | 0.0.0.0 |
| port | Listen port | 8023 |
| transport | Protocol type (telnet, tcp, websocket, ws_telnet) | telnet |
| handler_class | Handler class path (module:ClassName) | Required |
| max_connections | Maximum concurrent connections | 100 |
| connection_timeout | Session timeout in seconds | 300 |
| welcome_message | Message displayed on connection | None |
| ws_path | Path for WebSocket endpoint | /ws |
| allow_origins | CORS allowed origins | ["*"] |
| use_ssl | Enable SSL/TLS (WebSocket) | false |
| ssl_cert | Path to SSL certificate | None |
| ssl_key | Path to SSL key | None |
| ping_interval | WebSocket ping interval in seconds | 30 |
| ping_timeout | WebSocket ping timeout in seconds | 10 |
| enable_monitoring | Enable session monitoring (WebSocket) | false |
| monitor_path | Path for monitoring endpoint | /monitor |
Creating Handlers
Handlers define your server's behavior. Extend one of the base handler classes:
#!/usr/bin/env python3
from chuk_protocol_server.handlers.telnet_handler import TelnetHandler
class EchoTelnetHandler(TelnetHandler):
async def on_command_submitted(self, command: str) -> None:
if command.lower() == 'help':
await self.send_line("Available commands: help, info, quit")
else:
await self.send_line(f"Echo: {command}")
async def process_line(self, line: str) -> bool:
if line.lower() in ['quit', 'exit', 'q']:
await self.end_session("Goodbye!")
return False
await self.on_command_submitted(line)
await self.show_prompt()
return True
For more advanced customization, override additional methods:
class AdvancedHandler(TelnetHandler):
def __init__(self, reader, writer):
super().__init__(reader, writer)
self.custom_state = {}
async def show_prompt(self) -> None:
# Customize the prompt
await self.send_raw(b"my-app> ")
async def process_character(self, char: str) -> bool:
# Custom character processing
# Return False to terminate the connection
return await super().default_process_character(char)
Handler Classes
The framework provides several handler classes that you can extend:
- BaseHandler: Basic connection handling with raw I/O
- CharacterHandler: Character-by-character processing for interactive applications
- LineHandler: Line-by-line processing for simpler command interfaces
- TelnetHandler: Full telnet protocol support with negotiation
Choose the appropriate base class based on your application's needs.
Architecture
The framework is built on a layered architecture:
- Servers: Handle transport-specific connection management (Telnet, TCP, WebSocket)
- Handlers: Process client input and generate responses
- Adapters: Bridge between different transport types and handlers
This modular design allows for:
- Transport Layer: Manages network protocols and connections
- Character Protocol Layer: Character-by-character reading and processing
- Telnet Protocol Layer: Telnet-specific protocol handling and negotiation
- Application Logic Layer: Custom application behavior
Running the Server
Quick Launch (Recommended)
Use the main CLI dispatcher:
# Launch built-in sample servers
uv run chuk-protocol-server guess-who-server
uv run chuk-protocol-server echo-server
# Launch by name
uv run chuk-protocol-server sample-server guess_who_server
# Launch your custom server
uv run chuk-protocol-server sample-server -c config/my_server.yaml
# With verbose logging
uv run chuk-protocol-server server-launcher -c config/my_server.yaml -vv
Or use individual commands:
uv run guess-who-server
uv run echo-server
uv run sample-server echo_server
uv run sample-server -c config/my_server.yaml
Using uvx (No Installation Required)
The easiest way to try it out:
# Run directly from PyPI - no installation needed!
uvx chuk-protocol-server guess-who-server
uvx chuk-protocol-server echo-server
uvx chuk-protocol-server sample-server guess_who_server
uvx chuk-protocol-server sample-server -c ./my_config.yaml
Traditional Python Module Invocation
# With a configuration file
python -m chuk_protocol_server.server_launcher -c config/my_server.yaml
# Verbose logging
python -m chuk_protocol_server.server_launcher -c config/my_server.yaml -vv
Example Handlers
Here are examples of handlers for different use cases:
Echo Server Handler
from chuk_protocol_server.handlers.telnet_handler import TelnetHandler
class EchoHandler(TelnetHandler):
async def on_command_submitted(self, command: str) -> None:
await self.send_line(f"Echo: {command}")
Command Processor Handler
from chuk_protocol_server.handlers.line_handler import LineHandler
class CommandHandler(LineHandler):
def __init__(self, reader, writer):
super().__init__(reader, writer)
self.commands = {
"help": self.cmd_help,
"status": self.cmd_status,
"uptime": self.cmd_uptime
}
async def process_line(self, line: str) -> bool:
if not line.strip():
await self.show_prompt()
return True
parts = line.split(maxsplit=1)
cmd = parts[0].lower()
args = parts[1] if len(parts) > 1 else ""
if cmd in ["quit", "exit"]:
await self.end_session("Goodbye!")
return False
if cmd in self.commands:
await self.commands[cmd](args)
else:
await self.send_line(f"Unknown command: {cmd}")
await self.show_prompt()
return True
async def cmd_help(self, args):
await self.send_line("Available commands: help, status, uptime, quit")
async def cmd_status(self, args):
await self.send_line("Server status: OK")
async def cmd_uptime(self, args):
await self.send_line("Server uptime: 3 days, 4 hours")
Terminal Handling
The framework includes sophisticated terminal handling that correctly negotiates capabilities with telnet clients:
- Initial Negotiation: Establishes proper terminal settings at connection time
- Visual Feedback: Echoes characters and provides appropriate visual feedback
- Control Character Handling: Properly processes CR, LF, backspace, and other control characters
- Window Size: Adapts to client terminal dimensions when available
- Terminal Type: Detects client terminal type for specialized behavior
Session Monitoring
For WebSocket servers, you can enable session monitoring to observe client interactions:
websocket:
# ... other options ...
enable_monitoring: true
monitor_path: "/monitor"
Connect to the monitoring endpoint with a WebSocket client:
// Monitor all sessions
const monitor = new WebSocket('ws://localhost:8025/monitor');
monitor.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Session event:', data);
};
// Watch a specific session
monitor.send(JSON.stringify({
type: 'watch_session',
session_id: 'SESSION_ID'
}));
Extending the Framework
Adding a New Transport
- Create a new server class extending
BaseServer - Implement required methods for your transport
- Add the transport type to the server launcher
Creating Custom Handlers
- Extend one of the base handler classes (BaseHandler, CharacterHandler, LineHandler, TelnetHandler)
- Implement your application logic
- Configure the server to use your handler
Adding Custom Sample Servers
To add your own sample server to the framework:
- Create a directory under
src/chuk_protocol_server/sample_servers/ - Add your handler implementation in
server.py - Create a
config.yamlfile with your server configuration - The server will automatically be available via
sample-server <your_server_name>
Example structure:
src/chuk_protocol_server/sample_servers/
└── my_game_server/
├── __init__.py
├── server.py # Your handler class
└── config.yaml # Server configuration
Optionally, create a convenience launcher:
# src/chuk_protocol_server/my_game_launcher.py
from chuk_protocol_server.sample_server_launcher import launch_sample_server
def main():
launch_sample_server("my_game_server")
Then add it to pyproject.toml:
[project.scripts]
my-game-server = "chuk_protocol_server.my_game_launcher:main"
Logging
The framework includes comprehensive logging to assist with debugging:
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
Different components use different loggers:
chuk-protocol-server: Main logger for server eventsbase-server: Server lifecycle eventscharacter-handler: Character processing eventswebsocket-adapter: WebSocket connection eventsws-plain-server: WebSocket server events
Performance Considerations
- The server uses asyncio for efficient handling of multiple connections
- Character-by-character processing is more CPU-intensive than line mode
- Connection cleanup is handled carefully to prevent resource leaks
- WebSocket connections include ping/pong frames to detect disconnects
Troubleshooting
Common issues and solutions:
- ^M characters visible: Check that proper terminal negotiations are being sent
- No character echo: Verify ECHO option negotiation
- Slow performance: Reduce logging level in production environments
- Connection resets: Ensure proper error handling in custom handlers
- WebSocket client doesn't exit: Use the
--exit-on-eofflag with websocat
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
Acknowledgements
- Telnet protocol specifications (RFCs 854, 855, 856, etc.)
- The asyncio library for elegant async/await support
- The websockets library for WebSocket protocol support
- The Python community for inspiration and feedback
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chuk_protocol_server-0.2-py3-none-any.whl.
File metadata
- Download URL: chuk_protocol_server-0.2-py3-none-any.whl
- Upload date:
- Size: 87.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1ff9b6349a46871d4db1003cc37609b246e1c3811de36d7c0198c2975f0f9f7
|
|
| MD5 |
cf2276eb8debc2388e6e82266a8727dd
|
|
| BLAKE2b-256 |
5f81cf6990568749b1367b1d6cbb54bad508b689a6b13322ac981f61403083f6
|