隨着互聯網的發展,傳統的HTTP協議已經很難知足Web應用日益複雜的需求了。近年來,隨着HTML5的誕生,WebSocket協議被提出,它實現了瀏覽器與服務器的全雙工通訊,擴展了瀏覽器與服務端的通訊功能,使服務端也能主動向客戶端發送數據。
咱們知道,傳統的HTTP協議是無狀態的,每次請求(request)都要由客戶端(如 瀏覽器)主動發起,服務端進行處理後返回response結果,而服務端很難主動向客戶端發送數據;這種客戶端是主動方,服務端是被動方的傳統Web模式 對於信息變化不頻繁的Web應用來講形成的麻煩較小,而對於涉及實時信息的Web應用卻帶來了很大的不便,如帶有即時通訊、實時數據、訂閱推送等功能的應 用。在WebSocket規範提出以前,開發人員若要實現這些實時性較強的功能,常常會使用折衷的解決方法:輪詢(polling)和Comet技術。其實後者本質上也是一種輪詢,只不過有所改進。
輪詢是最原始的實現實時Web應用的解決方案。輪詢技術要求客戶端以設定的時間間隔週期性地向服務端發送請求,頻繁地查詢是否有新的數據改動。明顯地,這種方法會致使過多沒必要要的請求,浪費流量和服務器資源。
Comet技術又能夠分爲長輪詢和流技術。長輪詢改進了上述的輪詢技術,減少了無用的請求。它會爲某些數據設定過時時間,當數據過時後纔會向服務端發送請求;這種機制適合數據的改動不是特別頻繁的狀況。流技術一般是指客戶端使用一個隱藏的窗口與服務端創建一個HTTP長鏈接,服務端會不斷更新鏈接狀態以保持HTTP長鏈接存活;這樣的話,服務端就能夠經過這條長鏈接主動將數據發送給客戶端;流技術在大併發環境下,可能會考驗到服務端的性能。
這兩種技術都是基於請求-應答模式,都不算是真正意義上的實時技術;它們的每一次請求、應答,都浪費了必定流量在相同的頭部信息上,而且開發複雜度也較大。
伴隨着HTML5推出的WebSocket,真正實現了Web的實時通訊,使B/S模式具有了C/S模式的實時通訊能力。WebSocket的工做流程是這 樣的:瀏覽器經過JavaScript向服務端發出創建WebSocket鏈接的請求,在WebSocket鏈接創建成功後,客戶端和服務端就能夠經過 TCP鏈接傳輸數據。由於WebSocket鏈接本質上是TCP鏈接,不須要每次傳輸都帶上重複的頭部數據,因此它的數據傳輸量比輪詢和Comet技術小了不少.javascript
關於websocket的優勢能夠看:http://www.tuicool.com/articles/7zyMvy6html
Django的WebSocket:前端
1.dwebsocket: https://www.cnblogs.com/huguodong/p/6611602.htmljava
2.channels: http://www.cnblogs.com/evilliu/articles/6529087.htmlpython
參考連接:https://www.cnblogs.com/jingmoxukong/p/7755643.htmlweb
這裏使用flask做爲服務器,python版本爲3.6.5json
安裝模塊flask
pip install gevent-websocket
羣聊segmentfault
from flask import Flask,request,render_template from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket import json app = Flask(__name__) user_dict = {} # 空字典,用來存放用戶名和發送消息 @app.route("/<username>") # 參數爲用戶名 def index(username): # 獲取請求的WebSocket對象 user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: # 設置鍵值對 # {'xiao': <geventwebsocket.websocket.WebSocket object at 0x0000020F6F6B8DB0>} user_dict[username] = user_socket print(user_dict) # 循環,接收消息 while True: # 接收消息 msg = user_socket.receive() print(msg) # 反序列化數據,由於前端發送的是json recv_msg = json.loads(msg) print(recv_msg) # 構造數據結構 send_msg = { # 獲取用戶名 "username":recv_msg.get("username"), # 獲取消息 "msg":recv_msg.get("msg") } # 遍歷字典 for i in user_dict.values(): # 這裏的i就是websocket對象 # 判斷websocket對象等於請求的websocket對象 if i == user_socket: # 跳過循環 continue # 發送數據,對數據作序列化 i.send(json.dumps(send_msg)) @app.route("/ws") def ws(): return render_template("many_people.html") if __name__ == '__main__': # 建立一個WebSocket服務器 http_serv = WSGIServer(("0.0.0.0",5000),app,handler_class=WebSocketHandler) # 開始監聽HTTP請求 http_serv.serve_forever() # app.run("0.0.0.0", 5000, debug=True)
前端後端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 你的暱稱:<input type="text" id="nickname"> <button onclick="connws()">鏈接服務器</button> <br><br> 發送消息:<input type="text" id="talk"> <button onclick="send_msg()">發送信息</button><br><br> <div style="width: 500px;height: 100%;border: 1px red solid;" id="text"> </div> </body> <script type="application/javascript"> var user_name = null; //用戶名 var ws = null; //WebSocket 對象,默認設置爲空 //鏈接ws function connws() { //獲取輸入框中的用戶名 user_name = document.getElementById("nickname").value; //建立 WebSocket 對象 ws = new WebSocket("ws://127.0.0.1:5000/" + user_name); //客戶端接收服務端數據時觸發 ws.onmessage = function (data) { // 反序列化接收數據 var recv_msg = JSON.parse(data.data); console.log(recv_msg); // 執行自定義函數createDiv,傳入2個參數 createDiv(recv_msg.username, recv_msg.msg); }; } //發送消息 function send_msg() { // 獲取輸入框的發送消息 var talk = document.getElementById("talk").value; // 執行自定義函數createDiv createDiv("w", talk); // 組件發送數據對象 send_str = { username:user_name, //用戶名 msg:talk //消息 }; //使用鏈接發送數據,序列化對象 ws.send(JSON.stringify(send_str)); }; //顯示聊天信息 function createDiv(self, content) { // 建立div標籤 var divtag = document.createElement("div"); //定義格式 var who = self + " : "; // 判斷參數爲w時 if (self == "w") { // 替換字符串 who = "我 : " } // 修改顯示框的text屬性 divtag.innerText = who + content; // 獲取顯示框 var text = document.getElementById("text"); // appendChild() 方法向節點添加最後一個子節點 // 添加一個div標籤 text.appendChild(divtag); } </script> </html>
單聊
from flask import Flask,request,render_template from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket import json app = Flask(__name__) user_dict = {} # 空字典,用來存放用戶名和發送消息 @app.route("/<username>") # 參數爲用戶名 def index(username): # 獲取請求的WebSocket對象 user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: # 設置鍵值對 # {'xiao': <geventwebsocket.websocket.WebSocket object at 0x0000020F6F6B8DB0>} user_dict[username] = user_socket print(user_dict) # 循環,接收消息 while True: # 接收消息 msg = user_socket.receive() # print(msg) # 反序列化數據,由於前端發送的是json recv_msg = json.loads(msg) print(recv_msg) # 構造數據結構 send_msg = { # 消息 "msg": recv_msg.get("msg"), # 來自於哪一個用戶 "from_user": username, } # 獲取聊天對象的名字 to_user = user_dict.get(recv_msg.get("to_user")) # 發送數據 to_user.send(json.dumps(send_msg)) @app.route("/ws") def ws(): return render_template("single_chat.html") if __name__ == '__main__': # 建立一個WebSocket服務器 http_serv = WSGIServer(("0.0.0.0",5000),app,handler_class=WebSocketHandler) # 開始監聽HTTP請求 http_serv.serve_forever() # app.run("0.0.0.0", 5000, debug=True)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 你的暱稱:<input type="text" id="nickname"> <button onclick="connws()">鏈接服務器</button> <br> 與誰說話:<input type="text" id="sender"> <br> 發送消息:<input type="text" id="talk"> <button onclick="send_msg()">發送信息</button><br/><br/> <div style="width: 500px;height: 100%;border: 1px red solid;" id="text"> </div> </body> <script type="application/javascript"> var user_name = null; //用戶名 var ws = null; //WebSocket 對象,默認設置爲空 //鏈接ws function connws() { //獲取輸入框中的用戶名 user_name = document.getElementById("nickname").value; //建立 WebSocket 對象 ws = new WebSocket("ws://127.0.0.1:5000/" + user_name); //客戶端接收服務端數據時觸發 ws.onmessage = function (data) { // 反序列化接收數據 var recv_msg = JSON.parse(data.data); console.log(recv_msg); // 執行自定義函數createDiv,傳入2個參數 createDiv(recv_msg.from_user, recv_msg.msg); }; } function send_msg() { // 獲取輸入框的發送消息 var talk = document.getElementById("talk").value; // 獲取輸入框的聊天對象 var sender = document.getElementById("sender").value; // 執行自定義函數createDiv createDiv("w", talk); // 構造發送數據對象 send_str = { msg:talk, //消息 to_user:sender, //對方 }; //使用鏈接發送數據,序列化對象 ws.send(JSON.stringify(send_str)); }; //顯示聊天信息 function createDiv(self, content) { // 建立div標籤 var divtag = document.createElement("div"); //定義格式 var who = self + " : "; // 判斷參數爲w時 if (self == "w") { // 替換字符串 who = "我 : " } // 修改顯示框的text屬性 divtag.innerText = who + content; // 獲取顯示框 var text = document.getElementById("text"); // appendChild() 方法向節點添加最後一個子節點 // 添加一個div標籤 text.appendChild(divtag); } </script> </html>
相關拓展:https://segmentfault.com/a/1190000010140660