Flask WebSocket学习笔记

news2024/9/23 1:20:04

WebSocket

简介:WebSocket是一种全新的协议,随着HTML5的不断完善,越来越多的现代浏览器开始全面支持WebSocket技术了,它将TCP的Socket(套接字)应用在了webpage上,从而使通信双方建立起一个保持在活动连接通道。

运行流程:浏览器通过Javascript向服务器发起WebSocket连接的请求,在WebSocket连接建立成功后,客户端和服务器就可以通过TCP连接传输数据。因为WebSocket连接本质上是TCP连接,不需要每次传输都带上重复的头部数据,所以它的数据传输量比轮询和Comet(长连接)技术小很多。

原理:WebSocket协议是借用HTTP协议的1.01 switch protocol(服务器根据客户端的指定,将协议转换成为Upgrade首部所列的协议)来达到协议转换的,从HTTP协议切换成WebSocket通信协议。

具体连接方式:通过在请求头中添加upgrade:websocket及通信秘钥(Sec-WebSocket-Key),使双方握手成功,建立全双工通信。

WebSocket客户端连接报文

WebSocket服务端响应报文

1. Flask-SocketIO

要对应安装不然会出现版本问题

pipinstallgevent
pipinstalleventlet
pipinstallFlask-SocketIO==4.3.1
pipinstallpython-engineio==3.13.2
pipinstallpython-socketio==4.6.0

1.1 初始化

fromflaskimportFlask, render_template
fromflask_socketioimportSocketIO
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
​
if__name__ == '__main__':
    socketio.run(app)

1.2 客户端向服务器发送数据

客户端和服务端使用SocketIO时,消息都被当作事件进行接收。在客户端,Javascript 通过回调函数处理事件;在Flask-SocketIO服务端,每个事件都有对应的事件函数,类似原生Flask中,路由都有对应的视图函数。

前端代码:

<!DOCTYPE html>
<htmllang="en">
<head>
    <metacharset="UTF-8">
    <title>Test</title>
    <scripttype="text/javascript"src="{{ url_for('static', filename = 'js/jquery.min.js') }}"></script>
    <scripttype="text/javascript"src="{{ url_for('static', filename = 'js/socket.io.min.js') }}"></script>
</head>
<body>
    <h1>Hello World!</h1>
    <pid="t"></p>
    <script>
        varname_space='/dcenter'// 这里的命名空间必须与后端代码的命名空间一样,要不然无法建立连接
        varsocket=io.connect(location.protocol+'//'+document.domain+':'+location.port+name_space)  //初始化建立连接
        {# 向服务器发送信息 #}
        socket.on('connect',function () {
            // 这里的事件命名必须与后端绑定的事件命名一样,不然接收不到消息
            socket.emit('my event', {data: 'I\'m connected!'})
        })
    </script>
</body>
</html>

后端代码:

fromflaskimportFlask, render_template
fromflask_socketioimportSocketIO, emit
fromthreadingimportThread
importtime
​
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
#socketio = SocketIO()
socketio = SocketIO(logger=True, engineio_logger=True)  # 将日志输出到终端
socketio.init_app(app, cors_allowed_origins='*')    # 解决跨域问题
name_space = '/dcenter' # 命名空间必须与前端一致
​
# 初始化连接函数
@socketio.on('connect', namespace=name_space)
defconnected_msg():
    print('client connected.')
​
# 初始化断开函数
@socketio.on('disconnect', namespace=name_space)
defdisconnect_msg():
    print('client disconnected.')
​
# 路由路径
@app.route('/')
defindex():
    returnrender_template('index.html')
​
# 接收客户端消息(事件命名必须与前端一致)
@socketio.on('my event') # 匿名事件
defhandle_my_custom_event(json):
    print('received json: '+str(json))
    
# 接收客户端消息(事件命名必须与前端一致)
@socketio.on('my event', namespace=name_space) # 绑定命名空间
defhandle_my_custom_event(json):
    print('received json: '+str(json))
​
​
if__name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)​

服务器响应数据:

1.3 服务器实时接收数据

前端代码:

