flask_socketio收發消息


此過程結合flask-socketio 和 sockeio.js 講解

1.初始 flask-socketio

https://blog.csdn.net/eleanoryss/article/details/109600154

這個鏈接是介紹 websocket 和 HTTP 長連接的差別

websocket 說白一點就是,建立客戶端和服務端雙向通訊通道, 服務器可以主動向客戶端發消息。

https://www.jianshu.com/p/d81397edd2b1

上邊這個鏈接是一個對官方文檔的翻譯。

2.安裝

python 3.9
socket.io.js 3.1.3
https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.js

pip install flask-socketio

依賴安裝

pip install eventlet

3.初始化項目

from flask_socketio import SocketIO
import random
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

if __name__ == '__main__':
    socketio.run(app)
# 或者 set flask_app=app  app指的是項目
# flask run 

# 兩種方式都可以
# socketio = SocketIO()
# socketio.init_app(app)

4. 客戶端js

相關文檔

https://socket.io/get-started/chat#Integrating-Socket-IO
cdn 資源
https://cdnjs.com/libraries/socket.io

4.接收消息和發送消息

客戶端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.js" integrity="sha512-2RDFHqfLZW8IhPRvQYmK9bTLfj/hddxGXQAred2wNZGkrKQkLGj8RCkXfRJPHlDerdHHIzTFaahq4s/P4V6Qig==" crossorigin="anonymous"></script>
</head>
<body>
<script type="text/javascript">
    $(document).ready(function() {
        
        name_space = "/test";
        var socket = io.connect("http://127.0.0.1:5000");
        
        //  連接成功后發送消息
        socket.on("connect", function(){
            //  發送普通消息
            socket.send("connect once");
        })
        
	   // 接收后台發送至ceishi的消息
        socket.on("ceshi", function(data){
            console.log("有名" + data)
        })

        // 接收后台消息
        socket.on("message", function(data){
            console.log("無名"+data)
        })

    });
</script>
</body>
</html>

后台

@socketio.on("message")
def message(msg):
    print("message", msg)
    socketio.send(msg, broadcast=True)
    socketio.emit('ceshi', "測試")
    
    
#  接受 前端向json 接口發送的數據 對應前端 --> socket.emit("json", {"hello": "world"})
@socketio.on("json")
def json_msg(msg):
    print("json", msg)
    socketio.send(msg, broadcast=True)

講解:

之前看文檔不太明白,測試了好久.

socketio 發送消息分為 send 和 emit

​ send(): 發送至未命名的一般默認為message, 一會來講解message是什么

​ emit(): 發送到指定接受的活動上.

socketio.on

你可以認為這個是socketio 接收函數的接口, 用在客戶端<前端>就相當於 websocket 的 ws.onmessage 接受后台傳過來的數據, 用在后台同樣

socketio.emit("活動名", 消息, namespace<命名空間>)

在上邊代碼有一句:

socketio.emit('ceshi', "測試") # 這句話的意思是, 發送至活動ceshi 一條消息為 "測試"

在客戶端接收的地方為:

socket.on("ceshi", function(data){
            console.log("有名" + data)
        }) # 接受后台發送到 ceshi 活動的信息

后台同樣:

@socketio.on("message") # 接受發送到 message 活動的消息
@socketio.on("json") # 接受發送到 json 活動的消息 
@socketio.on("connect") # 當客戶端連接的時候觸發
@socketio.on("disconnect") # 當客戶端斷開連接的時候觸發
# 除外你還可以自定義名字
socketio.send

不指定活動名發送, 一般默認發送到 message 中

namespace: 下邊講解

