Skip to main content

Flask WebSockets

In modern web applications, real-time communication has become increasingly important. Traditional HTTP requests follow a request-response pattern where the client must initiate all communication. However, many applications require instant updates without refreshing the page or continuously polling the server. This is where WebSockets come in.

What are WebSockets?

WebSockets provide a persistent connection between a client and server, allowing for bidirectional communication. Unlike HTTP, which closes the connection after each request-response cycle, WebSockets keep the connection open for continuous data exchange.

Some common use cases for WebSockets include:

  • Chat applications
  • Live notifications
  • Collaborative editing tools
  • Real-time dashboards
  • Online gaming
  • Live sports updates

Flask-SocketIO

Flask itself doesn't have built-in WebSocket support. Instead, we can use the Flask-SocketIO extension, which integrates Socket.IO with Flask applications. Socket.IO is a library that enables real-time, bidirectional communication between clients and servers.

Setting Up Flask-SocketIO

First, let's install the necessary packages:

bash
pip install flask flask-socketio

Now, let's create a basic Flask application with Socket.IO:

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)

Creating HTML Templates

Let's create a simple template with Socket.IO client-side integration:

html
<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Flask-SocketIO Example</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 the Socket.IO server
const socket = io();

// Event handlers can go here
});
</script>
</head>
<body>
<h1>Flask-SocketIO Example</h1>
<div id="messages"></div>
</body>
</html>

Handling Socket Events

Server-Side Events

In Flask-SocketIO, we use decorators to define event handlers:

python
@socketio.on('connect')
def handle_connect():
print('Client connected')

@socketio.on('disconnect')
def handle_disconnect():
print('Client disconnected')

@socketio.on('message')
def handle_message(data):
print('Received message:', data)
# Echo the message back to the client
socketio.emit('message', data)

Client-Side Events

On the client side, we need to update our JavaScript to handle these events:

javascript
// Update the script section in index.html
const socket = io();

// Connection events
socket.on('connect', () => {
console.log('Connected to server');
});

socket.on('disconnect', () => {
console.log('Disconnected from server');
});

// Custom event handlers
socket.on('message', (data) => {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('p');
messageElement.textContent = data;
messagesDiv.appendChild(messageElement);
});

// Function to send a message
function sendMessage(message) {
socket.emit('message', message);
}

Building a Simple Chat Application

Let's build a complete chat application to demonstrate WebSockets in action:

Updated Server Code

python
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit, join_room, leave_room

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
socketio = SocketIO(app)

@app.route('/')
def index():
return render_template('chat.html')

@socketio.on('connect')
def handle_connect():
print('Client connected:', request.sid)

@socketio.on('disconnect')
def handle_disconnect():
print('Client disconnected:', request.sid)

@socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
emit('message', f'{username} has entered the 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.', to=room)

@socketio.on('chat_message')
def handle_message(data):
room = data['room']
emit('message', f"{data['username']}: {data['message']}", to=room)

if __name__ == '__main__':
socketio.run(app, debug=True)

Chat Template

html
<!-- templates/chat.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>
<style>
#chat-container {
width: 600px;
margin: 0 auto;
}
#messages {
height: 400px;
border: 1px solid #ccc;
overflow-y: scroll;
margin-bottom: 10px;
padding: 10px;
}
#user-form, #message-form {
margin-bottom: 10px;
}
input[type="text"] {
width: 70%;
padding: 5px;
}
button {
padding: 5px 10px;
}
</style>
</head>
<body>
<div id="chat-container">
<h1>Flask-SocketIO Chat</h1>

<div id="user-form">
<input type="text" id="username" placeholder="Username">
<input type="text" id="room" placeholder="Room">
<button id="join-btn">Join</button>
<button id="leave-btn" disabled>Leave</button>
</div>

<div id="messages"></div>

<div id="message-form">
<input type="text" id="message" placeholder="Type your message..." disabled>
<button id="send-btn" disabled>Send</button>
</div>
</div>

<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
const socket = io();
let activeRoom = '';
let username = '';

// DOM elements
const messagesDiv = document.getElementById('messages');
const usernameInput = document.getElementById('username');
const roomInput = document.getElementById('room');
const messageInput = document.getElementById('message');
const joinBtn = document.getElementById('join-btn');
const leaveBtn = document.getElementById('leave-btn');
const sendBtn = document.getElementById('send-btn');

// Add a message to the chat
function addMessage(message) {
const messageElement = document.createElement('p');
messageElement.textContent = message;
messagesDiv.appendChild(messageElement);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}

