一、轮询
在一些需要进行实时查询的场景下应用
比如投票系统:
大家一起在一个页面上投票
在不刷新页面的情况下,实时查看投票结果
1、后端代码
from flask import Flask, render_template, request, jsonifyapp = Flask(__name__)USERS = { ???1: {‘name‘: ‘明凯‘, ‘count‘: 300}, ???2: {‘name‘: ‘厂长‘, ‘count‘: 200}, ???3: {‘name‘: ‘7酱‘, ‘count‘: 600},}@app.route(‘/‘)def index(): ???return render_template(‘Poll.html‘, users=USERS)@app.route(‘/vote‘, methods=[‘POST‘])def vote(): ???# 接收uid,通过uid给打野票数 +1 ???# 用户提交Json数据过来,用request.json获取 ???uid = request.json.get(‘uid‘) ???USERS[uid][‘count‘] += 1 ???return "投票成功"@app.route(‘/get_vote‘)def get_vote(): ???# 返回users数据 ???# jsonify 是flask自带的序列化器 ???return jsonify(USERS)if __name__ == ‘__main__‘: ???app.run()
2、前端代码
<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<title>投票系统</title> ???<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> ???<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script></head><style> ???.my-li { ???????list-style: none; ???????margin-bottom: 20px; ???????font-size: 18px; ???}</style><body><div class="container"> ???<div class="row"> ???????<div class="col-md-6 col-md-offset-3"> ???????????<h1>LPL第一打野投票</h1> ???????????{% for (uid, user) in users.items() %} ???????????????<button class="btn btn-success" onclick="vote({{ uid }})">投票</button> ???????????????<li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票数是: {{ user.count }}</li> ???????????{% endfor %} ???????</div> ???</div></div><script> ???// 投票 ???function vote(uid) { ???????// 向后端发送投票请求 ???????axios.request({ ???????????url: ‘/vote‘, ???????????method: ‘post‘, ???????????data: { ???????????????uid: uid ???????????} ???????}).then(function (response) { ???????????console.log(response.data); ???????}) ???} ???// 获取最新的投票结果 ???function get_vote() { ???????axios.request({ ???????????url: ‘/get_vote‘, ???????????method: ‘get‘ ???????}).then(function (response) { ???????????// 获取后端传过来的新数据 ???????????// 重新渲染页面 ???????????let users = response.data; ???????????for (let uid in users) { ???????????????//根据uid获取li标签 改变innerText ???????????????let liEle = document.getElementById(uid); ???????????????liEle.innerText = `${users[uid][‘name‘]}目前的票数是: ${users[uid][‘count‘]}` ???????????} ???????}); ???} ???// 页面加载完后,立刻获取数据 ???window.onload = function () { ???????setInterval(get_vote, 2000) ???}</script></body></html>
3、轮询
特点:每隔一段时间不断向后端发送请求
缺点:消耗大 有延迟
二、长轮询
由于上面的轮询是不能实时查看到投票情况的,存在一定的延迟性
长轮询可以实现实时查看投票情况
1、后端代码
from flask import Flask, render_template, request, jsonify, sessionimport uuidimport queueapp = Flask(__name__)app.secret_key = ‘切克闹‘USERS = { ???1: {‘name‘: ‘明凯‘, ‘count‘: 300}, ???2: {‘name‘: ‘厂长‘, ‘count‘: 200}, ???3: {‘name‘: ‘7酱‘, ‘count‘: 600},}Q_DICT = { ???# uid: q对象}@app.route(‘/‘)def index(): ???# 模拟用户登录 ???# 模拟用户登录后的唯一id ???user_id = str(uuid.uuid4()) ???# 每个用户都有自己的Q对象 ???Q_DICT[user_id] = queue.Queue() ???# 把用户的id存到session ???session[‘user_id‘] = user_id ???# 页面展示投票的人的信息 ???return render_template(‘longPoll.html‘, users=USERS)@app.route(‘/vote‘, methods=[‘POST‘])def vote(): ???# 处理投票,给打野的票数 +1 ???# 用户提交Json数据过来,用request.json获取 ???uid = request.json.get(‘uid‘) ???USERS[uid][‘count‘] += 1 ???# 投票成功后,给每个用户的Q对象put最新的值进去 ???for q in Q_DICT.values(): ???????q.put(USERS) ???return "投票成功"@app.route(‘/get_vote‘)def get_vote(): ???# 请求进来,从session获取用户的id ???user_id = session.get(‘user_id‘) ???# 根据用户的id 获取用户的Q对象 ???q = Q_DICT.get(user_id) ???# try: ???# ????ret = q.get(timeout=30) ???# except queue.Empty: ???# ????ret = ‘‘ ???ret = q.get() ???return jsonify(ret)if __name__ == ‘__main__‘: ???app.run()
2、前端代码
<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<title>投票系统</title> ???<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> ???<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script></head><style> ???.my-li { ???????list-style: none; ???????margin-bottom: 20px; ???????font-size: 18px; ???}</style><body><div class="container"> ???<div class="row"> ???????<div class="col-md-6 col-md-offset-3"> ???????????<h1>LPL第一打野投票</h1> ???????????{% for (uid, user) in users.items() %} ???????????????<button class="btn btn-success" onclick="vote({{ uid }})">投票</button> ???????????????<li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票数是: {{ user.count }}</li> ???????????{% endfor %} ???????</div> ???</div></div><script> ???// 投票 ???function vote(uid) { ???????// 向后端发送投票请求 ???????axios.request({ ???????????url: ‘/vote‘, ???????????method: ‘post‘, ???????????data: { ???????????????uid: uid ???????????} ???????}).then(function (response) { ???????????console.log(response.data); ???????}) ???} ???// 获取最新的投票结果 ???function get_vote() { ???????axios.request({ ???????????url: ‘/get_vote‘, ???????????method: ‘get‘ ???????}).then(function (response) { ???????????// 判断后端的数据是否为空 ???????????if (response.data != ‘‘) { ???????????????// 获取到最新的数据 ???????????????let users = response.data; ???????????????for (uid in users) { ???????????????????// 根据uid找到每个li标签 ???????????????????let liEle = document.getElementById(uid); ???????????????????// 给每个li标签设置最新的数据 ???????????????????liEle.innerText = `${users[uid][‘name‘]}目前的票数是: ${users[uid][‘count‘]}` ???????????????} ???????????} ???????????// 获取完数据后,再发送请求,看还有没有人投票,有的话再去获取最新的数据 ???????????get_vote() ???????}); ???} ???// 页面加载完后,立刻获取数据 ???window.onload = function () { ???????get_vote() ???}</script></body></html>
3、长轮询
特点:满足实时更新
缺点:消耗大
实现:
利用queue对象实现请求夯住
每个请求进来都要生成一个q对象
如果有人投票 给所有的q对象put数据
拿数据请求从自己的q对象get数据
三、websocket
1、介绍
http协议
短连接 无状态 基于TCP/UDP协议进行传输数据(TCP/UDP: 传输协议)
socket
socket不是传输协议 跟websocket是两个完全不一样的东西 socket是套接字 API接口
websocket
H5出的新协议 浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
解决轮询问题
特点:
1. 握手 基于HTTP进行握手(因此websocket与Http有一定的交集,但不是同一个东西)
2. 发送数据加密
3. 保持连接不断开
2、在Flask中使用websocket
1. Flask没有websocket,需要安装包 ?pip install gevent-websocket2. 后端怎样建立一个支持websocket协议连接from geventwebsocket.handler import WebSocketHandlerfrom gevent.pywsgi import WSGIServer# 拿到websocket对象ws = request.environ.get("wsgi.websocket")# 后端发送数据ws.send(xxx)# 后端接收数据ws.receive()if __name__ == ‘__main__‘: ???# app.run() ???# 即支持HTTP 也支持websocket ???http_server = WSGIServer((‘0.0.0.0‘, 5000), app, handler_class=WebSocketHandler) ???http_server.serve_forever()3. 前端怎么发起websocket连接let ws = new WebSocket("ws://127.0.0.1:5000")# 前端发送数据ws.send("xxx")# 前端接收数据ws.onmessage = function(event){ ???# 注意数据数据类型的转换 ???????????let data = event.data}4. 收发消息1, 前端发送数据给后端 ???前端发送:ws.send(‘数据‘) ???后端接收:ws.receive() ???2, 后端发送数据给前端 ???后端发送:ws.send(‘数据‘) ???前端接收:ws.onmessage = function(event){ ???????let data = event.data ???} ???
3、Demo
1.后端
from flask import Flask, render_template, requestfrom geventwebsocket.handler import WebSocketHandlerfrom gevent.pywsgi import WSGIServerimport jsonapp = Flask(__name__)USERS = { ???1: {‘name‘: ‘明凯‘, ‘count‘: 300}, ???2: {‘name‘: ‘厂长‘, ‘count‘: 200}, ???3: {‘name‘: ‘7酱‘, ‘count‘: 600},}WEBSOCKET_LIST = []@app.route(‘/‘)def index(): ???return render_template(‘websocket.html‘, users=USERS)@app.route(‘/vote‘)def vote(): ???# 处理websocket ???# 判断是什么类型的请求,HTTP还是websocket ???# 看能否获取得到websocket的对象 ???ws = request.environ.get("wsgi.websocket") ???if not ws: ???????return "这是HTTP协议的请求" ???# 把所有用户的ws对象存到一个列表 ???WEBSOCKET_LIST.append(ws) ???while True: ???????# 获取前端传过来的uid,给打野票数 +1 ???????uid = ws.receive() ???????# 如果前端主动断开连接 ???????# 那么后端也关闭与前端的连接 ???????if not uid: ???????????WEBSOCKET_LIST.remove(ws) ???????????ws.close() ???????????break ???????uid = int(uid) ???????USERS[uid]["count"] += 1 ???????data = { ???????????"uid": uid, ???????????"name": USERS[uid]["name"], ???????????"count": USERS[uid]["count"] ???????} ???????for ws in WEBSOCKET_LIST: ???????????# 给前端发送新的数据 ???????????ws.send(json.dumps(data))if __name__ == ‘__main__‘: ???# app.run() ???# 这样启服务的意思是:即支持HTTP协议,也支持websocket协议 ???http_server = WSGIServer((‘127.0.0.1‘, 5000), app, handler_class=WebSocketHandler) ???http_server.serve_forever()
2.前端
<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<title>投票系统</title> ???<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> ???<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script></head><style> ???.my-li { ???????list-style: none; ???????margin-bottom: 20px; ???????font-size: 18px; ???}</style><body><div class="container"> ???<div class="row"> ???????<div class="col-md-6 col-md-offset-3"> ???????????<h1>LPL第一打野投票</h1> ???????????{% for (uid, user) in users.items() %} ???????????????<button class="btn btn-success" onclick="vote({{ uid }})">投票</button> ???????????????<li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票数是: {{ user.count }}</li> ???????????{% endfor %} ???????</div> ???</div></div><script> ???// 向后端发送一个websocket连接请求 ???let ws = new WebSocket(‘ws://127.0.0.1:5000/vote‘); ???function vote(uid) { ???????// 向后端发数据 ???????ws.send(uid) ???} ???ws.onmessage = function (event) { ???????let data = JSON.parse(event.data); ???????let liEle = document.getElementById(data.uid); ???????liEle.innerText = `${data.name}目前的票数是: ${data.count}` ???}</script></body></html>
轮询、长轮询和websocket
原文地址:https://www.cnblogs.com/Zzbj/p/10222887.html