Web端即時通信技術:即時通信技術簡單的說就是實現這樣一種功能:服務器端能夠即時地將數據的更新或變化反應到客戶端,例如消息即時推送等功能都是經過這種技術實現的。可是在Web中,因爲瀏覽器的限制,實現即時通信須要藉助一些方法。這種限制出現的主要緣由是,通常的Web通訊都是瀏覽器先發送請求到服務器,服務器再進行響應完成數據的現實更新。css
實現Web端即時通信的方法:實現即時通信主要有四種方式,它們分別是輪詢、長輪詢(comet)、長鏈接(SSE)、WebSocket。它們大致能夠分爲兩類,一種是在HTTP基礎上實現的,包括短輪詢、comet和SSE;另外一種不是在HTTP基礎上實現是,即WebSocket。下面分別介紹一下這四種輪詢方式,以及它們各自的優缺點。html
當咱們要實現一個實時投票系統,或者是實時通信系統,咱們的頁面數據總須要更新前端
咱們不能讓用戶一直去刷新頁面。因此就有了輪詢,長輪詢,以及websock的出現vue
既然我想要實時獲取後端的數據,那我就每隔2秒給後端發一次請求python
這種咱們就叫輪詢~那它會有一些缺點就是存在延時~就算每秒發一次~也會存在必定的延遲ios
下面咱們看下輪詢的代碼:web
from flask import Flask, render_template, request, jsonify app = Flask(__name__) USERS = { 1: {"name": "悟空", "count": 0}, 2: {"name": "悟能", "count": 0}, 3: {"name": "悟淨", "count": 0}, } @app.route("/") def index(): return render_template("index.html", users=USERS) @app.route("/vote", methods=["POST"]) def vote(): uid = request.json.get("uid") USERS[uid]["count"] += 1 return "投票成功" @app.route("/get_vote") def get_vote(): return jsonify(USERS) if __name__ == '__main__': app.run()
<!DOCTYPE html> <html lang="en"> <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> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script> </head> <body> <h1>選醜大賽</h1> <ul> {% for key, value in users.items()%} <li id="{{key}}" onclick="vote({{key}})">{{value.name}} ({{value.count}})</li> {% endfor %} </ul> <script> function vote(uid) { axios.request({ url: "/vote", method: "POST", data: { "uid": uid } }).then(function (res) { console.log(res.data) }) } function get_vote() { axios.request({ url: "/get_vote", method: "GET" }).then(function (res) { console.log(res) for(let key in res.data){ let liEle = document.getElementById(key); let username = res.data[key]["name"] let count = res.data[key]["count"] liEle.innerText = `${username} (${count})` } }) } window.onload = function () { setInterval(get_vote, 2000) } </script> </body> </html>
輪詢缺點就是延遲,那麼若是前端發送過來請求,若是沒有數據的更新npm
後端的請求就阻塞了,直到有數據返回或者超時再返回,這樣延遲就能夠獲得很好的解決json
python中有個queue對象,當咱們從這個隊列裏拿不到值的時候,能夠阻塞住請求的flask
import queue from flask import Flask app = Flask(__name__) q = queue.Queue() @app.route("/get") def index(): try: val = q.get(timeout=20) except queue.Empty: val = "超時" return val @app.route("/vote") def vote(): q.put("10") return "投票成功" if __name__ == '__main__': app.run()
若是我爲每一個請求都創建一個q對象,而後阻塞住他們的請求,有數據更新的時候,給他們的q對象返回值就能夠了。
from flask import Flask, render_template, request, jsonify, session import queue import uuid app = Flask(__name__) app.secret_key = "lajdgia" USERS = { 1: {"name": "悟空", "count": 0}, 2: {"name": "悟能", "count": 0}, 3: {"name": "悟淨", "count": 0}, } # 爲每一個用戶創建一個q對象 # 以用戶的uuid爲key 值爲q對象 Q_DICT = {} @app.route("/") def index(): user_uuid = str(uuid.uuid4()) session["user_uuid"] = user_uuid Q_DICT[user_uuid] = queue.Queue() return render_template("index2.html", users=USERS) @app.route("/vote", methods=["POST"]) def vote(): # 投票 循環q對象的dict 給每一個q對象返回值 uid = request.json.get("uid") USERS[uid]["count"] += 1 for q in Q_DICT.values(): q.put(USERS) return "投票成功" @app.route("/get_vote", methods=["POST", "GET"]) def get_vote(): # 獲取投票結果 去本身的q對象裏取值 沒有夯住 知道有或者超時返回 user_uuid = session.get("user_uuid") q = Q_DICT[user_uuid] try: users = q.get(timeout=30) except queue.Empty: users = "" return jsonify(users) if __name__ == '__main__': app.run()
<!DOCTYPE html> <html lang="en"> <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> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script> </head> <body> <h1>選醜大賽</h1> <ul> {% for key, value in users.items()%} <li id="{{key}}" onclick="vote({{key}})">{{value.name}} ({{value.count}})</li> {% endfor %} </ul> <script> function vote(uid) { axios.request({ url: "/vote", method: "POST", data: { "uid": uid } }).then(function (res) { console.log(res.data) }) } function get_votes() { axios.request({ url: "/get_vote", method: "POST" }).then(function (res) { console.log(res); if(res.data != ""){ for(let key in res.data){ let liEle = document.getElementById(key); let username = res.data[key]["name"] let count = res.data[key]["count"] liEle.innerText = `${username} (${count})` } } get_votes() }) } window.onload = function () { get_votes() } </script> </body> </html>
websocket是一個協議,協議規定
鏈接的時候須要握手,發送的數據須要加密~~鏈接以後不斷開
Flask不帶websocket,咱們須要下載
下載:pip install gevent-websocket
from flask import Flask, request, render_template from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) USERS = { 1: {"name": "悟空", "count": 0}, 2: {"name": "悟能", "count": 0}, 3: {"name": "悟淨", "count": 0}, } @app.route("/") def index(): return render_template("index3.html", users=USERS) WEBSOCKET_LIST = [] @app.route("/vote") def vote(): ws = request.environ.get("wsgi.websocket") if not ws: return "HTTP請求" WEBSOCKET_LIST.append(ws) while True: uid = ws.receive() if not uid: WEBSOCKET_LIST.remove(ws) ws.close() break uid = int(uid) USERS[uid]["count"] += 1 name = USERS[uid]["name"] new_count = USERS[uid]["count"] for client in WEBSOCKET_LIST: client.send(json.dumps({"uid": uid, "name": name, "count": new_count})) if __name__ == '__main__': http_server = WSGIServer(('127.0.0.1', 5000), app, handler_class=WebSocketHandler) http_server.serve_forever()
<!DOCTYPE html> <html lang="en"> <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> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script> </head> <body> <h1>選醜大賽</h1> <ul> {% for (key, value) in users.items() %} <li onclick="vote({{key}})" id="{{key}}">{{value.name}} ({{value.count}})</li> {% endfor%} </ul> <script> 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>