<!DOCTYPE html>
<htmllang="en">
<head>
    <metacharset="UTF-8">
    <title>Test</title>
    <scripttype="text/javascript"src="{{ url_for('static', filename = 'js/jquery.min.js') }}"></script>
    <scripttype="text/javascript"src="{{ url_for('static', filename = 'js/socket.io.min.js') }}"></script>
{#    <scripttype="text/javascript"src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>#}
</head>
<body>
    <h1>Hello World!</h1>
    <pid="t"></p>
    <ahref="javascript:a()">发送</a>
    <script>
        // 向服务器发送信息,如果要服务器实时接收数据的话,需建立连接与发送数据要同步进行(例如封装在一个函数里)
        functiona(){
            varsocket=io.connect('http://'+document.domain+':'+location.port);
            socket.on('connect', function() {
                socket.emit('message', 'I\'m connected!');
                {#socket.emit('message', {data: 'I\'m connected!'});#}
            });
            console.log('kkk')
        }
    </script>
</body>
</html>

后端代码:

fromflaskimportFlask, render_template
fromflask_socketioimportSocketIO, emit
fromthreadingimportThread
importmultiprocessing
importtime
​
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
# socketio = SocketIO()
socketio = SocketIO(logger=True, engineio_logger=True)  # 将日志输出到终端
socketio.init_app(app, cors_allowed_origins='*')    # 解决跨域问题
name_space = '/dcenter'
​
# 初始化断开函数
@socketio.on('connect', namespace=name_space)
defconnected_msg():
    print('client connected.')
​
# 初始化断开连接函数
@socketio.on('disconnect', namespace=name_space)
defdisconnect_msg():
    print('client disconnected.')
​
@app.route('/')
defindex():
    returnrender_template('index.html')
​
# 匿名函数
@socketio.on('message')
defhandle_message(message):
    print('received message: '+message)
​
​
if__name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)

先访问,点击发送:

服务器实时接收结果:

1.4 服务器向客户端发送数据

在启用广播选项的情况下发送消息时,连接到命名空间的所有客户端都会收到它,包括发送者。当不使用命名空间时,连接到全局命名空间的客户端会收到消息。请注意,不会为广播消息调用回调。

设置参数broadcast=True就会启动广播功能。

前端代码:

<!DOCTYPE html>
<htmllang="en">
<head>
    <metacharset="UTF-8">
    <title>Test</title>
    <scripttype="text/javascript"src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
    <scripttype="text/javascript"src="//cdn.bootcss.com/socket.io/1.5.1/socket.io.min.js"></script>
</head>
<body>
    <h1>Hello World!</h1>
    <pid="t"></p>
    <script>
        varname_space='/dcenter'// 这里的命名空间必须与后端代码的命名空间一样,要不然无法建立连接
        varsocket=io.connect(location.protocol+'//'+document.domain+':'+location.port+name_space)
         // 这里的事件命名必须与后端绑定的事件命名一样,不然接收不到消息
        socket.on('dcenter', function (res) {
            console.log(res)
            lett=res.data
            if (t) {
                $("#t").append(t).append('<br/>');
            }
        })
    </script>
</body>
</html>

后端代码:

fromflaskimportFlask, render_template
fromflask_socketioimportSocketIO, emit
fromthreadingimportThread
importtime
​
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
#socketio = SocketIO()
socketio = SocketIO(logger=True, engineio_logger=True)  # 将日志输出到终端
socketio.init_app(app, cors_allowed_origins='*')        # 解决跨域问题
name_space = '/dcenter'     # 命名空间
​
# 初始化连接函数
@socketio.on('connect', namespace=name_space)
defconnected_msg():
    print('client connected.')
    
# 回调函数
defcb():
    print('vvv==bbb')
    
# 初始化断开连接函数
@socketio.on('disconnect', namespace=name_space)
defdisconnect_msg():
    print('client disconnected.')
​
# 测试函数向客户端发送数据
deftest():
    event_name = 'dcenter'
    foriinrange(11):
        broadcasted_data = {'data': f"test message!,{i}"}
        # 消息广播
        socketio.emit(event_name, broadcasted_data, broadcast=False, namespace=name_space, callback=cb())
        time.sleep(1)
​
@app.route('/')
defindex():
    # 开启多线程进行消息发送
    td = Thread(target=test)
    td.start()
    returnrender_template('index.html')
​
​
if__name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)

访问http://127.0.0.1:5000/后,服务器会在11秒内依次向客户端发送数据

1.5 总结

1、命名空间事件命名必须前后端保持一致。

2、服务器的信息发送广播只支持线程广播但不支持进程广播。

2. 房间

