Python中的WebSocket

1、Websockets介紹

    隨着互聯網的發展,傳統的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

 

二,Python中的WebSocket

 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

相關文章
相關標籤/搜索