Skip to main content

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:

bash
pip install flask-socketio

You may also want to install eventlet or gevent for better performance in production:

bash
pip install eventlet
# or
pip install gevent

Basic Setup

Here's a minimal Flask-SocketIO application:

python
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:

  1. We import SocketIO from flask_socketio
  2. We create a SocketIO instance with our Flask app
  3. Instead of app.run(), we use socketio.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)

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:

html
<!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:

bash
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

python
@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:

python
@socketio.on('user_joined')
def handle_user_joined(username):
emit('announcement', f'{username} has joined the chat', broadcast=True)

On the client side:

javascript
// 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:

python
@socketio.on('message', namespace='/chat')
def handle_chat_message(data):
emit('message', data, namespace='/chat', broadcast=True)

On the client:

javascript
const chatSocket = io('/chat');
chatSocket.emit('message', 'Hello from the chat namespace');

Rooms

Rooms let you broadcast to specific groups of clients:

python
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:

javascript
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

python
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:

html
<!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

  1. Error Handling: Always include proper error handling in both server and client code.
python
@socketio.on_error()
def error_handler(e):
print('An error has occurred: ' + str(e))
  1. Authentication: Implement authentication to secure your WebSocket connections.

  2. Consider Message Size: Large messages can impact performance. Consider compression for large data.

  3. Use Background Tasks: For time-consuming operations, use background tasks to avoid blocking the event loop.

python
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()
  1. 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

Exercises

  1. Enhance the chat application to show user names and timestamps with messages.
  2. Add different "rooms" to the chat application so users can join specific chat rooms.
  3. Create a real-time dashboard that displays system metrics (like CPU usage, memory) updating every few seconds.
  4. Implement a collaborative to-do list where multiple users can add, complete, and delete tasks in real-time.
  5. 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! :)