python之轮询、长轮询、websocket
轮询
ajax轮询 ,ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
1、后端代码
from flask import Flask,render_template app = Flask(__name__) UUUU = { '1':{'name':'王','count':1}, '2':{'name':'李','count':1}, '3':{'name':'赵','count':1}, } @app.route('/index') def index(): return render_template('index.html',user_list = UUUU) if __name__ == '__main__': app.run()
2、前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>最帅的</title> </head> <body> <ul> {% for k,v in user_list.items() %} <li>{{k}}: {{v.name}} {{v.count}} </li> {% endfor %} </ul> <script> # 重点2秒重新请求 function reload() { window.location.reload() } setInterval(reload,2000) </script> </body> </html>
长轮询
long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消 息才返回或超时,返回完之后,客户端再次建立连接,周而复始,基于事件的触发,一个事件接一个事件。
Ajax轮询与long poll都属于不断发送http请求,然后等待服务器处理,可以看到http协议一个特点,被动性,服务端不能主动联系客户端,只有客户端发起。
缺点:Ajax轮询需要服务器有很快的处理速度与快速响应。long poll需要很高的并发,体现在同时容纳请求的能力。
1、后端代码
from flask import Flask,render_template,request,session,redirect,jsonify from uuid import uuid4 from queue import Queue,Empty import json app = Flask(__name__) app.secret_key = "asdfasdfasdf" UUUU = { '1':{'name':'王','count':1}, '2':{'name':'李','count':1}, '3':{'name':'赵','count':1}, } # 为每个登录用户保存 # dfasdfadsfasdfadf: Queue() USER_QUEUE_DICT = { } @app.before_request def check_login(): if request.path == '/login': return None user_info = session.get('user_info') if not user_info: return redirect('/login') @app.route('/login',methods=['GET','POST']) def login(): if request.method == "GET": return render_template('login.html') else: user = request.form.get('user') nid = str(uuid4()) USER_QUEUE_DICT[nid] = Queue() session['user_info'] = {'nid':nid, 'user':user } return redirect('/index') @app.route('/index') def index(): return render_template('index.html',user_list = UUUU) @app.route('/query') def query(): """每个用户查询最新投票信息""" ret = {'status':True,'data':None} current_user_nid = session['user_info']['nid'] queue = USER_QUEUE_DICT[current_user_nid] try: # {'uid':1, 'count':6} ret['data'] = queue.get(timeout=10) #十秒后断开,再连 except Empty as e: ret['status'] = False # return jsonify(ret) return json.dumps(ret) @app.route('/vote') def vote(): """ 用户投票 :return: """ uid = request.args.get('uid') old = UUUU[uid]['count'] new = old + 1 UUUU[uid]['count'] = new for q in USER_QUEUE_DICT.values(): q.put({'uid':uid, 'count':new}) return "投票成功" if __name__ == '__main__': app.run(host='0.0.0.0',threaded=True)
2、前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>最帅</title> </head> <body> <ul> {% for k,v in user_list.items() %} <li style="cursor: pointer;" ondblclick="doVote('{{k}}')" id="user_{{k}}">{{k}}: {{v.name}} <span>{{v.count}}</span> </li> {% endfor %} </ul> <!--<script src="/static/jquery-1.12.4.js"></script>--> <script src="{{ url_for('static',filename='jquery-1.12.4.js') }}"></script> <script> $(function () { get_data(); }) /* 查询最新信息 */ function get_data() { $.ajax({ url: '/query', type:'GET', dataType:'json', success:function (arg) { if(arg.status){ var liId = "#user_" + arg.data.uid; $(liId).find('span').text(arg.data.count); } get_data(); } }) } /* 投票 */ function doVote(uid) { $.ajax({ url:'/vote', // /vote?uid=1 type:'GET', data:{ uid:uid }, success:function (arg) { } }) } </script> </body> </html>
websocket
webSocket是html5一种新的协议,实现了浏览器与服务器之间的全双工通信,能很好的节省服务器资源与带宽,并在服务器端与浏览器端实现实时通行,他建立在TCP之上, 同http一样,通过tcp来传输数据。
只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,服务器端会知道连接的信息,知道客户端关闭请求,同时由服务器主动推送,当有信息需要发送时,直接发送。客户端的连接通过session对象存储,能够实现实时推送。
1、安装
pip3 install gevent-websocket
2、后端代码
from flask import Flask,render_template,request,session,redirect,jsonify import uuid from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) app.secret_key = 'xfsdfqw' USERS = { '1':{'name':'王','count':0}, '2':{'name':'李','count':0}, '3':{'name':'赵','count':0}, } @app.before_request def before_request(): if request.path == '/login': return None user_info = session.get('user_info') if user_info: return None return redirect('/login') @app.route('/login',methods=['GET','POST']) def login(): if request.method == "GET": return render_template('login.html') else: uid = str(uuid.uuid4()) session['user_info'] = {'id':uid,'name':request.form.get('user')} return redirect('/index') @app.route('/index') def index(): return render_template('index.html',users=USERS) # 为每个登录用户保存socket字典 WS_DICT = { } @app.route('/message') def message(): if request.environ.get('wsgi.websocket'): ws = request.environ['wsgi.websocket'] # 1. 刚连接成功 uid = session.get('user_info').get('id') WS_DICT[uid] = ws from geventwebsocket.websocket import WebSocket while True: # 2. 等待用户发送消息,并接受 message = ws.receive() # 关闭:message=None if not message: del WS_DICT[uid] break old = USERS[message]['count'] new = old + 1 USERS[message]['count'] = new data = {'user':message,'count':new} for k,v in WS_DICT.items(): # 3. 向客户端推送消息 v.send(json.dumps(data)) return "Connected!" if __name__ == '__main__': http_server = WSGIServer(('127.0.0.1', 5000), app, handler_class=WebSocketHandler) http_server.serve_forever()
3、前端代码
#login <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post"> <input type="text" name="user"> <input type="submit" value="提交"> </form> </body> </html>
# index <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>投票系统</h1> <a onclick="closeConn();">关闭连接</a> <a onclick="createConn();">创建连接</a> <ul> {% for k,v in users.items() %} <li id="user_{{k}}" ondblclick="vote('{{k}}')">{{v.name}} <span>{{v.count}}</span> </li> {% endfor %} </ul> <script src="{{ url_for('static',filename='jquery-3.3.1.min.js')}}"></script> <script> var socket = null; function socketInit() { socket.onopen = function () { /* 与服务器端连接成功后,自动执行 */ }; socket.onmessage = function (event) { /* 服务器端向客户端发送数据时,自动执行 */ var response = JSON.parse(event.data); // {'user':1,'count':new} var nid = '#user_' + response.user; $(nid).find('span').text(response.count) }; socket.onclose = function (event) { /* 服务器端主动断开连接时,自动执行 */ }; } /* 我要投票 id:帅哥id */ function vote(id) { socket.send(id); } function closeConn() { socket.close() } function createConn() { socket = new WebSocket("ws://127.0.0.1:5000/message"); socketInit(); } </script> </body> </html>