python Flask 11 Flas使用gevent-websocket 实现 Websocket
之前不知道在哪个场合下提到过如何从web后台向前台推送消息。听闻了反向ajax技术这种模式之后,大呼神奇,试了一下之后发现也确实可以用。不过,反向ajax的代价也很明显,只要客户端还和服务端要有信息交互,服务端就必须还维持客户端的这个请求,然后在合适的时候返回。当客户端一多,这么做的成本会比较大。
其他的后端推前端的技术还有类似于隐藏frame,Comet、长轮询等等,没有详细了解过,总之也是各有千秋但也各有利弊。
前不久在开发中碰到了这样一个场景,就是在后台执行一些代码,然后会根据执行的最新情况推送一些提示信息到前台让用户可以知道目前执行到哪一步了。典型就是一个后台向前端推送消息的,而且是比较简单的一个场景。用反向ajax的话好像略显累赘,因为消息的频度还是蛮高的,应该会消费不少网络资源,而且ajax请求的url后执行的程序肯定和后台的工作程序是并行的,如果要获得工作程序的进度信息可能还会涉及到进程间通信问题,总之各种麻烦。最好能找到一种解决方案,可以在后台随时推送数据后在前台实时展示并且允许后台程序继续跑的。
然后找了下就找到了websocket这种html5之后才有的技术。另外再找了下发现了flask-socketio这个拓展模块添加了flask对websocket的支持。
Websocket简介
1.轮询
机制: 客户端不断向服务端发起请求, 服务端不断询问消息, 回复客户端.
劣势: 前后端持续占用CPU资源,带宽浪费
优势: 数据实时性
应用:例如QQ
2.长轮询
机制: 客户端发起请求,服务端将连接状态保存不返回请求, 服务端有消息时返回状态,一定时间后断开,客户端再次发起长轮询
劣势: 相对轮询数据实时性较差
优势: 网络流量带宽节省, 客户端资源及部分服务端资源节省
3.长链接
WebSocket
机制: 客户端向服务端发起长链接请求,服务端打开与客户端的高速公路不断开
通信过程: 握手, 发送加密消息, 发送解密消息 .
websocket是html5中实现了服务端和客户端进行双向文本或二进制数据通信的一种新协议,其实已经低于HTTP协议本身和HTTP本质上没有什么关系了。不过形式上两者还是有想象之处。因此websocket的连接地址是长这样的:ws://localhost:8080。可以看到,协议修饰符不是http了。
另外,websocket在连接建立阶段是通过HTTP的握手方式进行的,这可以看做是为了兼容浏览器或者使用一些现成的功能来实现,这样一种捷径。当连接建立之后,客户端和服务端之间就不再进行HTTP通信了,所有信息交互都由websocket接管。
从资源占用的角度上来说,其实websocket比ajax占用的资源更多,但它真正实现了全双工通信这一点还是很理想的,意味着无论是前端还是后台的信息交互程序编写都会变得更加方便。由于采用了新的协议,所以我们也需要适当地改造下前后台的程序。
Websocket群聊
Websocket单聊
Websocket握手
Websocket加密解密
WebSocket: ws://192.168.12.47:5000/ WebSocket("ws://192.168.12.47:5000") 搭建服务: from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket # 语法提示 from flask import Flask,request app = Flask(__name__) @app.route("/ws") def ws(): user_socket = request.environ.get("wsgi.websocket") # type:WebSocket user_msg = user_socket.receive() user_socket.send() if __name__ == '__main__': http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler) http_serv.serve_forever() 客户端访问: var ws = new WebSocket("ws://192.168.12.47:9527/ws"); console.log(ws); ws.onopen = function () { alert("websocket is 就绪"); console.log(ws); }; ws.onmessage = function (ws_status) { console.log(ws_status.data) }; ws.onclose = function () { window.location.reload(); } 群聊:[websocket,websocket] for usocket in [websocket,websocket] 单聊: dict = {ID:websocket,ID2:websocket} dict.get(ID2) send_msg = {from_user:ID,to_user:ID2,msg:"hello word"} ID2.send(json.dumps({from_user:ID,to_user:ID2,msg:"hello.amr"})) websocket握手: Sec-WebSocket-Key: jocLOLLq1BQWp0aZgEWL5A== magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' key = jocLOLLq1BQWp0aZgEWL5A==258EAFA5-E914-47DA-95CA-C5AB0DC85B11 new_key = base64(sha1(key)) 将 new_key 返回客户端 websocket解密: websocket加密: import struct msg_bytes = "hello".encode("utf8") token = b"\x81" length = len(msg_bytes) # 5 #"\x81\xshello" if length < 126: token += struct.pack("B", length) elif length == 126: token += struct.pack("!BH", 126, length) else: token += struct.pack("!BQ", 127, length) msg = token + msg_bytes print(msg)