Flask-SocketIO基本使用

Flask-SocketIO是一个用于建立WebSocket全双工通信的Flask扩展。客户端应用可以使用任一SocketIO官方客户端库实现,或可以建立持久连接的兼容客户端实现。

1.安装

pip install flask-socketio

Flask-SocketIO提供三种异步服务机制:

  • eventlet:支持长轮询和websocket
  • gevent:需使用第三方库
  • Werkzeug:仅支持长轮询

2.初始化

1
2
3
4
5
6
7
8
9
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库,并且建立连接:

1
2
3
4
5
6
7
<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需要注册事件处理函数,类似于路由-视图函数机制。

下面的例子创建服务端无名事件处理函数:

1
2
3
@socketio.on('message')
def handle_message(message):
print('received message: ' + message)

上面的例子使用字符串消息,也可以使用json格式的消息:

1
2
3
@socketio.on('json')
def handle_json(json):
print('received json: ' + str(json))

最灵活的事件类型是使用定制事件名称。这些事件的消息数据格式可以是字符串、字节、整数或者json:

1
2
3
@socketio.on('my event')
def handle_my_custom_event(json):
print('received json: ' + str(json))

定制的命名事件支持多参数:

1
2
3
@socketio.on('my event')
def handle_my_custom_event(arg1, arg2, arg3):
print('received args: ' + arg1 + arg2 + arg3)

命名事件是最灵活的,因为命名事件消除了包含附加元数据(描述消息类型)的需要。Flask-socketIO也支持SocketIO命名域,SocketIO命名域允许客户端复用socket(多个连接建立在同一物理socket)。

1
2
3
@socketio.on('my event', namespace='/test')
def handle_my_custom_namespace_event(json):
print('received json: ' + str(json))

当SocketIO没有显式指定命名域,全局命名域‘/’将会被使用。

当装饰器语法不太合适时,on_event方法可被使用:

1
2
3
4
def my_function_handler(data):
pass

socketio.on_event('my event', my_function_handler, namespace='/test')

客户端可能请求一个承认的回调函数,该函数确认接收发送的消息。处理函数的任何返回值将会作为参数递送给客户端的回调函数:

1
2
3
4
@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()。

1
2
3
4
5
6
7
8
9
10
11
12
13
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参数指定。

1
2
3
4
5
6
7
@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:

1
2
3
@socketio.on('my event')
def handle_my_custom_event(json):
emit('my response', ('foo', 'bar', json), namespace='/chat')

SocketIO支持回调函数,该函数用于证实客户端成功接收了消息:

1
2
3
4
5
6
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:

1
2
3
@socketio.on('my event')
def handle_my_custom_event(data):
emit('my response', data, broadcast=True)

对于服务端主动发起的广播,socketio.send()和socketio.emit()方法可以用来广播消息至所有连接的客户端。

1
2
def some_function():
socketio.emit('some event', {'data': 42})

6、Rooms

Flask-SocketIO通过join_room和leave_room函数支持room概念。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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也支持连接和连接断开事件。

1
2
3
4
5
6
7
@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、基于类的命名域

上面描述的是基于装饰器的处理函数,也可以使用基于类的命名域处理函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
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、错误处理

1
2
3
4
5
6
7
8
9
10
11
@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
分享到