使用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被坑太多了,被逼无奈去研究了下框架源码(菜鸡一枚,无法深入研究),且略见成效,愿同诸君共享,少些曲折。
posted @ 2021-07-28 23:27  糖烤栗子&  阅读(2319)  评论(0编辑  收藏  举报