websocket连接是客户端与服务器之间永久的双向通信通道,直到某方断开连接。

  双向通道意味着在连接时,服务端随时可以发送消息给客户端,反之亦然,这在一些需要即时通讯的场景比如多人聊天室非常重要。

  flask_socketio实现了对websocket的封装,它可以让运行flask应用的服务端和客户端建立全双工通道。

  flask_socketio是一个python库,是flask框架的扩展。

一、安装

  1. pip install flask-socketio

二、实现对flask的封装

  1. from flask import Flask, render_template
  2. from flask_socketio import SocketIO,emit
  3. app = Flask(__name__)
  4. app.config['SECRET_KEY'] = 'secret!'
  5. socketio = SocketIO(app)
  6.  
  7. if __name__ == '__main__':
  8. socketio.run(app, debug=True)

  socketio.run()函数封装了flask的web服务器的启动

三、服务端向客户端推送

  socketio的两个函数send()和emit()都可以实现消息发送,前者用于无名事件,后者用于命名的事件。

  事件是消息的名称。如果把消息比做信件,事件就是贴在信封上的标识,这个标识规定了信件送往客户端或服务端的某个函数。

  1. from flask import Flask, render_template
  2. from flask_socketio import SocketIO,emit
  3. app = Flask(__name__)
  4. app.config['SECRET_KEY'] = 'secret!'
  5. socketio = SocketIO(app)
  6.  
  7. @socketio.on('connect', namespace='/test_conn')
  8. def test_connect():
  9. socketio.emit('server_response',
  10. {'data': connected’},namespace='/test_conn')
  11.  
  12. if __name__ == '__main__':
  13. socketio.run(app, debug=True)

  比如上面socketio.on('connect',namespace='/test_conn')中的connect就是soketio的内置事件,当客户端与服务端连接之后,前端和后端都会收到一个名为‘connect’的事件,服务端接到这个事件就会执行test_connect函数中的内容了。

  再说namespace,namespace可以标志多个事件,在官方文档的解释是“Namespaces allow a client to open multiple connections to the server that are multiplexed on a single socket.”。当一个客户端连接服务器的不同命名域的时候,可以在同一个socket连接里完成。我的理解是一个namespace就定义了一个后端websocket连接的接口,客户端与服务器通过三次握手建立socket连接后,连接不同的服务器接口,socket的连接并不会断开。这可以类比于http的路由(但是完全不同哦,因为传输协议完全不一样),在http连接范畴,当用户登录后,访问服务器不同的路由并不会改变它的登录状态。一个后端接口可以接受多个客户端的socket连接,如果在后端的emit中定义‘broadcast=True’,那么所有连接到这个命名域的客户端都会收到这个消息,命名域之间也可以通过发送消息指定命名域的方式来相互通信。

  再看soketio.emit,第一个参数'server_response'是服务端发送这个消息的事件名,在客户端要建立一个接受这个事件的函数处理,后面的字典就是消息内容,namespace='/test_conn'表示这个消息还是发送到同一个信道(test_conn)中。emit发送信息只能从前端发到后端或者从后端发向前端,如果在在前端emit(‘event’,{data})再写socket.on('event', {data})是收不到的。

四、定时推送

  实验的目的是服务端定时发送一个随机数到客户端,并且客户端可以及时显示。

  一开始,我在socketio装饰的函数中写了一个while循环

  1. from flask import Flask, render_template
  2. from flask_socketio import SocketIO,emit
  3. import random
  4. async_mode = None
  5. app = Flask(__name__)
  6. app.config['SECRET_KEY'] = 'secret!'
  7. socketio = SocketIO(app)
  8.  
  9. @app.route('/')
  10. def index():
  11. return render_template('index.html')
  12.  
  13. @socketio.on('connect', namespace='/test_conn')
  14. def test_connect():
  15. while True:
  16. socketio.sleep(5)
  17. t = random.randint(1, 100)
  18. socketio.emit('server_response',
  19. {'data': t},namespace='/test_conn')
  20.  
  21. if __name__ == '__main__':
  22. socketio.run(app, debug=True)

  事实证明这样是行不通的,虽然看上去,虽然服务端陷入while的死循环中,但是emit函数每次都会执行,所以理论上客户端应该可以定时收到服务端的随机数。但是结果是客户端根本接收不到,连soketio.on函数都没有触发运行。

  原因应该是当服务端陷入死循环,会影响与客户端之间的websocket连接,总之写while true需谨慎

  在flask_socketio的示例程序中,我找到了用后台线程进行while循环以解决这个问题的方法。

  1. from flask import Flask, render_template
  2. from flask_socketio import SocketIO,emit
  3. from threading import Lock
  4. import random
  5. async_mode = None
  6. app = Flask(__name__)
  7. app.config['SECRET_KEY'] = 'secret!'
  8. socketio = SocketIO(app)
  9. thread = None
  10. thread_lock = Lock()
  11.  
  12. @app.route('/')
  13. def index():
  14. return render_template('index.html')
  15.  
  16. @socketio.on('connect', namespace='/test_conn')
  17. def test_connect():
  18. global thread
  19. with thread_lock:
  20. if thread is None:
  21. thread = socketio.start_background_task(target=background_thread)
  22.  
  23. def background_thread():
  24. while True:
  25. socketio.sleep(5)
  26. t = random.randint(1, 100)
  27. socketio.emit('server_response',
  28. {'data': t},namespace='/test_conn')
  29.  
  30. if __name__ == '__main__':
  31. socketio.run(app, debug=True)

