Simple WebSocket server and client for Python
—
Synchronous WebSocket implementation providing thread-based WebSocket server and client functionality. These classes handle WebSocket connections using traditional blocking I/O operations with background threads managing the connection lifecycle.
WebSocket server that accepts connections from WSGI environments. Automatically detects the web server type (Werkzeug, Gunicorn, Eventlet, Gevent) and extracts the underlying socket for WebSocket communication.
class Server:
def __init__(self, environ, subprotocols=None, receive_bytes=4096,
ping_interval=None, max_message_size=None, thread_class=None,
event_class=None, selector_class=None):
"""
Initialize WebSocket server from WSGI environ.
Parameters:
- environ: WSGI environ dictionary containing request details and socket
- subprotocols: List of supported subprotocols or None
- receive_bytes: Buffer size for receiving data (default: 4096)
- ping_interval: Ping interval in seconds or None to disable
- max_message_size: Maximum message size in bytes or None for no limit
- thread_class: Thread class to use (default: threading.Thread)
- event_class: Event class to use (default: threading.Event)
- selector_class: Selector class to use (default: selectors.DefaultSelector)
"""@classmethod
def accept(cls, environ, subprotocols=None, receive_bytes=4096,
ping_interval=None, max_message_size=None, thread_class=None,
event_class=None, selector_class=None):
"""
Accept WebSocket connection from client.
Parameters:
- environ: WSGI environ dictionary with request details and socket
- subprotocols: List of supported subprotocols or None to disable negotiation
- receive_bytes: Buffer size for receiving data (default: 4096)
- ping_interval: Send ping packets at this interval in seconds or None
- max_message_size: Maximum allowed message size in bytes or None
- thread_class: Thread class for background operations
- event_class: Event class for synchronization
- selector_class: Selector class for I/O multiplexing
Returns:
Server instance connected to the client
"""def send(self, data):
"""
Send data over the WebSocket connection.
Parameters:
- data: Data to send (str for text message, bytes for binary message)
"""
def receive(self, timeout=None):
"""
Receive data over the WebSocket connection.
Parameters:
- timeout: Timeout in seconds (None for indefinite, 0 for non-blocking)
Returns:
Received data as str or bytes depending on message type
"""
def close(self, reason=None, message=None):
"""
Close the WebSocket connection.
Parameters:
- reason: Numeric status code for closure (default: 1000 normal closure)
- message: Text message to send with closure
"""
def choose_subprotocol(self, request):
"""
Choose subprotocol for the WebSocket connection.
Parameters:
- request: Request object containing client's requested subprotocols
Returns:
Selected subprotocol name or None if no subprotocol chosen
"""WebSocket client that connects to WebSocket servers using ws:// or wss:// URLs. Handles SSL/TLS connections automatically and supports custom headers and subprotocol negotiation.
class Client:
def __init__(self, url, subprotocols=None, headers=None, receive_bytes=4096,
ping_interval=None, max_message_size=None, ssl_context=None,
thread_class=None, event_class=None):
"""
Initialize WebSocket client.
Parameters:
- url: WebSocket URL to connect to (ws:// or wss://)
- subprotocols: Subprotocol name or list of names to request
- headers: Additional HTTP headers as dict or list of tuples
- receive_bytes: Buffer size for receiving data (default: 4096)
- ping_interval: Ping interval in seconds or None to disable
- max_message_size: Maximum message size in bytes or None for no limit
- ssl_context: SSL context for secure connections or None for default
- thread_class: Thread class to use (default: threading.Thread)
- event_class: Event class to use (default: threading.Event)
"""@classmethod
def connect(cls, url, subprotocols=None, headers=None, receive_bytes=4096,
ping_interval=None, max_message_size=None, ssl_context=None,
thread_class=None, event_class=None):
"""
Create and connect WebSocket client.
Parameters:
- url: WebSocket URL (ws:// or wss://)
- subprotocols: Subprotocol name or list of preference-ordered names
- headers: Additional HTTP headers as dict or list of tuples
- receive_bytes: Buffer size for receiving data (default: 4096)
- ping_interval: Send ping packets at this interval in seconds or None
- max_message_size: Maximum allowed message size in bytes or None
- ssl_context: Custom SSL context or None for default secure context
- thread_class: Thread class for background operations
- event_class: Event class for synchronization
Returns:
Connected Client instance
"""def send(self, data):
"""
Send data over the WebSocket connection.
Parameters:
- data: Data to send (str for text message, bytes for binary message)
"""
def receive(self, timeout=None):
"""
Receive data over the WebSocket connection.
Parameters:
- timeout: Timeout in seconds (None for indefinite, 0 for non-blocking)
Returns:
Received data as str or bytes depending on message type
"""
def close(self, reason=None, message=None):
"""
Close the WebSocket connection and underlying socket.
Parameters:
- reason: Numeric status code for closure (default: 1000 normal closure)
- message: Text message to send with closure
"""import simple_websocket
def websocket_handler(environ, start_response):
# Accept WebSocket connection
ws = simple_websocket.Server.accept(environ)
try:
while True:
# Receive message from client
message = ws.receive(timeout=30) # 30 second timeout
if message is None:
break # Timeout occurred
# Echo message back to client
ws.send(f"Echo: {message}")
except simple_websocket.ConnectionClosed:
print("Client disconnected")
except simple_websocket.ConnectionError as e:
print(f"Connection error: {e}")
finally:
ws.close()import simple_websocket
try:
# Connect to WebSocket server
ws = simple_websocket.Client.connect("ws://localhost:8000/ws")
# Send message
ws.send("Hello, Server!")
# Receive response
response = ws.receive(timeout=10)
print(f"Server response: {response}")
except simple_websocket.ConnectionError as e:
print(f"Failed to connect: {e}")
except simple_websocket.ConnectionClosed:
print("Server closed connection")
finally:
ws.close()# Server with custom subprotocol selection
class MyServer(simple_websocket.Server):
def choose_subprotocol(self, request):
# Custom logic for subprotocol selection
if 'chat' in request.subprotocols:
return 'chat'
elif 'echo' in request.subprotocols:
return 'echo'
return None
# Accept connection with subprotocol support
ws = MyServer.accept(environ, subprotocols=['chat', 'echo'])
print(f"Negotiated subprotocol: {ws.subprotocol}")
# Client requesting specific subprotocols
ws = simple_websocket.Client.connect(
"ws://localhost:8000/ws",
subprotocols=['chat', 'echo']
)
print(f"Server selected subprotocol: {ws.subprotocol}")class Base:
subprotocol: str | None # Negotiated subprotocol name
connected: bool # Connection status
receive_bytes: int # Buffer size for receiving data
ping_interval: float | None # Ping interval in seconds
max_message_size: int | None # Maximum message size limitInstall with Tessl CLI
npx tessl i tessl/pypi-simple-websocket