High-performance WebSockets for Flask apps powered by uWSGI and gevent
Project description
Flask-GWS
A high-performance WebSocket extension for Flask applications powered by uWSGI and gevent. Compatible with Python 3.7 and newer versions. Production-ready with focus on reliability and performance.
Example Usage
from flask import Flask
from flask_gws import WebSocket
app = Flask(__name__)
ws = WebSocket(app)
@ws.route('/echo')
def echo(ws):
while True:
msg = ws.receive()
if msg is None:
break
ws.send(msg)
if __name__ == '__main__':
app.run(debug=True, gevent=100)
Installation
Flask-GWS requires both gevent and uWSGI with WebSocket support. While gevent is automatically installed as a dependency, uWSGI requires special handling:
⚠️ IMPORTANT NOTE: Installing uWSGI with WebSocket support requires special flags for OpenSSL. The traditional method via requirements.txt (pip install uwsgi) will not work as it lacks SSL support needed for WebSockets. You must use the commands below with the specific compilation flags.
For Ubuntu/Debian:
CFLAGS="-I/usr/include/openssl" LDFLAGS="-L/usr/lib/x86_64-linux-gnu" UWSGI_PROFILE_OVERRIDE=ssl=true pip install --no-cache-dir uwsgi --no-binary :all:
For macOS (Apple Silicon):
CFLAGS="-I/opt/homebrew/opt/openssl@3/include" \
LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib" \
UWSGI_PROFILE_OVERRIDE=ssl=true pip install --no-cache-dir uwsgi --no-binary :all:
After installing uWSGI with the proper SSL support, you can install Flask-GWS:
pip install flask-gws
Advanced Usage: Client Tracking and Broadcasting
Flask-GWS can be used to track connected clients and implement broadcasting functionality:
from flask import Flask
from flask_gws import WebSocket
import time
app = Flask(__name__)
ws = WebSocket(app)
# Store connected clients
clients = {}
@ws.route('/echo')
def echo(ws):
# Store client using unique ID
client_id = ws.id
clients[client_id] = {
'ws': ws,
'connected_at': time.time()
}
print(f"New client connected: {client_id}, Total clients: {len(clients)}")
try:
while ws.connected:
msg = ws.receive()
if msg:
ws.send(msg)
time.sleep(0.1)
except Exception as e:
print(f"Error in websocket: {e}")
finally:
# Clean up when client disconnects
if client_id in clients:
print(f"Client disconnected: {client_id}")
clients.pop(client_id, None)
print(f"Total remaining clients: {len(clients)}")
# Broadcasting utility function
def broadcast(msg):
disconnected = []
for client_id, client_info in clients.items():
try:
client_info['ws'].send(msg)
except Exception as e:
print(f"Error broadcasting to client {client_id}: {e}")
disconnected.append(client_id)
# Clean up any disconnected clients
for client_id in disconnected:
clients.pop(client_id, None)
# Example route that triggers a broadcast
@app.route('/notify')
def notify():
broadcast("New notification for all clients")
return "Notification sent to all connected clients"
Client-Side Implementation
Here's a simple but robust client implementation using ReconnectingWebSocket:
<!-- Include the ReconnectingWebSocket library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.min.js"></script>
<div>
<div id="status">Connecting...</div>
<div id="messages"></div>
</div>
<script>
// WebSocket client with automatic reconnection
const messagesEl = document.getElementById('messages');
const statusEl = document.getElementById('status');
function addMessage(text) {
const messageEl = document.createElement('div');
messageEl.textContent = text;
messagesEl.appendChild(messageEl);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function connect() {
// Convert HTTP URL to WebSocket URL
const url_root = window.location.origin;
const server = url_root.replace('http', 'ws') + '/echo';
// Create a reconnecting WebSocket
window.socket = new ReconnectingWebSocket(server, [], {
connectionTimeout: 2000,
maxRetries: 15
});
socket.onopen = function() {
statusEl.textContent = 'Connected';
statusEl.style.color = 'green';
addMessage('Connection established');
};
socket.onmessage = function(message) {
addMessage('Received: ' + message.data);
// Special command handling
if (message.data === 'reload') {
window.location.reload();
}
};
socket.onclose = function() {
statusEl.textContent = 'Disconnected (reconnecting...)';
statusEl.style.color = 'orange';
};
socket.onerror = function() {
statusEl.textContent = 'Connection error';
statusEl.style.color = 'red';
};
}
// Initialize the connection
connect();
// Example function to send a message
function sendMessage(text) {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(text);
addMessage('Sent: ' + text);
} else {
addMessage('Cannot send: connection not open');
}
}
</script>
Deployment
You can use uWSGI's built-in HTTP router to get up and running quickly:
$ uwsgi --master --http :8080 --http-websockets --gevent 100 --wsgi-file app.py
...or call app.run, passing uwsgi any arguments you like:
app.run(debug=True, host='localhost', port=8080, master=True, gevent=100)
Development
To use Flask's interactive debugger, install werkzeug's DebuggedApplication middleware:
from werkzeug.debug import DebuggedApplication
app.wsgi_app = DebuggedApplication(app.wsgi_app, True)
Then run uWSGI with a single worker:
$ uwsgi --master --http :8080 --http-websockets --gevent 100 --workers 1 app.py
If you use app.run(debug=True), Flask-GWS will do this automatically.
WebSocket API
Flask-GWS handles the WebSocket handshake and provides a websocket client with the following methods:
websocket.recv()- Receive a message (blocking)websocket.receive()- Alias for recv()websocket.send(msg)- Send a messagewebsocket.send_binary(msg)- Send a binary messagewebsocket.recv_nb()- Non-blocking receivewebsocket.id- Unique identifier for the connectionwebsocket.connected- Boolean indicating if the connection is still active
Credits
This project is based on code from the Flask-uWSGI-WebSocket repository, with enhancements for improved performance and additional features. Flask-GWS provides a simplified and modernized implementation focused on gevent support and contemporary Python versions.
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 Distribution
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 flask_gws-0.1.0.tar.gz.
File metadata
- Download URL: flask_gws-0.1.0.tar.gz
- Upload date:
- Size: 9.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.32.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85b9f9b46f0d9fa57a070060d50fa9bd290f02c0b12d40f3eb02ae11c128739a
|
|
| MD5 |
48977aae128913804bc308e2d12dd111
|
|
| BLAKE2b-256 |
4fda072831e7cf0976d2f8bc6c9b1f28f1e4a783b8b9c5d2d6f037be74b2b9f2
|
File details
Details for the file flask_gws-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flask_gws-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.32.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c605dfaba8b964d4f9447dbdc77a425273832069f6f9ebee22b014a6979d8f8
|
|
| MD5 |
93d44222718badbc6868cb50a21d29a9
|
|
| BLAKE2b-256 |
1175d99baa34d73054c831ebb5888477f0a7a7fed18d318aa84a3ed7ca898254
|