实际应用场景中,可能需要给用户分组。比如,聊天室,不同用户只能收到他们所在房间的消息。通过join_room() 和 leave_room() 可以实现上述功能:

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.', room=room)
​
@socketio.on('leave')
def on_leave(data):
    username = data['username']
    room = data['room']
    leave_room(room)
    send(username + ' has left the room.', room=room)

send() 和emit() 函数接受room 参数。

所有客户端连接时,会被分配一个房间。默认房间名称为连接的session ID,Flask中通过request.sid获取该ID。客户端能加入所有存在的房间。客户端断开时,所有它加入的房间都会移除它。上下文外的socketio.send() 和 socketio.emit()也可以接收room参数,来给房间中所有客户端广播。

因为所有客户端在加入时,都被指定了一个私人的房间,所以,如果想要发送消息给指定客户端,也可以通过指定消息的room参数为该客户端session ID来实现。

参考文献:https://zhuanlan.zhihu.com/p/376370796

https://blog.csdn.net/weixin_46020624/article/details/123619230?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167513282616800225584650%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167513282616800225584650&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-123619230-null-null.142^v71^wechat,201^v4^add_ask&utm_term=Flask-SocketIO&spm=1018.2226.3001.4187

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/188300.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java面向对象基础

文章目录面向对象一、类和对象1. 类的介绍2. 类和对象的关系3. 类的组成4. 创建对象和使用对象的格式二、对象内存图1. 单个对象内存图2. 两个对象内存图3. 两个引用指向相同内存图三、成员变量和局部变量四、this 关键字1. this 可以解决的问题2. this 介绍3. this 内存图五、…

学术的快乐来源

文章目录我的核心快乐来源&#xff1a;其他非核心快乐源泉知乎搜到的别人的快乐来源作此文&#xff0c;以便懈怠时候看看&#xff0c;能够聊表安慰。 ——题记 抱着给自己枯燥无聊学术生涯找点乐子的想法&#xff0c; 我决定仔细思考一下自己做学术的时候有哪些快乐的地方&…

C++关于开源包7zip压缩工具的编译及使用

1、7zip的配置 7-Zip是一款免费开源的压缩与解压软件&#xff0c;基本能够满足绝大多数常见的压缩和解压文件需求&#xff0c;此外还支持了分卷压缩和解压&#xff0c;非常好用。但是调用7-zip库需要用到一个叫bit7z的库&#xff0c;bit7z是一个C静态库&#xff0c;其封装了简单…

MySQL 中主从之间是怎样保证数据一致的呢?

在我们日常的工作中&#xff0c;处理 MySQL 数据库相关问题时&#xff0c;我相信绝大多数 DBA 处理最棘手的问题就是数据库主从数据不一致的问题。 处理过关于 MySQL 数据库主从数据不一致的朋友一定印象非常深刻&#xff0c;因为稍有不慎就会将造成原有数据的丢失&#xff0c…

精益安灯电子看板实现了实时监测

众所周知&#xff0c;智能工厂的规划建设是一个十分复杂的系统工程。所以安灯电子看板是精益生产中一一个重要组成要素&#xff0c;可以提升工厂生产车间的过程管理&#xff0c;生产数据做的信息化、目视化&#xff1b;信息快捷化、生产工序透明化等&#xff0c;是提高生产率的…

自动控制原理笔记-根轨迹的概念-根轨迹方程

目录 根轨迹的基本概念&#xff1a; 根轨迹的概念&#xff1a;当开环系统某一参数从 0 到∞变化时&#xff0c;闭环极点在S 平面上变化所描绘出的轨迹。 闭环零极点与开环零极点之间的关系&#xff1a; 根轨迹方程&#xff1a; 开环增益于根轨迹间的关系&#xff1a; 闭环系…

excel 格式化日期为字符串

最近经常遇到excel打开文件的时候&#xff0c;excel自动将yyyy-MM-dd HH:mm:ss &#xff08;如&#xff1a;2022-01-21 12:12:12 &#xff09;之类的时间的自动转为这样的格式列&#xff0c;2022/1/21 12:12:12 &#xff0c;导致有想从excel/csv格式 中复制原始日期格式比较麻烦…

【软件测试】一个真正的测试面试过程,我比面试官还狡猾......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 先卖个关子&#xf…

关羽这灵敏度你们爱了吗?#走位 #游戏外设

关羽这灵敏度你们爱了吗&#xff1f;#走位 #游戏外设 关羽这灵敏度你们爱了吗&#xff1f;#走位 #游戏外设