客戶端代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.js" integrity="sha512-2RDFHqfLZW8IhPRvQYmK9bTLfj/hddxGXQAred2wNZGkrKQkLGj8RCkXfRJPHlDerdHHIzTFaahq4s/P4V6Qig==" crossorigin="anonymous"></script>
</head>
<body>
<script type="text/javascript">
    $(document).ready(function() {
        
        name_space = "/test";
        // 連接后台
        var socket = io.connect("http://127.0.0.1:5000");
        
        //  連接成功后發送消息
        socket.on("connect", function(){
            //  發送普通消息
            socket.send("connect once");
            // 發送json數據
            socket.emit("json", {"hello": "world"})
        })

        socket.on("ceshi", function(data){
            console.log("有名" + data)
        })

        // 接受后台消息
        socket.on("message", function(data){
            console.log("無名"+data)
        })
    });
</script>
</body>
</html>

后台代碼:

#encoding:utf-8
#!/usr/bin/env python
from flask import Flask, render_template
from flask_socketio import SocketIO
import random
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

 
@app.route('/')
def index():
    return render_template('socketio.html')
 

# 默認接受send 發送過來的, 同時也可以接受 emit("message", "測試")
@socketio.on("message")
def message(msg):
    print("message", msg)
    socketio.send(msg, broadcast=True)
    socketio.emit('ceshi', "測試")


#  接受 前端向json 接口發送的數據 對應前端 --> socket.emit("json", {"hello": "world"})
@socketio.on("json")
def json_msg(msg):
    print("json", msg)
    socketio.send(msg, broadcast=True)
    
if __name__ == '__main__':
    socketio.run(app, debug=True)

*** 至此你可以通過將接受的數據添加到頁面中的某個div,簡單實現一個在線聊天

broadcast = True

這是 send 和 emit 的參數,指廣播, 加上這個之后所有在指定命名空間中的客戶端都會收到消息

5. namespace

namespace

命名空間: 相當於 路由的name

​ 用來區分邏輯的,

var socket = io.connect("http://127.0.0.1:5000"+"/ceshi"); # 指定連接 ceshi 命名空間

后台使用:

@socketio.on("ceshi", namespace="/ceshi")

socketio.emit("ceshi", "message", namespace="/cesi")
1.也可以在發送消息中使用指明發送的命名空間, 加上后只會發送到連接到指定命名空間
2.如果連接指明了命名空間, 那么發送的時候不知明,則連接到指定空間的,不會接受消息

6.room 房間

在客戶端方面房間的概念, 用來對用戶分組,以便在某個命名空間下進一步的通信頻道分離. 將客戶端加入/遷出房間的操作在服務器端實現, join_room() 和 leave_room() , 還可以使用 close_room() 來刪除房間, rooms () 函數返回房間客戶端列表.

為了保持簡單,CatChat中並沒有添加房間功能,我們這里僅介紹實現的基本方法。首先,你需要創建一個Room模型存儲房間數據。房間可以使用任意的字符串或數字作為標識,所以可以使用主鍵列作為標
識,另外再創建一個name列用於存儲房間的顯示名稱。同時,我們還要在程序中提供房間的創建、編輯和刪除操作。在房間聊天頁面,我們可以在客戶端的connect事件監聽函數中使用emit()函數觸發服務器端自定義的join事件;同樣,用戶單擊離開按鈕離開房間后在客戶端disconnect事件處理函數中使用emit()函數觸發服務器端定義的leave事件:

socket.on('connect', function() {
   socket.emit('join');
});
socket.on('disconnect', function() {
   socket.emit('leave');
});

JavaScript

Copy

在服務器端,自定義的join和leave事件分別用來將用戶加入和移出
房間,這兩個自定義事件的處理函數如下所示:

from flask_socketio import join_room, leave_room


@socketio.on('join')
def on_join(data):
    username = data['username']
    room = data['room']
    join_room(room)
    # 發送至客戶端 status 接口, 只發送給在room房間的用戶
    emit('status', username + ' has entered the room.', room=room) # 只有指定房間內的用戶會收到消息, 這個room 名可以是字符串等唯一標識符


@socketio.on('leave')
def on_leave(data):
    username = data['username']
    room = data['room']
    leave_room(room)
    emit('status', username + ' has left the room.', room=room)

