Flask-[实现websocket]-(2): flask-socketio文档学习

news/2024/7/23 15:33:15 标签: flask

一、简单项目的构建

flask_websocket

        |---static

                |---js

                        |---jquery-3.7.0.min.js

                        |---socket.io_4.3.1.js

        |---templates

                |---home

                        |---group_chat.html

                        |---index.html

        |---app.py

1.1、python环境

python3.9.0

1.2、依赖包

Flask==2.1.0
eventlet==0.33.3     #使用这个性能会最优
Flask-SocketIO==5.3.4


1.3、js文件下载

https://code.jquery.com/jquery-3.7.0.min.js

https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.3.1/socket.io.min.js

 

1.4、app.py 配置

from flask import Flask, render_template
from flask_socketio import SocketIO

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app,cors_allowed_origins='*')

if __name__ == '__main__':
    socketio.run(app)

二、官方文档的学习

2.1、namespace:名称空间

概述:名称空间,事件函数在处理请求时,只会处理相同名称空间的websocket请求。跟路由分发很像,把处理请求范围缩小在指定区域中。一般用在不同功能,大方向的不同功能。

功能:Flask-SocketIO 还支持 SocketIO 命名空间,允许客户端在同一个物理套接字上多路复用多个独立连接。

1、如果不设置名称空间,是由默认的名称空间的,就是\

2、在fbv使用时,可以不指定默认的名称空间,在cbv使用时,必须指定名称空间

2.1.1、后端简单的例子:

1、使用默认的全局名称空间

@socketio.on('message')
def handle_message(data):
    #data: 前端发送过来的数据
    print('received message: ' + data)

2、定义了名称空间的

@socketio.on('message',namespace='/default')
def namespace_message(data):
    '''
    :param data:前端发起websocket,传递给服务端的数据 
    :return: 
    '''
    print('接收到前端发送过来的数据:',data)

虽然这两个事件函数,监听的是同一个事件,但是它们的名称空间不一样,所以数据的收发不会混乱。

2.1.1、前端js例子

1、js连接默认的名称空间

<!DOCTYPE html>
<html>
<head>
    <script src="/static/js/socket.io_4.3.1.js"></script>
    <script src="/static/js/jquery-3.7.0.min.js"></script>
    <script type="text/javascript">
    
        var socket = io('http://'+document.domain+':'+location.port);
         // 1、监听服务器端,给message事件的响应
        socket.on('message', function(message) {
            // 处理从服务器接收到的响应数
            alert(message)
        });


        //3、 发送'message'事件给服务器d
        function sendMessage() {
            var message = {"type":"user","id":1};
            //向后端获取id=1的用户的数据
            socket.emit('message',message); //可以直接返回字典等数据

        }
    </script>
</head>
<body>
    <div id="show">

    </div>
    <button onclick="sendMessage()">Send Message</button>

</body>
</html>

2、js连接指定的名称空间

<!DOCTYPE html>
<html>
<head>
    <script src="/static/js/socket.io_4.3.1.js"></script>
    <script src="/static/js/jquery-3.7.0.min.js"></script>
    <script type="text/javascript">
        const namespace = '/default';
        var socket = io('http://'+document.domain+':'+location.port+namespace);
         // 1、监听服务器端,给message事件的响应
        socket.on('message', function(message) {
            // 处理从服务器接收到的响应数
            alert(message)
        });


        //3、 发送'message'事件给服务器d
        function sendMessage() {
            var message = {"type":"user","id":1};
            //向后端获取id=1的用户的数据
            socket.emit('message',message); //可以直接返回字典等数据

        }
    </script>
</head>
<body>
    <div id="show">

    </div>
    <button onclick="sendMessage()">Send Message</button>

</body>
</html>

2.2、事件

概述:在名称空间下,就开始匹配事件名,最终是基于事件名进行消息的收发的。事件名可以找到指定的函数来处理这个事件请求的。还是跟路由分发类似,先找到一个大范围,再从这个范围中找指定的某个路由,将请求交给路由对应的函数处理。

2.2.1、自带的4个事件

1、connect   事件

        用来控制是否允许客户端建立连接的,一般会在这里验证用户的token,token验证通过才允许连接;或群聊中显示谁进入群聊了。

发起连接时,怎么携带上数据:

<script type="application/javascript">
    //1、发起连接

    const namespace = '/test'
    const socket = io('http://'+document.domain+':'+location.port+namespace,
        {query:{'token':'123456','group':group,'name':name}}
    );
</script>
@socketio.on('connect',namespace='/chat')
def chat_connect():
    '''
    控制群里用户进入群连接
    :return:
    '''
    token = request.args.get('token')
    name = request.args.get('name')
    group = request.args.get('group')
    print('发起连接时携带的数据:',request.args)
    if token:
        join_room(group)
        emit('group_recv',{'name':name,'msg':f'进入’{group}‘群聊','type':'connect'},room=group)
    else:
        return False