var、let、const之间的区别

说一下var、let、const之间的区别一、var二、let三、const四、var、let 、const的区别&#xff1f;一、var 用var声明的变量既是全局变量&#xff0c;也是顶层变量 注意&#xff1a;顶层对象&#xff0c;在浏览器环境指的是window对象&#xff0c;在Node指的是global对象。 var…

老杨说运维 | AIOps如何助力实现全面可观测性(下)

上期我们讲到可观测性是什么&#xff0c;以及它能给企业带来的价值&#xff0c; 戳→「老杨说运维 | AIOps如何助力实现全面可观测性&#xff08;上&#xff09;」一键回看上期精彩内容。 说完了什么是可观测性&#xff0c;这期我们来看看可观测性是如何落地实践的。 一、可观…

若依配置教程(五)数据权限的使用及配置

文章目录一、Controller模块二、Mapper.xml三、在Impl服务层加入权限注解四、分配数据权限若依文档 学会了前几篇文章如何新建模块和生成代码&#xff0c;这篇接下来介绍数据权限的配置和使用&#xff1a; 首先&#xff0c;在建立数据库表的时候&#xff0c;必须要添加user_id…

深入理解spring三级缓存解决循环依赖的设计思路

说到这个话题的时候&#xff0c;很多人再熟悉不过了&#xff0c;因为听到太多了&#xff0c;而且百度一大堆&#xff0c;但是理解到什么程度了呢&#xff0c;或者说只是知道这回事&#xff0c;但是为什么这样设计&#xff0c;代码中有什么可以借鉴的&#xff0c;在实际业务中有…

Jmeter和JDK下载安装及环境变量配置详细教程

写在最前面的话&#xff0c;jmeter安装部署依赖java环境&#xff0c;所以得安装JDK java环境检查 命令行输入&#xff1a;java -version 如果出现以下内容&#xff0c;说明java环境已安装&#xff0c;无需理会&#xff0c;如果没有&#xff0c;需要安装JDK 一、下载并安装JDK…

PSP模拟器截图CG的高清化-Waifu2x

由PSP游戏本体提取CG图片直接超分自然是比较舒服的&#xff0c;但实际上因了加密等诸多问题&#xff0c;甚或不如直接模拟器截了图进行处理来得方便 1. 模拟器设置 如果要截图的话&#xff0c;自然是以得到更好的效果为宜&#xff0c;于是可以对模拟器进行一些基本的设置。 对…

Java两大工具库:Commons和Guava(3)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客。值此新春佳节&#xff0c;我给您拜年啦&#xff5e;祝您在新的一年中所求皆所愿&#xff0c;所行皆坦途&#xff0c;展宏“兔”&#xff0c;有钱“兔”&#xff0c;多喜乐&#xff0c;常安宁&#xff01;虽然Apache Com…

【33】C语言 | 联合体详解

目录 1、联合类型的定义 2、联合的特点 3、联合大小的计算 1、联合类型的定义 联合也是一种特殊的自定义类型这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间《所以联合也叫共用体)先看下面代码输出什么&#xff1f; union Un {char c;int …

我用css3为好友胡歌的宝宝做了一个动画照片墙

软件人生风雨十年&#xff0c;仙剑一过去也有十年有余了&#xff0c;和胡歌认识那么久&#xff0c;今日喜闻好友胡歌生宝宝的消息&#xff0c;惊喜之余用css3为胡歌的宝宝做了动画照片墙的模板效果。 目录 1. 实现思路 2. 墙体的实现 3. 选取模板素材&#xff0c;进行图片元…

虹科案例 | 石油天然气行业CFD高性能计算解决方案

公司简介 DNV GL 是全球领先的能源、石油和海事行业风险管理及资产绩效提升的软件供应商&#xff0c;主要为客户提供全面的风险管理和各类评估认证服务&#xff0c;认证涉及信息通信技术、汽车及航空天、食品与饮料、医疗等方面。 DNV GL以让世界更安全、更智能、更环保为使命…

随笔记——MQ

文章目录1、 概要2、 为什么使用MQ/使用MQ的好处&#xff1f;3、 使用MQ的缺点&#xff1f;4、 使用MQ会产生的问题及解决办法&#xff1f;4.1、如何保证消息的顺序性&#xff1f;4.2、如何解决消息被重复消费&#xff1f;4.3、如何解决消息丢失&#xff1f;4.4、如何解决消息积…