作者:詹聪聪
序言:
本人工作中需要用到flask-socketio,在学习英文文档时发现,flask-socketio目前并没有相关的中文文档。斗胆利用业余时间将这个库的英文文档翻译出来,希望能够帮助那些没有时间或精力研习英文文档的朋友。鉴于水平有限,翻译错误在所难免,还望各位不吝赐教。
注意:译者所用的flask-socketio版本号是:2.7.2,无特殊情况,本文档的一切特性均以2.7.2版本为准。
正文:
flask-SocketIO 为flask应用提供了一个客户端与服务器之间低延迟的双向通信。客户端应用可以用Javascript,C++,Java,Swift或者其它任意的编程语言的socketio官方库的客户端去和服务端创建一个永久的连接。
1.安装
你可以使用pip这样常规的方式来安装这个包:
> pip install flask-socketio
2.依赖
Flask-SocketIO兼容python2.7和python3.3+。这个异步的服务的包的依赖可以有三个选择:
eventlet:这是最好的选择,支持长连接(long-polling)和websocket传输。
gevent: 支持许多不同的配置,长连接传输是完全支持的,但是不同于eventlet,gevent并没有原生支持websocket。添加websocket(功能)有两种方法:gevent-websocket包为gevent添加了websocket支持,但是不幸的是,这个包只能用于python2;至于另外一个选择,是用uWSGI网络服务器,这个能够在功能上支持websocket。gevent依然是可操作的选择,但是优先级略微地低于eventlet。
基于Werkzeug开发的flask服务器也是可行的,使用缺乏可操作性的caveat,它仅可以被用于简化workflow的开发。这个方案仅支持长连接方式传输。
这个扩展自动寻找已安装的异步框架来使用。最优先的是eventlet,其次是gevent。在gevent中,对于websocket的支持,uWSGI是优先考虑的,其次是gevent-websocket。如果eventlet和gevent都没有被安装,那么就使用flask-development将会被启用。
如果使用多进程,一个消息队列服务将会被进程用来协调操作,例如广播。支持这个队列的有Redis,RabbitMQ,还有其他由Kombu支持的包。
在客户端,Javascript官方的SOcket.IO可以用来创建一个与服务端通信的连接。这里有许多用Swift,Java,C++编写的官方客户端。非官方的客户端也是可以工作的,只要他们支持了Socket.IO协议。
3.初始化
接下来的代码例子揭示了,怎样去把Flask-SocketIO引入到Flask应用:
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.configp['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
if __name__ == '__main__':
socketio.run(app)
init_app()风格的初始化也是支持的。注意网络服务器的启动。函数socketio.run()封装了网络服务器的启动部分,并且代替了flask开发服务器的标准启动语句app.run()
。当应用在debug模式下,Werkzeug开发服务器也是在socketio.run()
中被合理地应用和配置。如果可用的话,在生产模式下eventlet网络服务器也是被应用的,否则,gevent网络服务器将会被启用。如果eventlet和gevent都没有被安装,那么将会使用Werkzeug开发网络服务器。
在flask 0.11中被引入的可点击命令行界面也是被支持的。这个扩展提供了一个新版的flask run命令,适合启动一个Socket.IO服务器。用法示例:
FLASK_APP = my_app flask run
这个应用只能为那种连接到客户端的页面服务,并且客户端还需引用Socket.IO库并且建立一个连接:
<script type="text/javascript" scr="//cdn.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(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
</script>
4.接收消息
在使用SocketIO的时候,消息将被作为活动(event)的两端接收。在客户端使用JavaScript回叫信号。使用Flask-SocketIO服务器,需要为这些活动注册处理器(handler),类似于视图函数怎样处理路由。
下面的例子是为一个未命名的活动创建了一个服务端的活动处理器(event handler):
@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_sustom_event(arg1, arg2, arg3):
print('received args: ' + arg1 + arg2 + arg3)
命名活动是极度复杂的,在其消除了额外的元数据(metadata)来描述消息类型的时候。
Flask-SocketIO同样支持命名空间(namespace),这个功能允许客户端在一个相同的物理socket上多路复用几个独立的连接:
@scoketio.on('my event', namespace='/test')
def handle_my_custom_namespace_event(json):
print('received json: ' + str(json))
当一个命名空间没有具体指出,一个全局的命名空间'/'将会被启用
有时,装饰器的语法并不方便,on_event()方法可以作为替代
def my_function_handler(data):
pass
socketio.on_event('my event', my_function_handler, namespace='/test')
客户端要求一个确认回复,来确认消息的接收。任何一个从处理函数(handler function)中返回的值都会在回调函数中作为一个参数返回给客户端。
@socketio.on('my event')
def handle_my_custom_event(json):
print('received json: ' + str(json))
return('one', 2)
在上面的例子中,客户端回调函数将会回调两个参数,one
和2
。如果处理函数没有返回值,这个客户端回调函数将以没有参数的情况返回。
5.发送消息
之前章节定义的SocketIO活动处理函数可以凭借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)
@socket.on('my event')
def handle_my_custom_event(json):
emit('my response', json)
注释一下,send()
和emit()
是怎样用在已命名和未命名的活动上的
当运作在有命名空间的活动中时,send()
和emit()
默认用在接下来的消息中。不同的命名空间可以被具体化到可选择的可选择的命名空间参数上:
@socketio.on('message')
def handle_message(message):
send(message, namespace='/chat')
@socketio.on('message')
def handle_my_sustom_event(json):
emit('my response', json, namespace='/chat')
为了实现发送一个多参数的活动,发送一个元组:
def ack():
print('message was received!')
@socketio.on('my event')
def handle_my_custom_event(json):
emit('my response', json, callback=ack)
使用回调时,JavaScript客户端使用回调函数在接收到的信息时回调。在客户端应用启用回调函数时,服务器会启用服务端相匹配的函数去响应。如果客户端没有回调任何值,这些将会作为服务端的响应被提供。
客户端的应用同样要求一个来自服务端的确认信息。如果服务端想为一次响应提供一个参数,它必须要在活动处理函数中被返回。
@socketio.on('my event')
def handle_my_custom_event(json):
# ... handle the event
return 'foo', 'bar', 123 # client callback will receive these 3 arguments
6.广播
SocketIO另外一个非常有用的特性就是广播消息。Flask-SocketIO中,只要将broadcast = True
这个可选参数加到send()和emit()中即可:
@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})
注意:通过对send()和emit()的上下文的感知,socketio.send()
和socketio.emit()
不是相同的函数。同样需要注意的是:以上的用法是没有客户端内容,所以假定broadcast=True
,并且需要被具体化。
7.房间
在许多应用中,有必要将用户划分为可以一并处理的几个子集。最好的例子是,一个包含多个房间的聊天应用,当用户收到他所在的房间的消息,而不会收到其他人所在房间的消息。Flask-SocketIO支持通过join_room()
和leave_room()
函数来支持房间的概念:
from flask_socketio import join_room, leave_room
@socketio.on('join')
def on_join(data):
uername=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
作为一个参数,将消息广播到所有在给定房间里的客户端。
所有连接(到服务器)的客户端都被分配到一个房间,并且以连接的会话编号(session ID)命名这个房间的名称,这个会话编号由request.sid获得。一个既定的客户端可以加入任何一个房间,这个房间的名称可以是任何名称。当一个客户端终止(与服务器的)连接,它将会从原来所在的房间里除名。这两个上下文无关的函数socketio.send()
和socketio.emit()
也会接受参数room
,把消息广播到这个房间里的所有客户端。
一旦所有的客户端被分配到一个自己的房间,为了将消息发送到一个唯一的客户端,会话编号可以作为参数room的值。
8.连接活动
Flask-SocketIO同样支持连接和断开的活动。接下来的例子将会展示怎样为他们注册一个处理函数:
@socketio.on('connect', namespace='/chat')
def test_connect():
emit('my response', {'data': 'Connected'})
@socketio.on('disconnected', namespace='/chat')
def test_disconnected():
print('Client disconnected')
连接活动处理处理函数可以选择性地返回一个False去拒绝这个连接。这是为了在这一点上进行身份认证。
注意:连接和断开活动可以在各自使用的命名空间内独立地发送。
9.基于类的命名空间
以上描述的作为基于装饰器的活动处理函数的替代,属于命名空间的活动处理函数可以被创造成一个类的方法。Flask_socketio.Namespace提供了一个基于类的方法来创造命名空间。
from flask_socketio import Namespace, emit
class MyCustomNamespace(Namespace):
def on_conect():
pass
def on_disconnect():
pass
def on_my_event(data):
emit('my_response', data)
socket.on_namespace(MyCustomNamespace('/test'))
使用一个基于类的命名空间时,所有服务器接收到的活动将会被分配到一个方法,该方法的活动名称是以on_为前缀的活动。例如,名称为my_event
的活动,将会由on_my_event
函数来处理。如果一个接收到的活动在命名空间类中没有与之相匹配的处理方法。这个活动将会被忽略。所有在基于类的命名空间内的活动必须使用具有合法的方法名称的单词。
作为一个定义基于类的命名空间的简便方法,这个命名空间实例包括了几个版本的flask_socketio.SocketIO类,并且他们默认的命名空间参数并没有给出。
如果一个活动同时具有在基于类的命名空间里的处理函数和基于装饰器的处理函数,只有装饰器函数会被调用。
10.错误处理
Flask-SocketIO也可以处理异常:
@socketio.on_error() # handles the dafault 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
错误处理函数将异常对象作为一个参数
这个消息和数据参数作为当前的请求将会被察觉request.event
变量察觉,这有利于外部活动处理函数的错误日志和调试
from flask import request
@socketio.on("my error event")
def on_my_event(data):
raise RuntimeError()
@socket.on_error_default
def default_error_handler(e):
print(request.event["message"]) # "my error event"
print(request.event["args"]) # (data,)
11.访问flask上下文全局变量
SocketIO活动处理不同于路由处理,在于它引入了许多容易混淆的东西,围绕着SocketIO什么可以做,什么不可以做。最主要的区别就是SocketIO活动发生在单个长期运行在上下文的请求之中。
尽管有所不同,Flask-SocketIO将环境改造成类似于常规HTTP请求,使SocketIO活动处理更加轻松。接下来的列表描述了什么将会生效,什么不会。
在活动处理函数之前推送应用的上下文使得
current_app
和g
可以在处理函数中可用。这个请求的上下文同样在回调处理函数前被启用,也使
request
和session
可用。但是注意到WebSocket活动与之并没有独立的联系,因此为连接期间分派的所有事件推送启动连接的请求上下文。request
上下文全局变量随一个sid成员增加,这个成员是为了给连接一个独特的会话编号(session ID)。这个值在客户端刚刚添加的时候,就被最初的房间使用了。request上下文全局变量由包含了当前处理函数的命名空间和活动参数的
argument
和event
来增加。这个活动成员是一个包含了message
和args
键值的字典。session
上下文全局变量表现得和通常的请求不一样。在连接开始建立的时候,就会复制一份用户的会话在这个连接上下文中给处理器调用。如果SocketIO处理器修改了这个会话,这个修改过的会话就会为未来的SocketIO处理器保留,但是正常的HTTP路由处理器不会察觉这些改变。有效率的是,当SocketIO处理器改变这个会话的时候,会话就会为这些处理器创建一个“分支”(fork)。这个限制的技术原因是用户的会话cookie必须要发送到客户端,这需要HTTP请求和应答而不是SocketIO连接。在使用服务端的会话时,比如那些由Flask-Session或者Flask-KVSession扩展提供的会话,在HTTP处理器中的会话改变也可以在SocketIO处理器中可见,只要这个会话不是在SocketIO处理器中修改的。before_request
和after_request
钩子不会调用SocketIO活动处理器。SocketIO处理器可以使用自定义的装饰器,但是大多数Flask装饰器并不适于SocketIO处理器,考虑到SocketIO连接中没有Response对象这一概念。
12.身份认证
应用的共同需要就是验证他们用户的身份。自从SocketIO没有使用HTTP请求和应答,传统的基于网页表单和HTTP请求的机制不能用于SocketIO连接。如果需要的话,应用可以实施自定义的登陆表单,当用户按下提交按钮时,它利用一个SocketIO消息将证书发送到服务器。
然而,在大多数情况下,在SocketIO连接建立之前使用传统的身份验证方式会更加方便,用户的身份信息可以被记录下来作为用户会话或者cookie,之后在SocketIO连接建立起来的时候,这些信息也可以被SocketIO活动处理器得到。
13.使用Flask-SocketIO的Flask-Login模块
Flask-SocketIO可以获得由Flask-Login维护的登陆信息。在一个正常的Flask-Login身份认证被使用的时候,login_user()函数将会被调用去记录用户会话中的用户,任何SocketIO连接都可以得到current_user
上下文变量:
@socketio.on('connect')
def connect_handler():
if current_user.is_authenticated:
emit('my response',
{'message':'{0} has joined'.format(current_user.name)},
broadcast=True
)
else:
return False # not allowed here
注意到login_required
装饰器不能和SocketIO活动处理器一起使用,但是一个自定义的关闭连接无身份认证的装饰器可以按下面的方式创建:
import functools
from flask import request
from flask_login import current_user
from flask_socketio import disconnect
def authenticated_only(f):
@functools.wraps(f):
def wraped(*args, **kwargs):
if not current_user.is_authenticated:
disconnect()
else:
return f(*args, **kwargs)
return wraped
@socketio.on('my event')
@authenticated_only
def handle_my_custom_event(data)
emit('my response',
{'message': '{0} has joined'.format(current_user.name)},
broadcast=True
)
14.部署
我们有多种部署Flask-SocketIO服务器的选择,从最简单到疯狂地复杂。在这一章节里,我们将会
介绍最普遍的选择。
嵌入式服务器
最简单的策略是安装eventlet或者gevent,并且就像前面章节的例子中引用socketio.run(app)
的方式来启动网络服务器。这个将会在eventlet或者gevent网络服务器中启动这个应用,被嵌入的网络服务器是哪一个取决于是安装的是哪一个。
注意到socketio.run(app)
运行在eventlet或gevent已安装上的生产服务器中。如果它们中没有一个被安装,那么这个应用运行在Flask开发服务器中,这并不适于生产环境的使用。
不幸的是,这个选择并不能在带有uWSGI的gevent服务器上使用,你可以在下面获取更多有关这个选项的信息。
Gunicorn网络服务器
作为socketio.run(app)
替代方法的就是使用gunicorn作为网络服务器,工作在eventlet或gevent下。这个选择下,除了gunicorn要安装,eventlet或者gevent也是不可缺少的。这个条命令将会启动这个基于gunicorn的eventlet服务器:
gunnicorn --worker--class eventlet -w 1 module:app
如果你更倾向于使用gevent,启动服务器的命令如下:
gunicorn -k gevent -w 1 module:app
当使用gunicorn作为gevent的工作站并且websocket支持也被提供的时候,上述命令就必须被改成选择一个自定义的gevent网络服务器来支持websocket协议。修改后的命令如下:
gunicorn -k geventwebsocket.gunicorn.worker.GeventWebSocketWorker -w 1 module:app
在上述这些命令中,module是python模块或者是定义了应用实例的包,此外,app是应用实例本身。
Gunicorn 18.0版本是被推荐和Flask-SocketIO搭配的版本。19.x版本已知在带有WebSocket的一些特定部署场景下存在不兼容的情况。
gunicorn由于使用了有限的负载均衡算法,不可能在使用这种网络服务器时调用两个以上工作进程因为这个原因,上面的所有例子中都包含了-w 1
的可选参数。
15.uWSGI网络服务器
当使用uWSGI网络服务器搭配geventd的时候,Socket.IO服务器的时候,可以利用uWSGI原生的WebSocket支持。
一个配置和运用uWSGI服务器完整的解释超出了本文的论述范围。uWSGI服务器确实是一个比较复杂的,它提供了大量而又详尽的设置选项。它必须使用Websocket和SSL编译才能支持WebSocket传输。作为介绍,下面的命令启动了一个uWSGI服务器作为范例,这个应用app.py运行在端口5000:
uwsgi --http :5000 --gevent 1000 --http-websockets --master --wsgi-file app.py --callable app
16.使用nginx作为反向代理服务器
使用nginx作为前端的反向代理将请求传递给应用是可行的。然而,只有nginx 1.4版本以上才支持WebSocket协议。下面是nginx代理HTTP和WebSocket请求的一个最基本的配置:
server {
listen 80;
server_name _;
location / {
include proxy_params;
proxy_pass http://127.0.0.1:5000;
}
location /socket.io {
include proxy_params;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://127.0.0.1:5000/socket.io;
}
}
下面的例子增加了对负载平衡多个服务器的支持:
upstream socketio_nodes {
ip_hash;
server 127.0.0.1:5000;
server 127.0.0.1:5001;
server 127.0.0.1:5002;
# to scale the app, just add more nodes here!
}
server {
listen 80;
server_name _;
location / {
include proxy_params;
proxy_pass http://127.0.0.1:5000;
}
location /socket.io {
include proxy_params;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://socketio_nodes/socket.io;
}
}
虽然上面的例子可以作为最初的配置工作,要知道生产环境安装的nginx需要一个完整的配置,包括部署的其它方面,例如服务于静态文件的assert和SSL支持。
17.使用多个工作站
Flask-SocketIO从2.0版本起带有负载均衡器支持多个工作站。部署多个工作站给了使用Flask-SocketIO的应用程序有能力在多进程和多主机之间传播客户端链接,这种方式的扩展支持极大规模的并发客户端。
使用多个Flask-SocketIO工作站需要两个依赖:
- 负载均衡器必须要配置成总是将所有的HTTP请求从一个给定的客户端转发到同样的工作站中。这有时会作为
sticky session
被提及。对于nginx,使用这个ip_bash指示来达到上述要求。Gunicorn不能用于多工作站,因为它的负载均衡算法并不支持粘性会话(sticky session)。 - 一旦每个服务器只拥有一个客户端连接,在Redis、RabbitMQ等例子中,消息队列将会被使用,来协调复杂的操作,比如:广播和房间。
当使用消息队列的时候,有许多额外的依赖包需要被安装:
- 对于Redis,redis包必须被安装(
pip install redis
)。 - 对于RabbitMQ,kombu包必须要被安装(
pip install kombu
)。 - 对于其它Kombu支持的消息队列,Kombu documentation里可以找到需要的依赖。
- 如果使用了eventlet或者gevent,那么通常需要使用猴子(Monkey)修补Python标准库来强制消息队列包使用协同友好的函数和类。
为了启动多个Flask-SocketIO服务器,你必须首先确保消息队列服务正在运行。为了开启一个Socket.IO服务器,使他连接到一个消息队列,需要添加参数message_queue
到构造函数SockIO:
socketio=SocketIO(app,message_queue='redis://')
参数message_queue的值就是队列服务所使用的连接URL。对于一个运行在同一个作为服务器的主机中的Redis队列来说,可以使用redis://
这样的URL。同样,对于一个默认的RabbitMQ队列可以使用amqp://
开头的URL。Kombu包有一个文档章节阐述了对于所有支持队列的URL格式。
18.外部进程消息
对于许多类型的应用,从非服务端创建会话活动很有必要,例如一个Celery工作站。如果SocketIO服务器并没有按照前面章节那样配置监听队列,那么所有其它的进程可以像服务器那样创建它自己的SocketIO实例来创建消息活动。
例如,一个运行在eventlet网络服务器上的应用,使用了Redis消息队列,下面的Python脚本将向所有的客户端广播一个消息活动。
socketio=SocketIO(message_queue='redis://')
socketio.emit('my event', {'data': 'foo'}, namespace='/test')
当使用这种方法引用SocketIO实例,Flask应用实例将不会传递到构造函数。
当SocketIO通过消息队列使用参数channel来选择一个具体channel的对话。当很多独立的SocketIO服务公用一个队列的时候,使用一个自定义的channel名称将是很有必要的。
Flask-SocketIO并没有在使用eventlet或者gevent时应用猴子(monkey)来修补。但是当使用消息队列的时候,如果Python标准库没有使用猴子来修补,那么消息队列服务的Python包很可能会挂起。
很重要的一点是:外部进程想连接到SocketIO服务器并不需要像主服务器那样使用eventlet或者gevent。使一个服务器使用了协同框架,外部进程不是一个阻力。例如,Celery工作站并不需要配置使用eventlet或者gevent,是因为主服务器已经有了。但是,如果你的外部进程因为某种原因
使用了协同框架,那么monkey修复就很可能是需要的,那么消息队列就可以获得协同友好的函数和类。
19.从Flask-SocketIO 0.x 升级到 1.x 和 2.x 版本
老版本的Flask-SocketIO有完全不同的一系列依赖包。老版本依赖gevent-socketio和gevent-websocket,这些包 1.0 版本都不需要了。
尽管依赖的改变,但是 1.0 版本却没有太多重要的改变。下面是一个实际改变的详细的清单:
- 1.0 版本放弃支持Python 2.6,增加了对Python 3.3, Python 3.4 和 pypy 的支持。
- 0.x 版本需要老版本的Socket.IO javascript客户端。从 1.0 版本开始,支持新发布的Socket.IO和Engin.IO。1.0版本以前的Socket.IO将不再被支持。Swift和C++官方的Socket.IO客户端也被支持。
- 0.x 版本依赖gevent,gevent-socketio和gevent-websocket.1.0 版本以后将不再使用。在Flask开发的网络服务器中,gevent是三种后端网络服务器选择之一,另外两个是eventlet和其它常规多线程WSGI服务器。
- Socket.IO服务器选项在 1.0 版本中也有所改变。它们可以由SocketIO构造函数来提供,或者由
run()
调用。这些选项在使用前在这两者中被合并。 - 0.x 版本暴露了gevent-socketio在连接中作为
request.namespace
。在 1.0 版本中它不再被使用。这个请求对象定义了request.namespace
作为待处理的命令空间。并且增加了request.aid
,为客户端连接定义了一个独有的会话ID,request.event
包含了活动名称和参数。 - 为了获得房间列表,0.x版本需要应用使用私有gevent-socketio结构,包含
request.namespace.rooms
表达式。这是在 1.0 版本中将不再出现,因为它包含了一个合适的room()
函数。 - 这个推荐的“把戏(trick)”发送消息到一个独立的客户端将消息分发到每个客户端所在的独立的房间内,这个地址消息对应着目的房间(desired room)。这个特性在 1.0 版本中被正式化了,当客户端连接到服务器时,它会立即自动地被分配到一个特定的房间内。
- 全局命名空间的
connect
活动在 1.0 版本之前并没有被触发。这bug已经被修复了并且按照预期触发。 - 在 1.0 版本增加了对客户端的回调函数的支持。
为了升级到新的Flask-SocketIO版本,你需要升级你的Socket.IO客户端到兼容Socket.IO 1.0 协议。对于Javascript客户端,1.3.x和1.4.x版本经过充分地测试,发现是兼容的。
在服务端,有一些要点是要被考虑到的:
- 如果你想继续使用gevent,那么gevent-socketio需要从你的虚拟环境中卸载,因为这个包将不再需要并且可能会与它的替代——python-socketio相冲突。
- 如果你想轻微地提高性能和稳定性,那么推荐你转而使用eventlet。为了做到这一点,需要卸载gevent、gevent-socketio和gevent-websocket,然后安装eventlet。
- 如果你的应用使用了猴子修复了并转向了eventlet,需要调用
eventlet.monkey_patch()
来代替gevent中的monkey.patch_all()
。此外,任何对gevent的调用必须被同等条件下的对eventlet调用替代。 - 任何使用
request.namespace
需要被直接调用Flask-SocketIO函数替代。例如,request.namespace.rooms
要用rooms()
函数替换。 - 任何使用内置的gevent-socketio的对象都必须被去除,当这个包不再是所需的依赖的时候。