// Socket events
socket.on('connect', () => {
addMessage('Connected to server');
});

socket.on('disconnect', () => {
addMessage('Disconnected from server');
});

socket.on('message', (data) => {
addMessage(data);
});

// Join room
joinBtn.addEventListener('click', () => {
username = usernameInput.value.trim();
activeRoom = roomInput.value.trim();

if (username && activeRoom) {
socket.emit('join', {username: username, room: activeRoom});
addMessage(`You joined room: ${activeRoom}`);

// Update UI
usernameInput.disabled = true;
roomInput.disabled = true;
joinBtn.disabled = true;
leaveBtn.disabled = false;
messageInput.disabled = false;
sendBtn.disabled = false;
}
});

// Leave room
leaveBtn.addEventListener('click', () => {
socket.emit('leave', {username: username, room: activeRoom});
addMessage(`You left room: ${activeRoom}`);

// Update UI
usernameInput.disabled = false;
roomInput.disabled = false;
joinBtn.disabled = false;
leaveBtn.disabled = true;
messageInput.disabled = true;
sendBtn.disabled = true;
activeRoom = '';
});

// Send message
sendBtn.addEventListener('click', () => {
const message = messageInput.value.trim();
if (message) {
socket.emit('chat_message', {
username: username,
room: activeRoom,
message: message
});
messageInput.value = '';
}
});

// Allow Enter key to send messages
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && !sendBtn.disabled) {
sendBtn.click();
}
});
});
</script>
</body>
</html>

Running the Application

To run the application, save the files and execute:

bash
python app.py

Open multiple browser windows at http://localhost:5000 to test the chat functionality. You can:

  1. Enter a username and room name
  2. Join the room
  3. Send messages
  4. See messages from other users in the same room
  5. Leave the room

Namespaces and Rooms

Socket.IO has two important concepts for organizing connections:

Namespaces

Namespaces allow you to split the logic of your application into separate communication channels:

python
# Server-side
chat = socketio.namespace('/chat')

@chat.on('connect')
def chat_connect():
print('Client connected to chat namespace')

@chat.on('message')
def chat_message(data):
emit('message', data, namespace='/chat')
javascript
// Client-side
const chatSocket = io('/chat');

Rooms

Rooms are used to group connections within a namespace, as we've seen in our chat example. Rooms are useful for broadcasting messages to a subset of clients:

python
@socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
emit('message', f'{username} has entered the room.', to=room)

Error Handling with WebSockets

It's important to handle errors in your WebSocket applications:

python
@socketio.on_error()
def error_handler(e):
print('An error has occurred:', e)

@socketio.on_error('/chat')
def chat_error_handler(e):
print('An error has occurred in the chat namespace:', e)

@socketio.on_error_default
def default_error_handler(e):
print('Default error handler:', e)

Authentication with WebSockets

For secure applications, you'll often need authentication:

python
from flask_login import current_user

@socketio.on('connect')
def handle_connect():
if not current_user.is_authenticated:
return False # Reject the connection
else:
print(f'User {current_user.username} connected')

Broadcasting to All Clients

Sometimes you need to send a message to all connected clients:

python
@socketio.on('broadcast_message')
def handle_broadcast(data):
emit('message', data, broadcast=True)

Deployment Considerations

When deploying WebSocket applications, there are a few considerations:

  1. Use an asynchronous server like Eventlet or Gevent
  2. Consider using a message queue like Redis for scaling
  3. Ensure your reverse proxy (Nginx, etc.) is configured for WebSockets
python
# For production with Eventlet
import eventlet
eventlet.monkey_patch()

# At the end of your file
if __name__ == '__main__':
socketio.run(app, debug=False, host='0.0.0.0')

Summary

WebSockets provide a powerful way to implement real-time bidirectional communication in Flask applications. With Flask-SocketIO, you can:

  • Establish persistent connections between clients and server
  • Send and receive messages in real-time
  • Organize connections using namespaces and rooms
  • Build interactive web applications like chat systems, dashboards, and more

WebSockets are an essential technology for modern web applications that require real-time updates and interactions.

Additional Resources

Exercises

  1. Enhance the chat application to show a list of active users in each room
  2. Implement private messaging between users
  3. Add typing indicators to show when a user is typing a message
  4. Create a collaborative drawing application using WebSockets
  5. Build a real-time dashboard that displays system metrics updated via WebSockets

With these skills, you can create powerful interactive web applications that respond instantly to user actions and server events!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)