知识点回顾:
- 什么是轮训?
- 通过定时器让程序每隔n秒执行一次操作。
- 什么是长轮训?
- 浏览器向后端发起请求,后端会将请求 hang 住,最多hang 30s。
如果一直不返回数据:则最多等待30s,紧接着用户立即再发送请求。
如果有数据返回:则操作数据并立即再发送请求。
PS:后台可以使用队列或redis的列表来hang住请求。
- 轮训和长轮训目的?
由于Http请求是无状态、短连接所以服务端无法向客户端实时推送消息,
所以,我们就是可以使用:轮训和长轮训去服务端获取实时数据。
websocket
websocket是什么?
websocket是一套类似于http的协议。
websocket协议:\r\n分割、创建连接后不断开、 验证+数据加密;
websocket本质:
- 就是一个创建连接后不断开的socket
- 当连接成功之后:
- 客户端(浏览器)会自动向服务端发送消息,包含: Sec-WebSocket-Key: 校验
- 服务端接收之后,会对于该数据进行加密:base64(sha1(swk + magic_string))
- 构造响应头:
HTTP/1.1 101 Switching Protocols\r\n
Upgrade:websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: 加密后的值\r\n
WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n
- 发给客户端(浏览器)
- 建立:双工通道,接下来就可以进行收发数据
- 发送的数据是加密,解密,根据payload_len的值进行处理:
- payload_len <=125
- payload_len ==126
- payload_len ==127
- 获取内容:
- mask_key
- 数据
根据mask_key和数据进行位运算,就可以把值解析出来。
在项目中使用:
- django: channel
- flask: gevent-websocket
- tornado: 内置
flask示例
安装:pip3 install gevent-websocket
作用:
- 处理Http、Websocket协议的请求 -> socket
- 封装Http、Websocket相关数据 -> request
基本结构:
from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer @app.route('/test') def test(): ws = request.environ.get('wsgi.websocket') ws.receive() ws.send(message) ws.close() return render_template('index.html') if __name__ == '__main__': http_server = WSGIServer(('0.0.0.0', 5000,), app, handler_class=WebSocketHandler) http_server.serve_forever()
实现一个web聊天室
后端:
from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from flask import Flask, render_template, request import pickle app = Flask(__name__) app.secret_key = 'xfsdfqw' @app.route('/index') def index(): return render_template('index.html') WS_LIST = [] @app.route('/test') def test(): ws = request.environ.get('wsgi.websocket') if not ws: return '请使用WebSocket协议' # websocket连接已经成功 WS_LIST.append(ws) while True: # 等待用户发送消息,并接受 message = ws.receive() # 关闭:message=None if not message: WS_LIST.remove(ws) break for item in WS_LIST: item.send(message) return "asdfasdf" if __name__ == '__main__': http_server = WSGIServer(('0.0.0.0', 5000,), app, handler_class=WebSocketHandler) http_server.serve_forever()
前端:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <style> .msg-item { padding: 5px; border: 1px; margin: 0 5px; } </style> </head> <body> <h1>首页</h1> <div> <h2>发送消息</h2> <input id="msg" type="text"/> <input type="button" value="发送" onclick="sendMsg()"> <h2>接收消息</h2> <div id="container"> </div> </div> <script src="/static/jquery-3.3.1.min.js"></script> <script> ws = new WebSocket('ws://192.168.12.42:5000/test'); ws.onmessage = function (event) { var tag = document.createElement('div'); tag.className = 'msg-item'; tag.innerText = event.data; $('#container').append(tag); } function sendMsg() { ws.send($('#msg').val()); } </script> </body> </html>
问题:
a. 实时消息推送,利用什么技术实现?
- 轮训 ,优点:简单; 缺点:请求次数多,服务器压力大,消息延迟。
- 长轮训 ,优点:实时接收数据,兼容性好; 缺点:请求次数比原来少,但是相对来也不少。
- websocket ,优点:代码简单,不再反复创建连接。 缺点:兼容性。
b. 在框架中使用WebSocket:
- django,channel
- flask,gevent-websocket
- tornado,内置