Python

  1. 經測試發現,我在左邊窗口點擊加入, 左邊窗口會收到房間里發送的消息,右邊不會。

  2. 再次點擊右邊的,哪左邊右邊都會收到消息,因為兩個都在房間里了。

7.sid 用戶連接唯一標識

可以指定sid 發送消息, 但是sid 每次連接都會變,所以可以跟用戶表綁定, 每次連接更新用戶表sid, 在本次連接用到的時候取出用即可。

擴展:
經上述測試后我們是否可以簡單實現房間聊天功能。
1. 用戶去創建自己的聊天室並命名(存到數據庫中, 可以有唯一id + 聊天室名字), 
2. 展示所有聊天室(從數據庫中獲取)
3. 用戶選擇聊天室(向后台join接口發送用戶信息, 同時發送房間唯一標識),
4. 后台將用戶加入聊天室,使用(join_room) 即可
5. 用戶發送消息的時候,出消息內容還要加上聊天室唯一標識, 后台去向指定標識傳就可以
6. 用戶關閉聊天室,采用 close_room(room) 關閉

再次擴展:
	1.聊天室信息存到 redis 中, 在redis中存儲聊天室在線人數,每有人連接進來更新一下。
	2. 單對單聊天擴展
		這種情況,要考慮消息離線問題,需要數據庫/ 消息隊列。
		簡單點采用數據庫:
			單對單原理: 
				相當於創建1對1的房間
			1. 與誰聊天就和誰創建一個房間,數據庫為房間表 + 用戶表, 一 對 多, 采用join_room()
				1>每次打開聊天界面,把后台消息刷新到頁面上。
				2>刪除聊天的時候刪除房間
			2. 同樣來一個 房間表 + 用戶表, 一 對 多, 采用sid 指定 用戶的sid 發送消息,
				

Copy

在這兩個事件處理器中,我們分別調用Flask-SocketIO提供的
join_room()和leave_room()函數,並傳入房間的唯一標識符。
提示
房間也支持命名空間,通過join_room()和leave_room()函數的
namespace參數指定,默認使用當前正在處理的命名空間,可以通過
Flask-SocketIO附加在請求對象上的namespace屬性獲得,即
request.namesapce。
同樣,在發送事件時,也要指定發到哪個房間,這通過使用
send()和emit()函數中的room參數來指定。比如,下面是創建廣播
新消息的room message事件處理函數:

@socketio.on('room message')
def new_room_message(message_body):
	emit('message', {'message': current_user.username + ':' + message_body}, 			room=current_user.room)

如果你僅需要對用戶進行分組,那么房間是你的最佳選擇。命名空
間是在程序層面上的頻道分離。如果我們要在程序中同時實現全局聊
天、匿名聊天室、房間、私聊,這四類功能對消息的處理各自不同,所
以我們需要為這四類功能指定不同的命名空間(全局聊天可以使用默認
的全局命名空間)。在需要分離通信頻道時,我們需要根據程序的特點
來決定方式:僅使用命名空間、僅使用房間或兩者結合使用。
附注
你可以通過Flask-SocketIO作者Miguel Grinberg提供的這個聊天程序
https://github.com/miguelgrinberg/Flask-SocketIO-Chat)示例了解關於
房間的具體實現。
順便說一下,基於房間你也可以實現私信/私聊功能。只需要把
room設為代表某個用戶的唯一值,在發送事件時,就只有目標用戶的客
戶端才能接收到事件。你可以把這種實現方法理解為“一個人的房間”。
這個能代表用戶的唯一值可以是主鍵值、username或是Flask-SocketIO附
加到request對象上代表每個客戶端id的session id(request.sid)。
提示
如果你使用request.sid作為唯一值,那么需要在User模型中添加一個
sid字段存儲這個值,然后在服務器端的connect事件處理函數中更新這
個值。

部分概念和內容來自:《Flask Web開發實戰(李輝)》

https://huyu.info/blog/detail/103

一個簡單的測試項目, 實現大廳聊天和開房間聊天

https://github.com/SweetShance/weChat


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM