使用Flask-socketio完成Flask Websocket


flask-socketio

為了方便所以將代碼以及注釋放上面, 在代碼之后有對這個框架的解釋(由於本人比較菜,只能解釋一點皮毛 )^__^

flask后端文件 flask_ws.py :

from flask import Flask, render_template, request
from flask_socketio import SocketIO, Namespace, emit

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO()
# 解決跨域問題
socketio.init_app(app, cors_allowed_origins='*')


# 用來存放客戶端的 sid,即 session id
# 可以不單獨定義字典存放 sid與namespace,flask-socketio 默認將 sid 存放在 room 中
socket_pool = {}

# Websocket 通過namespace 和 sid 標識具體客戶端
# 第一個 Websocket 類
class MyCustomNamespace(Namespace):
    name_space = '/wechat'

    # 連接成功調用的方法
    def on_connect(self):
        global socket_pool

        print("connect..")
        print('-----------------')
        print(self.server.manager)
        print(self.socketio)
        print('-----------------')
        print(request.namespace)
        socket_pool[request.sid] = MyCustomNamespace.name_space
        print(socket_pool)

    # 斷開連接調用的方法
    def on_disconnect(self):
        global socket_pool
        print("disconnect...")
        del socket_pool[request.sid]
        print(socket_pool)

    # 往 接收客戶端標消息識為 'message' 的方法
    def on_message(self, data):
        print(data)
        # 把消息發送到客戶端的 'response' 標識的方法中, 一般是 on_response()
        emit('response', '123', to=request.sid)
        print('--------------')
        print(request.sid)
        print('--------------')
        print(socketio.server.manager.rooms)

    # 往 接收客戶端標消息識為 'hello' 的方法
    def on_hello(self, data):
        print('hello world')


    # 發送消息
    def send(self, data):
        # 向 namespace中的所有 Websocket 連接廣播消息, namespace參數不能少, to缺省是廣播模式
        emit('response', data, namespace=name_space)
        # 向 sid 所標識的客戶端 單播
        emit('response', data, namespace=name_space, to=request.sid)

# 為了詳細展示不同類,這里就不做繼承
# 第二個 Websocket 類
class MyCustomNamespace1(Namespace):
    name_space = '/wechat1'
    def on_connect(self):
        global socket_pool

        print("connect..")
        print(request.namespace)
        socket_pool[request.sid] = MyCustomNamespace1.name_space
        print(socket_pool)

    def on_disconnect(self):
        global socket_pool
        print("disconnect...")
        del socket_pool[request.sid]
        print(socket_pool)

    def on_message(self, data):
        print(data)
        emit('response', '123')
        self.send('asd')

    # 發送消息
    def send(self, data):
        emit('response', data, namespace=MyCustomNamespace1.name_space)


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


@app.route('/sendll/')
def sendll():
    for sid,namespace_value in socket_pool.items():
        print(sid)
        emit('response', '123456', namespace=namespace_value, to=sid)
    print('ok')
    return 'ok'

@app.route('/<file>')
def index1(file):
    return render_template(file)

# #等同於類中的 on_message 方法
# @socketio.on('message', namespace="/wechat")
# def handle_message(message):
#     print('received message: ' + message['data'])
#     socketio.emit("response", {'age': 18}, namespace="/wechat")
#
# #等同於類中的 on_connect 方法
# @socketio.on('connect', namespace="/wechat")
# def connect():
#     print("connect..")
#
# #等同於類中的 on_disconnect 方法
# @socketio.on('disconnect', namespace="/wechat")
# def connect():
#     print("disconnect...")

if __name__ == '__main__':
    # 注冊WebSocket 以及 命名空間 MyCustomNamespace.name_space ('/wechat')
    socketio.on_namespace(MyCustomNamespace( MyCustomNamespace.name_space ))
    # 注冊WebSocket 以及 命名空間 MyCustomNamespace1.name_space ('/wechat1')
    # 有多個類或者命名空間可以繼續注冊
    socketio.on_namespace(MyCustomNamespace1( MyCustomNamespace1.name_space ))
    # 啟動app, 運行在 8080 端口
    socketio.run(app, host='127.0.0.1', port=8080)

前端測試文件1 index.html 連接的命名空間是 /wechat

<html>
    <head>
        <script type="text/javascript"
        src="https://code.jquery.com/jquery-3.4.0.min.js"></script>
        <script type="text/javascript"
        src="./socket.io.js"></script>

        <script type="text/javascript" charset="utf-8">
          var ws = io.connect('ws://127.0.0.1:8080/wechat');

          ws.emit("message", { "data": "zhangsan" })
		  
          ws.on('connect', function(data) {
              ws.emit('message', { 'data': 'I\'m connected!' });
          });

          ws.on('disconnect', function(data){
              ws.emit('message', { 'data': 'I\'m disconnected!' });
          });

          ws.on('response', function(data) {
              console.log(data);
          });
          ws.on
        </script>
    </head>

    <body>
        <h1>為了艾澤拉斯</h1>
    </body>
</html>

前端測試文件2 index2.html

<html>
    <head>
        <script type="text/javascript"
        src="https://code.jquery.com/jquery-3.4.0.min.js"></script>
        <script type="text/javascript"
        src="./socket.io.js"></script>

        <script type="text/javascript" charset="utf-8">
          var ws = io.connect('ws://127.0.0.1:8080/wechat1');

          ws.emit("message", { "data": "zhangsan" })

          ws.on('connect', function(data) {
              ws.emit('message', { 'data': 'I\'m connected!' });
          });

          ws.on('disconnect', function(data){
              ws.emit('message', { 'data': 'I\'m disconnected!' });
          });

          ws.on('response', function(data) {
              console.log(data);
          });
          ws.on
        </script>
    </head>

    <body>
        <h1>為了艾澤拉斯</h1>
    </body>
</html>

先放上連接成功的圖片(后端)
image
先放上連接成功的圖片(前端)
image

說明,我所使用的擴展包版本如下:

后端

- Flask:		V1.1.2
- eventlet:		V0.31.1
- Python:		V3.6.5
- Flask-SocketIO:	V5.1.0

前端:

jquery-3.4.0
Socket.IO v4.1.2
ps: 前端的Socket.IO文件好像 1.3及以下版本連接Web后台時會一直報 400 錯誤,這是個大坑

好了,接下來講講這個flask-socketio組件。

這個組件將WebSocket封裝的已經很完善了,尤其是斷線重連機制,可以說是省去了很多麻煩,具體的優點大家可以去看大佬們的博客~我就不在這里賣弄了,嘻嘻嘻。
我就來講講這個組件與是如何存放用戶的socket標識的
話不多說先上原理圖(個人理解)
image

  • 1.在框架啟動時先初始化了 SocketIO()
  • 2.在SocketIO初始化的時候同時初始化了Server()並存放在自己的server屬性中。
  • 3.在Server初始化的時候又初始化了BaseManager(),並將初始化的BaseManager 存放在 Server實例的 manager屬性中。而存放與用戶 連接的 socket標識就存放在 BaseManager的rooms中
描述的具體流程如上圖所示
以下是我輸出的 rooms 中的內容

此時我使用兩個客戶端連接了 '/wechat', sid:lgrl3XcO-CXxvF9uAAAE,cmzqIqmXEiXHibHPAAAF
一個客戶端連接了 '/wechat1', sid:x8wAcozzFtKAaxeCAAAB

{
    '/wechat1': {
        None: bidict({'x8wAcozzFtKAaxeCAAAB': 'kdA3cUoQTHArrgdaAAAA'}),
        'x8wAcozzFtKAaxeCAAAB': bidict({'x8wAcozzFtKAaxeCAAAB': 'kdA3cUoQTHArrgdaAAAA'})
    },
    '/wechat': {
        None: bidict({'lgrl3XcO-CXxvF9uAAAE': 'bVaIf8vaTfou1AbZAAAC', 'cmzqIqmXEiXHibHPAAAF': 'la_OeJPnarS66rTiAAAD'}),
        'lgrl3XcO-CXxvF9uAAAE': bidict({'lgrl3XcO-CXxvF9uAAAE': 'bVaIf8vaTfou1AbZAAAC'}),
        'cmzqIqmXEiXHibHPAAAF': bidict({'cmzqIqmXEiXHibHPAAAF': 'la_OeJPnarS66rTiAAAD'})
    }
}

當然,在一開我貼出來的代碼中並沒有使用這個 rooms,為了方便我使用了字典存放sid 與 對應的 namespace。

入站第一篇,為了諸君在找資料時少踩點坑,哈哈哈
為什么要寫這篇博客,是因為我初學WebSocket被坑太多了,被逼無奈去研究了下框架源碼(菜雞一枚,無法深入研究),且略見成效,願同諸君共享,少些曲折。


免責聲明!

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



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