2、disconnect   事件

        一般是用于统计活着的连接数,或者群聊中显示谁退出群聊。

3、message 事件

        可以传递字典、字符串等数据结构。

4、json 事件

        可以传递字典、字符串等数据结构。在使用过程中。

json和message事件,好像在使用上没有区别,看文档也没有看出啥不同之处。

2.2.2、事件函数的写法

1、显示指定事件名

使用默认的名称空间时:

@socketio.on('my_event')
def handle_my_custom_event(json):
    print('received json: ' + str(json))

指定名称空间时:

@socketio.on('my_event',namespace='/test')
def handle_my_custom_event(json):
    print('received json: ' + str(json))

2、使用函数名作为事件名

使用默认的名称空间时:

@socketio.event
def my_custom_event(data):
    print('received data: ',data)

指定名称空间时:

@socketio.event(namespace='/test')
def my_custom_event(data):
    print('received data: ',data)

2.2.3、自定义事件名称

自定义的事件名称,message、json、connect、disconnect是保留名称,不能用于已命名事件。

2.3、消息发送

from flask_socketio import send,emit

1、send  : 不能指定事件名,默认指定message事件,不能改。

传递的参数:第一个是发送的数据,namespace=‘/名称空间’, broadcast=True 采用广播

2、emit:需要指定事件名,可以指定自定义的事件。

传递的参数:事件名称、传递发送的数据,namespace=‘/名称空间’, broadcast=True 采用广播

在普通的视图函数中使用:(在事件函数中使用方法一样)

@app.route('/test/push')
def test_push():
    emit('json',{'code':200,'msg':'使用emit来主动推送广播'},namespace='/test',broadcast=True)
    send({'code':200,'msg':'使用send来主动推送广播'},namespace='/test',broadcast=True)
    return jsonify({"code":200,'msg':'ok'})

注意:send和emit必须在 socketio.on 装饰的事件函数中使用,需要使用到request.sid ,在普通的视图函数中无法这两个方法。

3、socketio.send() 和socketio.emit()  

参数:不能传递broadcast=True,其他与send和emit没有区别。

功能:发送的消息是广播消息。

socketio.send() 是给message事件推送消息,而且推送的是广播消息。

socketio.emit() 需要传递事件名称,推送是也是广播消息。

注意:这个两个方法一般在视图函数中使用,给系统中同一个名称空间下的事件名广播消息。

2.4、广播

广播:广播的范围是同一个名称空间下的同一个事件进行消息广播。

from flask_socketio import send,emit

使用这两个时,要实现广播功能需要添加参数,broadcast=True。

from flask_socketio import SocketIO

socketio=SocketIO(app)

socketio.send('data'), 给message事件广播消息。

socketio.emit('xxx','data') , 给xxx事件广播消息。

2.5、房间号

概述:要实现群聊功能,需要房间的功能,将连接添加到某个房间中,这些连接就在一个房间中。一个连接向房间中发送消息,房间中的所有连接都会接收到该消息。房间很像广播,只是房间的把消息的范围限定在房间中(名称空间、事件名称、房间号都一样),而广播,只要名称空间和事件名一样就会接收到,消息范围会更大。

from flask_socketio import join_room,leave_room

join_room : 加入房间

leave_room: 退出房间

官方文档的例子:

1、建立连接后,基于join事件,把连接添加到某个房间中

2、建立连接后,基于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)
    send(username + ' has entered the room.', to=room)

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

在使用时,加上名称空间会更容易区分功能。

from flask_socketio import join_room, leave_room

@socketio.on('join',namespace='/chat')
def on_join(data):
    username = data['username']
    room = data['room']
    join_room(room)
    send(username + ' has entered the room.', to=room)

@socketio.on('leave',namespace='/chat')
def on_leave(data):
    username = data['username']
    room = data['room']
    leave_room(room)
    send(username + ' has left the room.', to=room)


@socketio.on('group',namespace='/chat')
def handle_group(data):
    '''
    :param data: 用户在群聊中发送消息
    :return:
    '''
    print('chat群里发消息:',data)
    group = data.get('group')
    msg = data.get('msg')
    name = data.get('name')
    ret_data = {'msg':msg,'name':name,'type':'data'}
    emit('group_recv',ret_data,room=group)

2.6、CBV的写法

概述:使用CBV的写法 ,就必须传递上namespace,即使是默认的全局名称空间,也得传递'/' .

2.6.1、官方例子

from flask_socketio import Namespace, emit

class MyCustomNamespace(Namespace):
    def on_connect(self):
        #方法名去掉on_,剩下的就是事件名称,connect事件
        pass

    def on_disconnect(self):
        #方法名去掉on_,剩下的就是事件名称,disconnect事件
        pass

    def on_my_event(self, data):
        #方法名去掉on_,剩下的就是事件名称,my_event事件
        emit('my_response', data)

#注册该消费类,类似注册路由一样
socketio.on_namespace(MyCustomNamespace('/test'))

2.6.2、官方例子与js结合

