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

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

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

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

一、安装

pip install flask-socketio

二、实现对flask的封装

from flask import Flask, render_template
from flask_socketio import SocketIO,emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app) if __name__ == '__main__':
socketio.run(app, debug=True)

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

三、服务端向客户端推送

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

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

from flask import Flask, render_template
from flask_socketio import SocketIO,emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app) @socketio.on('connect', namespace='/test_conn')
def test_connect():
socketio.emit('server_response',
{'data': ‘connected’},namespace='/test_conn') if __name__ == '__main__':
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循环

from flask import Flask, render_template
from flask_socketio import SocketIO,emit
import random
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app) @app.route('/')
def index():
return render_template('index.html') @socketio.on('connect', namespace='/test_conn')
def test_connect():
while True:
socketio.sleep(5)
t = random.randint(1, 100)
socketio.emit('server_response',
{'data': t},namespace='/test_conn') if __name__ == '__main__':
socketio.run(app, debug=True)

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

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

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

from flask import Flask, render_template
from flask_socketio import SocketIO,emit
from threading import Lock
import random
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
thread = None
thread_lock = Lock() @app.route('/')
def index():
return render_template('index.html') @socketio.on('connect', namespace='/test_conn')
def test_connect():
global thread
with thread_lock:
if thread is None:
thread = socketio.start_background_task(target=background_thread) def background_thread():
while True:
socketio.sleep(5)
t = random.randint(1, 100)
socketio.emit('server_response',
{'data': t},namespace='/test_conn') if __name__ == '__main__':
socketio.run(app, debug=True)

五、客户端

  index.html的内容如下

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="//code.jquery.com/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
</head>
<body>
<h1 id="t"></h1>
<script type="text/javascript">
$(document).ready(function() {
namespace = '/test_conn';
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace, {transports: ['websocket']});
socket.on('server_response', function(res) {
console.log(res.data);
$('#t').text(res.data);
});
});
</script>
</body>
</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. js脚本 将本地图片路径转换为html

    公司业务类似于电商, 因此有很多纯图片展示的商品详情页, 类似淘宝店商品页面下的多个图片组成的商品详情页, 页面很简单, 就是一大串img标签, 但是每次做详情页都要配合emmet一顿操作( 如下图 ...

  2. c++ 可变参数模板

    可变参数模板,自己尝试了个例子,如下: // variadicTemplates.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #incl ...

  3. linux ssh和scp消除每次问yes/no

    ssh 10.11.3.61The authenticity of host '10.11.3.61 (10.11.3.61)' can't be established.RSA key finger ...

  4. 最大获利 HYSBZ - 1497 (最大权闭合图)

    最大权闭合图: 有向图,每个点有点权,点权可正可负.对于任意一条有向边i和j,选择了点i就必须选择点j,你需要选择一些点使得得到权值最大. 解决方法: 网络流 对于任意点i,如果i权值为正,s向i连容 ...

  5. Educational Codeforces Round 26 B,C

    B. Flag of Berland 链接:http://codeforces.com/contest/837/problem/B 思路:题目要求判断三个字母是否是条纹型的,而且宽和高相同,那么先求出 ...

  6. 【POJ 2823】Sliding Window(单调队列/堆)

    BUPT2017 wintertraining(16) #5 D POJ - 2823 题意 给定n,k,求滑窗[i,i+k-1]在(1<=i<=n)的最大值最小值. 题解 单调队列或堆. ...

  7. 【ZOJ2276】Lara Croft(bfs)

    BUPT2017 wintertraining(16) #4 D ZOJ - 2276 题意 n个数字绕成环,有两个指示数字的方块,每次可以顺时针或逆时针移动其中一个,步数是它当前位置的数字a[i], ...

  8. BZOJ5312 冒险(势能线段树)

    BZOJ题目传送门 表示蒟蒻并不能一眼看出来这是个势能线段树. 不过仔细想想也并非难以理解,感性理解一下,在一个区间里又与又或,那么本来不相同的位也会渐渐相同,线段树每个叶子节点最多修改\(\log ...

  9. LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)

    题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...

  10. 自学Linux Shell7.2-linux文件权限

    点击返回 自学Linux命令行与Shell脚本之路 7.2-linux文件权限 在linux中每个文件有所有者.所在组.其它组的概念 所有者一般为文件的创建者,谁创建了该文件,就天然的成为该文件的所有 ...