Flask-SocketIO是一个用于建立WebSocket全双工通信的Flask扩展。客户端应用可以使用任一SocketIO官方客户端库实现,或可以建立持久连接的兼容客户端实现。
1.安装
pip install flask-socketio
Flask-SocketIO提供三种异步服务机制:
- eventlet:支持长轮询和websocket
- gevent:需使用第三方库
- Werkzeug:仅支持长轮询
2.初始化
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
if __name__ == '__main__':
socketio.run(app)
应用必须给客户端返回一个html页面加载SocketIO库,并且建立连接:
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
</script>
3.接收消息
使用SocketIO时,客户端和服务端以事件的方式接收消息-事件驱动。在客户端,javascript使用回调函数。在服务端,Flask-SocketIO需要注册事件处理函数,类似于路由-视图函数机制。
下面的例子创建服务端无名事件处理函数:
@socketio.on('message')
def handle_message(message):
print('received message: ' + message)
上面的例子使用字符串消息,也可以使用json格式的消息:
@socketio.on('json')
def handle_json(json):
print('received json: ' + str(json))
最灵活的事件类型是使用定制事件名称。这些事件的消息数据格式可以是字符串、字节、整数或者json:
@socketio.on('my event')
def handle_my_custom_event(json):
print('received json: ' + str(json))
定制的命名事件支持多参数:
@socketio.on('my event')
def handle_my_custom_event(arg1, arg2, arg3):
print('received args: ' + arg1 + arg2 + arg3)
命名事件是最灵活的,因为命名事件消除了包含附加元数据(描述消息类型)的需要。Flask-socketIO也支持SocketIO命名域,SocketIO命名域允许客户端复用socket(多个连接建立在同一物理socket)。
@socketio.on('my event', namespace='/test')
def handle_my_custom_namespace_event(json):
print('received json: ' + str(json))
当SocketIO没有显式指定命名域,全局命名域‘/’将会被使用。
当装饰器语法不太合适时,on_event方法可被使用:
def my_function_handler(data):
pass
socketio.on_event('my event', my_function_handler, namespace='/test')
客户端可能请求一个承认的回调函数,该函数确认接收发送的消息。处理函数的任何返回值将会作为参数递送给客户端的回调函数:
@socketio.on('my event')
def handle_my_custom_event(json):
print('received json: ' + str(json))
return 'one', 2
上例中,客户端回调函数将会被传递两个参数:‘one’和2。
4、发送消息
Socket IO事件处理函数可以发送回复消息至连接的客户端,方法包括send()和emit()。
from flask_socketio import send, emit
@socketio.on('message')
def handle_message(message):
send(message)
@socketio.on('json')
def handle_json(json):
send(json, json=True)
@socketio.on('my event')
def handle_my_custom_event(json):
emit('my response', json)
当使用命名域时,send和emit默认使用接收消息的命名域。不同的命名域可以通过namespace参数指定。
@socketio.on('message')
def handle_message(message):
send(message, namespace='/chat')
@socketio.on('my event')
def handle_my_custom_event(json):
emit('my response', json, namespace='/chat')
当使用多个参数回复一个事件时,发送一个tuple:
@socketio.on('my event')
def handle_my_custom_event(json):
emit('my response', ('foo', 'bar', json), namespace='/chat')
SocketIO支持回调函数,该函数用于证实客户端成功接收了消息:
def ack():
print 'message was received!'
@socketio.on('my event')
def handle_my_custom_event(json):
emit('my response', json, callback=ack)
当使用回调函数时,js客户端从接收的消息中获取回调函数,客户端回调函数唤起之后服务端的回调函数相应地被唤起。如歌客户端回调函数具有参数,服务端的回调函数具有相同的参数。
5、广播
Flask-SocketIO支持广播,只需在send和emit函数中添加关键字参数broadcast=True:
@socketio.on('my event')
def handle_my_custom_event(data):
emit('my response', data, broadcast=True)
对于服务端主动发起的广播,socketio.send()和socketio.emit()方法可以用来广播消息至所有连接的客户端。
def some_function():
socketio.emit('some event', {'data': 42})
6、Rooms
Flask-SocketIO通过join_room和leave_room函数支持room概念。
from flask_socketio import join_room, leave_room
@socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
send(username + ' has entered the room.', room=room)
@socketio.on('leave')
def on_leave(data):
username = data['username']
room = data['room']
leave_room(room)
send(username + ' has left the room.', room=room)
send和emit函数接收关键字参数room,将消息发送至连接对应room的客户端。
所有的客户端连接room时用session ID命名,可以使用request.sid获取。
7、连接事件
Flask-SocketIO也支持连接和连接断开事件。
@socketio.on('connect', namespace='/chat')
def test_connect():
emit('my response', {'data': 'Connected'})
@socketio.on('disconnect', namespace='/chat')
def test_disconnect():
print('Client disconnected')
8、基于类的命名域
上面描述的是基于装饰器的处理函数,也可以使用基于类的命名域处理函数。
from flask_socketio import Namespace, emit
class MyCustomNamespace(Namespace):
def on_connect(self):
pass
def on_disconnect(self):
pass
def on_my_event(self, data):
emit('my_response', data)
socketio.on_namespace(MyCustomNamespace('/test'))
9、错误处理
@socketio.on_error() # Handles the default namespace
def error_handler(e):
pass
@socketio.on_error('/chat') # handles the '/chat' namespace
def error_handler_chat(e):
pass
@socketio.on_error_default # handles all namespaces without an explicit error handler
def default_error_handler(e):
pass