Flask SocketIO Basics
Introduction
WebSockets provide a persistent connection between a client and server, allowing for real-time, bidirectional communication. Flask-SocketIO is an extension for Flask that makes it easy to integrate WebSocket functionality into your web applications. This enables you to build interactive features like live notifications, chat applications, real-time analytics dashboards, and collaborative tools.
In this tutorial, we'll explore the fundamentals of Flask-SocketIO and learn how to create your first real-time application.
What is Flask-SocketIO?
Flask-SocketIO is a Flask extension that provides WebSocket support for Flask applications using the Socket.IO protocol. Socket.IO is a JavaScript library that enables real-time, bidirectional communication between web clients and servers. It primarily uses WebSocket but can fall back to other methods like long polling when WebSocket isn't available.
Key advantages of Flask-SocketIO:
- Bidirectional communication: Server can push data to clients without clients requesting it
- Real-time updates: Changes propagate instantly to connected users
- Fallback options: Works even when WebSockets aren't supported
- Room-based messaging: Supports grouping connections for targeted messaging
- Easy integration: Works seamlessly with existing Flask applications
Setting Up Flask-SocketIO
Installation
First, let's install Flask-SocketIO using pip:
pip install flask-socketio
You may also want to install eventlet or gevent for better performance in production:
pip install eventlet
# or
pip install gevent
Basic Setup
Here's a minimal Flask-SocketIO application:
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
socketio = SocketIO(app)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
socketio.run(app, debug=True)
Notice the differences from a standard Flask application:
- We import
SocketIO
fromflask_socketio
- We create a SocketIO instance with our Flask app
- Instead of
app.run()
, we usesocketio.run()
to start the server
Creating Your First Real-Time Application
Let's create a simple real-time message broadcasting application. When a user sends a message, all connected clients will see it instantly.
Server-Side (Python)
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
socketio = SocketIO(app, cors_allowed_origins="*")
@app.route('/')
def index():
return render_template('index.html')
@socketio.on('message')
def handle_message(data):
print('Received message: ' + data)
emit('message', data, broadcast=True)
if __name__ == '__main__':
socketio.run(app, debug=True)
Client-Side (HTML/JavaScript)
Create a templates folder and add an index.html
file:
<!DOCTYPE html>
<html>
<head>
<title>Flask-SocketIO Chat</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
// Connect to SocketIO server
const socket = io();
// Handle form submission
document.getElementById('message-form').addEventListener('submit', (e) => {
e.preventDefault();
const input = document.getElementById('message-input');
const message = input.value;
if (message) {
// Send message to server
socket.emit('message', message);
input.value = '';
}
});
// Listen for messages from server
socket.on('message', (message) => {
const messagesDiv = document.getElementById('messages');
const messageItem = document.createElement('div');
messageItem.textContent = message;
messagesDiv.appendChild(messageItem);
// Auto-scroll to the bottom
messagesDiv.scrollTop = messagesDiv.scrollHeight;
});
});
</script>
<style>
#messages {
height: 300px;
overflow-y: auto;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<h1>Flask-SocketIO Chat</h1>
<div id="messages"></div>
<form id="message-form">
<input id="message-input" type="text" placeholder="Type a message..." autocomplete="off">
<button type="submit">Send</button>
</form>
</body>
</html>
Running the Application
Run your Flask application:
python app.py
Open your browser to http://127.0.0.1:5000/
and you should see the chat interface. Try opening multiple browser windows to see messages appear in real-time across all clients!
Understanding SocketIO Events
SocketIO is event-based, which means communication happens through named events. There are several types of events you can use:
Handling Connect and Disconnect Events
@socketio.on('connect')
def handle_connect():
print('Client connected')
@socketio.on('disconnect')
def handle_disconnect():
print('Client disconnected')
Custom Events
You can create custom events for different types of interactions:
@socketio.on('user_joined')
def handle_user_joined(username):
emit('announcement', f'{username} has joined the chat', broadcast=True)
On the client side:
// Emit an event
socket.emit('user_joined', 'Alice');
// Listen for an event
socket.on('announcement', (message) => {
console.log(message); // "Alice has joined the chat"
});
Namespaces and Rooms
SocketIO provides namespaces and rooms to organize connections and messages:
Namespaces
Namespaces allow you to separate concerns in your application:
@socketio.on('message', namespace='/chat')
def handle_chat_message(data):
emit('message', data, namespace='/chat', broadcast=True)
On the client:
const chatSocket = io('/chat');
chatSocket.emit('message', 'Hello from the chat namespace');
Rooms
Rooms let you broadcast to specific groups of clients:
from flask_socketio import join_room, leave_room, emit
@socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
emit('message', f'{username} has joined the room {room}', to=room)
@socketio.on('leave')
def on_leave(data):
username = data['username']
room = data['room']
leave_room(room)
emit('message', f'{username} has left the room {room}', to=room)
@socketio.on('room_message')
def room_message(data):
emit('message', data['message'], to=data['room'])
Client-side code to join a room:
socket.emit('join', {username: 'Alice', room: 'python'});
// Send a message to the room
socket.emit('room_message', {room: 'python', message: 'Hello Python developers!'});
Real-World Example: Collaborative Drawing Application
Let's create a simple collaborative drawing board to demonstrate the power of Flask-SocketIO:
Server-Side Code
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
socketio = SocketIO(app, cors_allowed_origins="*")
@app.route('/')
def index():
return render_template('drawing.html')
@socketio.on('draw_line')
def handle_draw_line(data):
emit('draw_line', data, broadcast=True, include_self=False)
if __name__ == '__main__':
socketio.run(app, debug=True)
Client-Side (HTML/JavaScript)
Create a drawing.html
file in your templates folder:
<!DOCTYPE html>
<html>
<head>
<title>Collaborative Drawing Board</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<style>
#canvas {
border: 1px solid black;
background-color: white;
}
body {
margin: 20px;
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Collaborative Drawing Board</h1>
<p>Draw something! Everyone connected will see your drawing in real-time.</p>
<canvas id="canvas" width="800" height="600"></canvas>
<script>
document.addEventListener('DOMContentLoaded', () => {
const socket = io();
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
let drawing = false;
let lastX = 0;
let lastY = 0;
// Set up drawing style
context.lineJoin = 'round';
context.lineCap = 'round';
context.lineWidth = 5;
// Mouse event handlers
function startDrawing(e) {
drawing = true;
lastX = e.offsetX;
lastY = e.offsetY;
}
function draw(e) {
if (!drawing) return;
// Draw line
context.beginPath();
context.moveTo(lastX, lastY);
context.lineTo(e.offsetX, e.offsetY);
context.stroke();
// Send line data to server
socket.emit('draw_line', {
x1: lastX,
y1: lastY,
x2: e.offsetX,
y2: e.offsetY
});
// Update last position
lastX = e.offsetX;
lastY = e.offsetY;
}
function stopDrawing() {
drawing = false;
}
// Add event listeners
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
// Handle incoming drawing data
socket.on('draw_line', (data) => {
context.beginPath();
context.moveTo(data.x1, data.y1);
context.lineTo(data.x2, data.y2);
context.stroke();
});
});
</script>
</body>
</html>
With this application, multiple users can draw on the canvas simultaneously, and everyone will see each other's drawings in real-time!
Best Practices
- Error Handling: Always include proper error handling in both server and client code.
@socketio.on_error()
def error_handler(e):
print('An error has occurred: ' + str(e))
-
Authentication: Implement authentication to secure your WebSocket connections.
-
Consider Message Size: Large messages can impact performance. Consider compression for large data.
-
Use Background Tasks: For time-consuming operations, use background tasks to avoid blocking the event loop.
import threading
@socketio.on('long_task')
def handle_long_task():
# Emit a "processing" message immediately
emit('status', {'message': 'Processing started'})
# Start a background thread for the long task
def background_task():
# Do the time-consuming work here
result = process_data()
# When done, emit the result back
socketio.emit('task_complete', {'result': result})
thread = threading.Thread(target=background_task)
thread.daemon = True
thread.start()
- Test with Multiple Clients: Always test your application with multiple clients to ensure proper real-time behavior.
Summary
In this tutorial, we've covered the fundamentals of Flask-SocketIO:
- Setting up a Flask application with SocketIO support
- Creating and handling custom events
- Using namespaces and rooms for organizing connections
- Building real-time applications like chat and collaborative drawing
- Best practices for production applications
Flask-SocketIO opens up a world of possibilities for creating interactive web applications that respond immediately to user actions and server events. By enabling real-time communication, you can create more engaging and dynamic user experiences.
Additional Resources
- Official Flask-SocketIO Documentation
- Socket.IO JavaScript Library Documentation
- Eventlet Documentation (for production deployment)
Exercises
- Enhance the chat application to show user names and timestamps with messages.
- Add different "rooms" to the chat application so users can join specific chat rooms.
- Create a real-time dashboard that displays system metrics (like CPU usage, memory) updating every few seconds.
- Implement a collaborative to-do list where multiple users can add, complete, and delete tasks in real-time.
- Build a simple multiplayer game using Flask-SocketIO (like tic-tac-toe or a simple drawing guessing game).
Happy coding with Flask-SocketIO!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)