我们在使用socketio时可以选择让socketio自己管理session或者是采用flask的session。在初始化Socketio时,可以加上manage_session这个关键字参数来决定session的管理方式,默认是True(即有socketio自己管理)
在默认情况下,flask-socketio可以修改在socket建立之前被push的上下文,但是这个修改仅限在socketio所定义的事件函数内有效,当在本次socket事件被触发时修改了session,那么修改语句以后的语句(包括下次同样的函数被触发)都可以得到被修改的数据,但是并不被保存到client,因为只有HTTP才可以设置cookie,下面是例子:
from flask import Flask, session, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'DFSDFDSF'
socketio = SocketIO(app)
@app.route('/')
def index():
session['a'] = 123
return 'ok'
@app.route('/test/')
def chat():
return render_template('a.html') # a.html有定义的socket事件,可以触发下面的‘message'
@socketio.on('message')
def message(msg):
print(session.get('a'))
session['a'] = 555
print(session.get('a'))
if __name__ == '__main__':
socketio.run(app)
结果
123
555
123
555
555
555
首先访问首页,设置session[‘a’]=123,再次访问/test/,返回含有socket的html,然后触发’message’事件,首先打印session的值(也就是在首页设置的值),随后修改,再次打印可以看到,session已被修改,并且下次‘message’被触发,还是修改后的值。
当我们设置manage_session为False时,这个时候一般时在采用server-session的时候使用。通过flask-socket的源码可以看出,这么设置,可以在HTTP函数和SOCKET事件中共享session。即既可以在HTTP函数中修改session,也可以在socket事件中修改session,session的变化都能被这两个发现,下面是socket的事件触发时的部分源码
def _handle_event(self, handler, message, namespace, sid, *args):
if sid not in self.server.environ:
# we don't have record of this client, ignore this event
return '', 400
app = self.server.environ[sid]['flask.app']
with app.request_context(self.server.environ[sid]):
if self.manage_session:
# manage a separate session for this client's Socket.IO events
# created as a copy of the regular user session
if 'saved_session' not in self.server.environ[sid]:
self.server.environ[sid]['saved_session'] = \
_ManagedSession(flask.session)
session_obj = self.server.environ[sid]['saved_session']
else:
# let Flask handle the user session
# for cookie based sessions, this effectively freezes the
# session to its state at connection time
# for server-side sessions, this allows HTTP and Socket.IO to
# share the session, with both having read/write access to it
session_obj = flask.session._get_current_object() **这里时为了后面判断,方便定义了这个变量**
_request_ctx_stack.top.session = session_obj
flask.request.sid = sid
flask.request.namespace = namespace
flask.request.event = {'message': message, 'args': args}
try:
if message == 'connect':
ret = handler()
else:
ret = handler(*args)
except:
err_handler = self.exception_handlers.get(
namespace, self.default_exception_handler)
if err_handler is None:
raise
type, value, traceback = sys.exc_info()
return err_handler(value)
if not self.manage_session:
# when Flask is managing the user session, it needs to save it
if not hasattr(session_obj, 'modified') or session_obj.modified:
resp = app.response_class()
app.session_interface.save_session(app, session_obj, resp)
return ret
当socket的事件被触发时,with app.request_context()自动把上下文push,由于整个app都被保存在self.server中,每次被push的session都是一样的(session里存的是memcached或者redis中的key,session中的值被存在value中),由于我们设置了manage_session为False 所以 session_obj = flask.session._get_current_object() 这是为了上端代码的后面的执行方便。可以看到上段代码的最后有保存session的动作,为了保存,首先需实例化一个response对象,代码到这里就需要下一个扩展了,因为server-session扩展都是在这里修改的接口,我们以flask-session为扩展并以memcached作为存储为例:
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
full_session_key = self.key_prefix + session.sid
if PY2 and isinstance(full_session_key, unicode):
full_session_key = full_session_key.encode('utf-8')
if not session:
if session.modified:
self.client.delete(full_session_key)
response.delete_cookie(app.session_cookie_name,
domain=domain, path=path)
return
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
expires = self.get_expiration_time(app, session)
if not PY2:
val = self.serializer.dumps(dict(session), 0)
else:
val = self.serializer.dumps(dict(session))
self.client.set(full_session_key, val, self._get_memcache_timeout(
total_seconds(app.permanent_session_lifetime)))
if self.use_signer:
session_id = self._get_signer(app).sign(want_bytes(session.sid))
else:
session_id = session.sid
response.set_cookie(app.session_cookie_name, session_id,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)
session.sid是一个uuid,可以看到在保存session的过程中,以session.sid+self.prefix 为key,将真正的session作为value存进去,在使用memcached时要注意,memcached的最长过期时间是30天,超过这个时间的存不进去,但是flask默认的permanent_session_lifetime是31天,虽然flask-session中有关于这个问题的做了兼容,但是似乎不起作用,毕竟两年前的扩展了,需要手动将permanent_session_lifetime设为小于31天。只要将session存到memcached中就可以做到共享session了。set_cookie在socket通信中是不会生效的。