websocket實現群聊和單聊(轉)
昨日內容回顧
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} #View Codedef 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.Blueprint1.將功能和主程序分離,註冊 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
轉: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實現群聊和單聊(轉)