五、客户端

  index.html的内容如下

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <script type="text/javascript" src="//code.jquery.com/jquery-1.4.2.min.js"></script>
  7. <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
  8. </head>
  9. <body>
  10. <h1 id="t"></h1>
  11. <script type="text/javascript">
  12. $(document).ready(function() {
  13. namespace = '/test_conn';
  14. var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace, {transports: ['websocket']});
  15. socket.on('server_response', function(res) {
  16. console.log(res.data);
  17. $('#t').text(res.data);
  18. });
  19. });
  20. </script>
  21. </body>
  22. </html>

  注意客户端也要导入socketio的库,然后用io.connect建立命名域的socket连接。

  如果不加{transports: ['websocket']},实际上建立的是长轮询。长轮询或websocket都是由客户端发起的

  最后在浏览器输入http://127.0.0.1:5000就可以了

六、结果

  开发者工具的console可以查看日志

使用flask_socketio实现服务端向客户端定时推送的更多相关文章

  1. 利用控制台承载SignalR作为服务端、及第三方推送信息

    一 首先建立一个控制台需要引用一些组件 特别要注意引用Microsoft.Owin.Host.HttpListener别忘了这个组件,不引用他可能程序正常运行不会报错,但服务器一直开启失败(我之前就是 ...

  2. java服务端集成极光消息推送--详细开发步骤

    1.极光推送账号准备 要使用极光消息推送必须先在官方网站上注册账号,并添加应用. 产品介绍:https://docs.jiguang.cn/jpush/guideline/intro/ 注册开发者账号 ...

  3. php做推送服务端实现ios消息推送

    本文部分内容引用于 http://zxs19861202.iteye.com/blog/1532460 准备工作 1.获取手机注册应用的deviceToken(iphone手机注册应用时返回唯一值de ...

  4. oauth2.0服务端与客户端搭建

    oauth2.0服务端与客户端搭建 - 推酷 今天搭建了oauth2.0服务端与客户端.把搭建的过程记录一下.具体实现的功能是:client.ruanwenwu.cn的用户能够通过 server.ru ...

  5. 解决有关flask-socketio中服务端和客户端回调函数callback参数的问题(全网最全)

    由于工作当中需要用的flask_socketio,所以自己学习了一下如何使用,查阅了有关文档,当看到回调函数callback的时候,发现文档里都描述的不太清楚,最后终于琢磨出来了,分享给有需要的朋友 ...

  6. Centos6.9 搭建rsync服务端与客户端 案例:全网备份项目

    rsync的企业工作场景说明 1)定时备份 1.1生产场景集群架构服务器备份方案项目 借助cron+rsync把所有客户服务器数据同步到备份服务器 2)实时复制 本地数据传输模式(local-only ...

  7. 【Eureka】服务端和客户端

    [Eureka]服务端和客户端 转载:https://www.cnblogs.com/yangchongxing/p/10778357.html Eureka服务端 1.添加依赖 <?xml v ...

  8. 基于Select模型的Windows TCP服务端和客户端程序示例

    最近跟着刘远东老师的<C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台)>,Bilibili视频地址为C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台),重新复习下 ...

  9. asp.net获取服务端和客户端信息

    asp.net获取服务端和客户端信息 获取服务器名:Page.Server.ManchineName获取用户信息:Page.User 获取客户端电脑名:Page.Request.UserHostNam ...

随机推荐

  1. 【题解】ID分配

    题目描述 您正在处理要为每个客户端分配唯一ID的特定系统的后端.但是,系统是分布式的,并且有许多组件,每个组件都必须能够为客户端分配ID.换句话说,您希望每个组件都使用尽可能少的组件之间的通信来分配I ...

  2. 【BZOJ3413】匹配(后缀自动机,线段树合并)

    [BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ...

  3. 神奇的操作——线段树合并(例题: BZOJ2212)

    什么是线段树合并? 首先你需要动态开点的线段树.(对每个节点维护左儿子.右儿子.存储的数据,然后要修改某儿子所在的区间中的数据的时候再创建该节点.) 考虑这样一个问题: 你现在有两棵权值线段树(大概是 ...

  4. HDU 1029 Ignatius and the Princess IV / HYSBZ(BZOJ) 2456 mode(思维题,~~排序?~~)

    HDU 1029 Ignatius and the Princess IV (思维题,排序?) Description "OK, you are not too bad, em... But ...

  5. Xshell配置是vi显示多种颜色

    在链接中,点 File菜单——properties   或按快捷键 alt+p 第一步: Properties--->Terminal 右边的窗口中,将Terminal Type 选择为linu ...

  6. SpringMVC接受JSON参数详解及常见错误总结我改

    SpringMVC接受JSON参数详解及常见错误总结 最近一段时间不想使用Session了,想感受一下Token这样比较安全,稳健的方式,顺便写一个统一的接口给浏览器还有APP.所以把一个练手项目的前 ...

  7. Codeforces Round #419 (Div. 2)(B)差分数组

    传送门:Problem B https://www.cnblogs.com/violet-acmer/p/9721160.html 题意: Karen有n个关于煮咖啡的食谱,每个食谱都有个煮咖啡的最适 ...

  8. ubuntu14安装node0.12.7

    1. 官网下载linux系统二进制文件, 链接如下: https://nodejs.org/ 2. 解压到/opt/目录 3. 设置node环境变量 export NODE_HOME=/opt/nod ...

  9. yolov2-tiny-voc.cfg 参数解析

    一.参数解析 [net] batch=64 # number of images pushed with a forward pass through the network subdivisions ...

  10. Hadoop基础-HDFS的API常见操作

    Hadoop基础-HDFS的API常见操作 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本文主要是记录一写我在学习HDFS时的一些琐碎的学习笔记, 方便自己以后查看.在调用API ...