1. 程式人生 > >websocket實現群聊和單聊(轉)

websocket實現群聊和單聊(轉)

isp turn ext str 17. 釋放 關閉連接 動態參數 發送信息

昨日內容回顧

技術分享圖片
1.Flask路由
    1.endpoint="user" # 反向url地址
    2.url_address = url_for("user")
    3.methods = ["GET","POST"] # 允許請求進入視圖函數的方式
    4.redirect_to # 在進入視圖函數之前重定向
    5./index/<nid> # 動態參數路由 <int:nid> def index(nid)
    6.strict_slashes # 是否嚴格要求路由地址 /
    7.defaults={"nid":1} #
def index(nid) 2.Flask初始化配置(實例化): 1.template_folder # 指定模板路徑 2.static_url_path # 指定靜態文件目錄的URL地址 3.static_folder # 指定靜態文件目錄路徑 3.Flask對象配置 1.DEBUG #開發模式的調試功能 True False 2.app.config.from_object(class) # 通過對象的方式導入配置 3.secret_key # 開啟session功能的時候需要添加的配置 4.Blueprint
1.將功能和主程序分離,註冊 2.bl = Blueprint("dongdong",__name__) 3.註冊 register_blueprint(bl) 5.send_file jsonify 1.send_file # 打開並返回文件 content-type:文件類型 2.jsonify # 將一個字符串 轉為JSON格式 加入 content-type:application/json 頭 6.特殊的裝飾器: 1.before_request # 在請求進入視圖函數之前執行的函數(登錄認證) 2.after_request #
在請求響應回瀏覽器之前執行的函數 3.before_first_request # 在第一次請求進入視圖函數之前執行的函數 4.errorheader(404) # 當遇到此類錯誤響應的時候(自定義錯誤頁面) 7.flash 1.flash("msg","tag") # 閃現存儲 2.get_flashed_messages(category_filter=["tag"]) # 閃現取值 只要用到了get_flashed_messages就一定清空flash
View Code

轉:https://www.cnblogs.com/xiao987334176/p/9605536.html#autoid-3-5-0

websocket

WebSocket 是什麽?

WebSocket 是一種網絡通信協議。RFC6455 定義了它的通信標準。

WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。

為什麽需要 WebSocket ?

了解計算機網絡協議的人,應該都知道:HTTP 協議是一種無狀態的、無連接的、單向的應用層協議。它采用了請求/響應模型。通信請求只能由客戶端發起,服務端對請求做出應答處理。

這種通信模型有一個弊端:HTTP 協議無法實現服務器主動向客戶端發起消息。

這種單向請求的特點,註定了如果服務器有連續的狀態變化,客戶端要獲知就非常麻煩。大多數 Web 應用程序將通過頻繁的異步JavaScript和XML(AJAX)請求實現長輪詢。輪詢的效率低,非常浪費資源(因為必須不停連接,或者 HTTP 連接始終打開)。

技術分享圖片

因此,工程師們一直在思考,有沒有更好的方法。WebSocket 就是這樣發明的。WebSocket 連接允許客戶端和服務器之間進行全雙工通信,以便任一方都可以通過建立的連接將數據推送到另一端。WebSocket 只需要建立一次連接,就可以一直保持連接狀態。這相比於輪詢方式的不停建立連接顯然效率要大大提高。

技術分享圖片

WebSocket 如何工作?

Web瀏覽器和服務器都必須實現 WebSockets 協議來建立和維護連接。由於 WebSockets 連接長期存在,與典型的HTTP連接不同,對服務器有重要的影響。

基於多線程或多進程的服務器無法適用於 WebSockets,因為它旨在打開連接,盡可能快地處理請求,然後關閉連接。任何實際的 WebSockets 服務器端實現都需要一個異步服務器。

WebSocket 客戶端

在客戶端,沒有必要為 WebSockets 使用 JavaScript 庫。實現 WebSockets 的 Web 瀏覽器將通過 WebSockets 對象公開所有必需的客戶端功能(主要指支持 Html5 的瀏覽器)。

客戶端 API

以下 API 用於創建 WebSocket 對象。