html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>群聊</title>
    <script type="application/javascript" src="/static/js/jquery-3.7.0.min.js"></script>
    <script type="application/javascript" src="/static/js/socket.io_4.3.1.js"></script>
    <script type="application/javascript">
        //传递给模板的数据,转成python数据结构
        const data = {{ data|tojson }};
        const group = 'group1'; //群名
        const name = 'lhz';//当前用户名
        //1、发起连接
        const socket = io('http://'+document.domain+':'+location.port+'/cbv',
            {query:{'token':'123456','group':group,'name':name}}
        );


        //2、监听my_event事件,服务端发送数据给my_event事件
        socket.on('my_event',function (data) {
            console.log('my_event事件监听到')
            console.log(data)

        });

        //3、前端主动发my_event事件到服务端
        function sendDataFunc() {
           const $sendData = $('#sendDataId');
           socket.emit('my_event',{'group':group,'msg':$sendData.val(),'name':name});
        }

    </script>
</head>
<body>

{#1、展示群聊的消息#}
<div id="showDataId">

</div>

{#2、发送消息的输入框#}
<div>
    <input id="sendDataId" type="text">
    <p><input type="button" value="发送给my_event事件" onclick="sendDataFunc()"></p>
</div>

</body>
</html>

cbv例子:

@app.route('/cbv')
def cbv_html():
    return render_template('test/test_cbv.html',data={'group':'123','name':'lhz'})

class MyCustomNamespace(Namespace):
    def on_connect(self):
        # 方法名去掉on_,剩下的就是事件名称,connect事件
        print(request.args,'发起连接时携带的数据')

    def on_disconnect(self):
        # 方法名去掉on_,剩下的就是事件名称,disconnect事件
        print('退出连接')

    def on_my_event(self, data):
        # 方法名去掉on_,剩下的就是事件名称,my_event事件
        print('前端给my_event事件发送的消息',data)
        emit('my_event', {'code':200,'msg':'后端接收到并返回了','data':data.get('msg')})


# 注册该消费类,类似注册路由一样
socketio.on_namespace(MyCustomNamespace('/cbv'))


http://www.niftyadmin.cn/n/5038564.html

相关文章

MySQL 深分页优化

在实际应用场景中&#xff0c;列表分页查询是很常见的。假设现在存在某张表&#xff0c;已知 ID 是主键&#xff0c;针对 user_name 建立了二级索引。 针对该表进行分页查询。 select * from table order by id limit offset, size;那么同样都是获取 10 条数据&#xff0c;查…

17.适配器模式(Adapter)

意图&#xff1a;将一个类的接口转换为Client希望的另一个接口&#xff0c;使得原本由于接口不兼容而不能一起工作的那些类在一起工作。 UML图 Target&#xff1a;定义Client使用的与特定领域相关的接口。 Client&#xff1a;与符合Target接口的对象协同工作。 Adaptee&#xf…

达梦数据库随系统开机自动启动脚本

写一个脚本&#xff0c;实现在服务器开机后自动启动达梦数据库的功能。 1. 在/etc/init.d/目录下&#xff0c;编写脚本&#xff0c;并将脚本命名为startdm.sh。脚本内容实现如下&#xff1a; #!/bin/bash #chkconfig:2345 80 90 #decription:启动达梦# 切换到 dmdba 用户 su …

计算机视觉与深度学习-图像分割-视觉识别任务02-目标检测-【北邮鲁鹏】

目录标题 参考目标检测定义深度学习对目标检测的作用单目标检测多任务框架多任务损失预训练模型姿态估计 多目标检测问题滑动窗口&#xff08;Sliding Window&#xff09;滑动窗口缺点 AdaBoost&#xff08;Adaptive Boosting&#xff09;参考 区域建议 selective search 思想慢…

Linux 的性能调优的思路

Linux操作系统是一个开源产品&#xff0c;也是一个开源软件的实践和应用平台&#xff0c;在这个平台下有无数的开源软件支撑&#xff0c;我们常见的apache、tomcat、mysql等。 开源软件的最大理念是自由、开放&#xff0c;那么Linux作为一个开源平台&#xff0c;最终要实现的是…

Kotlin协程CoroutineScope异步async取消cancel等待await的任务

Kotlin协程CoroutineScope异步async取消cancel等待await的任务 import kotlinx.coroutines.*fun main(args: Array<String>) {runBlocking {val mScope CoroutineScope(Dispatchers.IO).async {println("->")delay(999999)println("<-")"…

基于Java所涉及的人工智能的框架

11 References: [1] Java中人工智能的框架_永远的12的博客-CSDN博客

SQL语句学习系列(1)

目录 查询语句1. 查询所有列的所有行&#xff1a;2. 查询指定列的所有行&#xff1a;3. 查询满足条件的行&#xff1a;4. 查询满足多个条件的行&#xff1a;6. 查询满足条件的行数&#xff1a;7. 查询满足条件的唯一值&#xff1a;8. 查询满足条件的分组统计&#xff1a;9. 查询…