websocket可以实现client和server之间的双向通信,可以用来做为聊天室的传输方式 。下面一个小例子展示如果使用:
网页的代码部分:
<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.bootcss.com/socket.io/1.5.1/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('message', function (msg) {
if (msg.data === 'error') {
window.open('/');
}
else {
var message = msg.data;
var username = msg.username
$('#chat').append(username + ": ");
$('#chat').append(message);
$('#chat').append('<br>')
var chatlist = document.getElementById('chat');
chatlist.scrollTop = chatlist.scrollHeight;
chatlist.scrollTop = chatlist.scrollHeight;
}
});
$('#mess').submit(function (event) {
var message = $('#message').val();
socket.emit('message', {data: message});
return false;
});
})
</script>
服务端:
@socketio.on('message')
def client_msg(msg):
if current_user.is_authenticated:
message = Message(from_user=current_user.username, content=msg['data'])
db.session.add(message)
db.session.commit()
a = {'data': (msg['data']), 'username': current_user.username}
send(a, broadcast=True)
else:
send({'data': 'error'}, broadcast=False)
disconnect()
服务端创建了一个’message’事件,接收前端’message’事件发来的消息,并存入数据库,随后将这个消息传送给所有已连接的client(包括发送这条消息的客户端,因为client这里设计的逻辑是:发送的消息不会展示在页面,只显示接受的消息)。因为websocket建立的时候是通过http进行握手的,所有在websocket建立之前可以从cookie中得到用户的信息,并且该扩展可以从对应的cookie中得到用户信息,因此current_user可用。
下面的是在事件触发后,扩展内部的部分代码,handler()就是’message’下面定义的函数:
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是代理后的对象
_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()
当manage_session为True时,扩展会复制一个session到self.server.environ中,并以SID作为key,然后将这个_request_ctx_stack.top.session指向这个session对象,这样就可以在event对应的函数中修改session,但是这个修改的session并不会被保存在client中,因为websocket不支持设置cookie,被修改的session在下次事件被触发时生效。
flask-socketio和js中的socket 默认的域都是socket.io,如果想修改这个值,必须前后端同时修改,否则不能建立,
path = environ['PATH_INFO']
if path is not None and \
path.startswith('/{0}/'.format(self.engineio_path)):
return self.engineio_app.handle_request(environ, start_response)
elif self.wsgi_app is not None:
return self.wsgi_app(environ, start_response)
else:
start_response("404 Not Found", [('Content-type', 'text/plain')])
return ['Not Found']
flask-socketio初始化:
if app is not None:
# here we attach the SocketIO middlware to the SocketIO object so it
# can be referenced later if debug middleware needs to be inserted
self.sockio_mw = _SocketIOMiddleware(self.server, app,
socketio_path=resource)
app.wsgi_app = self.sockio_mw
class _SocketIOMiddleware(socketio.Middleware):
"""This WSGI middleware simply exposes the Flask application in the WSGI
environment before executing the request.
"""
def __init__(self, socketio_app, flask_app, socketio_path='socket.io'):
self.flask_app = flask_app
super(_SocketIOMiddleware, self).__init__(socketio_app,
flask_app.wsgi_app,
socketio_path)
def __call__(self, environ, start_response):
environ = environ.copy()
environ['flask.app'] = self.flask_app
return super(_SocketIOMiddleware, self).__call__(environ,
start_response)
flask-socketio在初始化的时候,会增加一个中间件来分别处理socket通信和http通信,真正处理这件事是engineio.Middleware 。因为socketio的实现是通过engineio实现的。