var Socket = new WebSocket(url, [protocol] );

以上代碼中的第一個參數 url, 指定連接的 URL。第二個參數 protocol 是可選的,指定了可接受的子協議。

WebSocket 屬性

以下是 WebSocket 對象的屬性。假定我們使用了以上代碼創建了 Socket 對象:

屬性描述
Socket.readyState 只讀屬性 readyState 表示連接狀態,可以是以下值:0 - 表示連接尚未建立。1 - 表示連接已建立,可以進行通信。2 - 表示連接正在進行關閉。3 - 表示連接已經關閉或者連接不能打開。
Socket.bufferedAmount 只讀屬性 bufferedAmount 已被 send() 放入正在隊列中等待傳輸,但是還沒有發出的 UTF-8 文本字節數。

WebSocket 事件

以下是 WebSocket 對象的相關事件。假定我們使用了以上代碼創建了 Socket 對象:

事件事件處理程序描述
open Socket.onopen 連接建立時觸發
message Socket.onmessage 客戶端接收服務端數據時觸發
error Socket.onerror 通信發生錯誤時觸發
close Socket.onclose 連接關閉時觸發

WebSocket 方法

以下是 WebSocket 對象的相關方法。假定我們使用了以上代碼創建了 Socket 對象:

方法描述
Socket.send() 使用連接發送數據
Socket.close() 關閉連接

示例

技術分享圖片
// 初始化一個 WebSocket 對象
var ws = new WebSocket("ws://localhost:9998/echo");

// 建立 web socket 連接成功觸發事件
ws.onopen = function () {
  // 使用 send() 方法發送數據
  ws.send("發送數據");
  alert("數據發送中...");
};

// 接收服務端數據時觸發事件
ws.onmessage = function (evt) {
  var received_msg = evt.data;
  alert("數據已接收...");
};

// 斷開 web socket 連接成功觸發事件
ws.onclose = function () {
  alert("連接已關閉...");
};
技術分享圖片

WebSocket 服務端

WebSocket 在服務端的實現非常豐富。Node.js、Java、C++、Python 等多種語言都有自己的解決方案。

以下,介紹我在學習 WebSocket 過程中接觸過的 WebSocket 服務端解決方案。

群聊

項目結構如下:

./
├── chat.py
└── templates
    └── many_people.html

服務器

這裏使用flask作為服務器,python版本為3.6.5

安裝模塊

pip install gevent-websocket

chat.py

技術分享圖片 技術分享圖片
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>
View Code

啟動flask程序,訪問頁面: http://127.0.0.1:5000/ws

技術分享圖片

開3個網頁,輸入昵稱,開始聊天。註意:每個網頁,連接服務器一次,就可以了!

技術分享圖片

還可以多開幾個網頁,幾個人,可以同時聊天。

單聊

單聊,需要指定一個用戶,才能發起一對一聊天!

項目結構如下:

./
├── one_chat.py
└── templates
    └── single_chat.html

服務器

one_chat.py

技術分享圖片
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)
View Code

客戶端

single_chat.html

技術分享圖片
<!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>
View Code

效果如下:

技術分享圖片

總結:

技術分享圖片
1.DButils 數據庫連接池
    創建連接池同時創建連接
    用到連接時從連接池中抽取一個連接
    釋放連接時將連接放回連接池中
    節省與mysql的通訊次數和時長
 
2.Websocket 通訊協議 

 Web + socket
 QQ 即時通訊軟件 97 
 
 初期輪詢:
    QQ 聯眾 軟件不斷的循環訪問服務器問它有沒有給我發送的消息
    優點:響應及時
    缺點:浪費CPU資源,浪費帶寬
 
 長輪詢:
    當客戶端發起詢問,服務器說你等著1分鐘之後,你再來問我
    斷開再次發起連接,服務器幫你輪詢
    優點:響應及時
    缺點:用戶一旦形成規模,服務器消耗是致命的
 
 新的協議 websocket 
    規定了一個數據格式
    收發數據
    該收就收
    該發就發

3.群聊

4.私聊
View Code

websocket實現群聊和單聊(轉)