flask-socketio实现WebSocket的方法


Posted in Python onJuly 31, 2018

【flask-socektio】

之前不知道在哪个场合下提到过如何从web后台向前台推送消息。听闻了反向ajax技术这种模式之后,大呼神奇,试了一下之后发现也确实可以用。不过,反向ajax的代价也很明显,只要客户端还和服务端要有信息交互,服务端就必须还维持客户端的这个请求,然后在合适的时候返回。当客户端一多,这么做的成本会比较大。

其他的后端推前端的技术还有类似于隐藏frame,Comet、长轮询等等,没有详细了解过,总之也是各有千秋但也各有利弊。

前不久在开发中碰到了这样一个场景,就是在后台执行一些代码,然后会根据执行的最新情况推送一些提示信息到前台让用户可以知道目前执行到哪一步了。典型就是一个后台向前端推送消息的,而且是比较简单的一个场景。用反向ajax的话好像略显累赘,因为消息的频度还是蛮高的,应该会消费不少网络资源,而且ajax请求的url后执行的程序肯定和后台的工作程序是并行的,如果要获得工作程序的进度信息可能还会涉及到进程间通信问题,总之各种麻烦。最好能找到一种解决方案,可以在后台随时推送数据后在前台实时展示并且允许后台程序继续跑的。

然后找了下就找到了websocket这种html5之后才有的技术。另外再找了下发现了flask-socketio这个拓展模块添加了flask对websocket的支持。

概述

websocket是html5中实现了服务端和客户端进行双向文本或二进制数据通信的一种新协议,其实已经低于HTTP协议本身和HTTP本质上没有什么关系了。不过形式上两者还是有想象之处。因此websocket的连接地址是长这样的:ws://localhost:8080。可以看到,协议修饰符不是http了。

另外,websocket在连接建立阶段是通过HTTP的握手方式进行的,这可以看做是为了兼容浏览器或者使用一些现成的功能来实现,这样一种捷径。当连接建立之后,客户端和服务端之间就不再进行HTTP通信了,所有信息交互都由websocket接管。

从资源占用的角度上来说,其实websocket比ajax占用的资源更多,但它真正实现了全双工通信这一点还是很理想的,意味着无论是前端还是后台的信息交互程序编写都会变得更加方便。由于采用了新的协议,所以我们也需要适当地改造下前后台的程序。

前端ws编写以及socket.io.js

由于是比较新的东西,并不一定所有的浏览器都支持,所有可以用:

if ('WebSocket' in window){
 websocket = new WebSocket('ws://localhost:8080');
}

这样的方式来判断是否支持,只有支持的情况下才开始websocket处理

其实光实现双向通信是并没有什么用的,主要还是在通信过程中,让前后端发生一些动作,这就需要添加监听事件。在前端这里,我们可以给websocket这个对象的一些监听回调接口赋值,来规定在不同的场合下前端做些不同的事情。比如:

wesocket.onopen = function(){
  alert('建立websocket连接');
}

websocket.onerror = function(){
  alert('WebSocket连接发生错误');
}

/****
等等,由于有封装程度更高的js模块,就不扩展写从较底层构建websocket的方法了
****/

 socket.io.js

如果觉得略显麻烦,那么可以用一些已经封装好的websocket的js库,比如socket.io.js。这个库似乎是专门为了node.js设计的,(主要因为网上随便一搜都是把它和node.js结合使用相关的信息。。)不过单独拿出来也能用。引用如果使用cdn方式,那么可以写

<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>

应用了socket.io.js的一个简单socket对象的创建可以这么写:

var websocket_url = 'http"//' + document.domain + ':' + location.port + '/testnamespace';
//没错是用http开头的url了,因为这个库会自动解析并帮我们创建websocket对象的
//最后的namespace是websocket中的命名空间,后面再讲
var socket = io.connect(wesocket_url);

得到了这个socket对象之后,我们可以用这个对象进行消息的收发。简答的消息收发如下:

//发送消息
socket.emit('request_for_response',{'param':'value'});
//监听回复的消息
socket.on('response',function(data){
  if (data.code == '200'){
    alert(data.msg);
  }
  else{
    alert('ERROR:' + data.msg);
  }
});

其中request_for_response和response两个名字都是我自己取的,这两个名字应该和后端相关的名字协同一致才能保证通信的成功。另外刚才提到了namespace这个东西,因为namespace是在socket创建的时候就决定的,也就是说这些消息的收发都是在'testnamespace'这个空间中进行的。所以在后端上这个空间也要和前端一致。

后端socket编写(flask-socketio)

这里用python的后端来说明。python有socketio这个模块,不过和前端时一样,直接从较为底层的开始编写比较僵硬。各类web框架应该都对websocket有了较好的支持,这里选用了flask这个框架的flask-socketio的扩展。

flask-socketio的创建和运行方式如下:

from flask import Flask 
from flask_socketio import SocketIO,emit
app = Flask(__name__)
socketio = SocketIO()
socketio.init_app(app)
"""
对app进行一些路由设置
"""
"""
对socketio进行一些监听设置
"""
if __name__ == '__main__':
  socketio.run(app,debug=True,host='0.0.0.0',port=5000)
  #这里就不再用app.run而用socketio.run了。socketio.run的参数和app.run也都差不多

上面的,对app的路由设置就不再说了,想说的是对socketio的监听设置,这才是真正关系到前后端websocket通信过程的。结合前面的前端代码,socketio的监听设置可以这样做:

@socketio.on('request_for_response',namespace='/testnamespace')
def give_response(data):
  value = data.get('param')
  #进行一些对value的处理或者其他操作,在此期间可以随时会调用emit方法向前台发送消息
  emit('response',{'code':'200','msg':'start to process...'})
  time.sleep(5)
  emit('response',{'code':'200','msg':'processed'})

socketio也用了和app.route类似的装饰器的形式进行监听设置。主要参数中有namespace这一项,也就是这项指定了这个监听的范围。在前端,只有注册在testnamespace上的socket,emit向request_for_response的消息才会被这个函数接受并处理。处理函数自带一个参数用来接收前端emit来消息中的那个object,在处理函数中可以对其解析处理。随后后端向前端发送了start to process的消息。也使用了emit这个方法,然后指明了监听是response。也就是说前端on在response上的监听处理函数会处理这个消息(当然还是在testnamespace的框架内)。发出消息后后端不会被阻塞而是继续向下执行,在处理了5秒钟之后发出了结束处理的消息,前端自然隔了五秒之后就得到了这个消息了。

socket监听响应函数本身不需要返回什么值,只需要在处理过程中适当的位置emit出消息即可。

网上其他一些教程中会提到send方法来取代emit方法的位置(无论是前端还是后端),其实send方法就是把上文中的'request_for_response','response'这两个标识都默认成'message'。如此在写的时候就不用写事件名,直接写要传递的参数即可。反过来看,用emit方法实际上是做了一个自定义事件的工作,可以说更加灵活多变一点。

略大项目中Manager的支持

一般而言,上面那样的socketio.run总给人感觉是个玩具项目。如果要做一个大型的项目那肯定得用一些更加高端的启动方式才行,另外配置也应该有独立的config.py,通过app.config.form_object的方法来填充配置。

但是经过我的实验,虽然用manager.run可以让socketio工作,但是似乎会存在类似于缓存一样的一种机制。也就是说后台emit出来的信息并不直接发送到前端,而是在整个响应函数执行结束之后一股脑儿的爆出来。不知道是后端发送时进行了缓存还是前端接收时进行了缓存。

所以如果要用websocket的话尽量还是用socketio.run这种方法启动把。至于配置可以在socketio.run中添加类似于debug=current_app.config['DEBUG'],host=current_app.config['HOST']这样的方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python中正则表达式的使用详解
Oct 17 Python
利用 Monkey 命令操作屏幕快速滑动
Dec 07 Python
用生成器来改写直接返回列表的函数方法
May 25 Python
python写一个md5解密器示例
Feb 23 Python
python爬虫正则表达式之处理换行符
Jun 08 Python
Pytorch使用MNIST数据集实现CGAN和生成指定的数字方式
Jan 10 Python
pyftplib中文乱码问题解决方案
Jan 11 Python
浅谈Python中的继承
Jun 19 Python
Python3爬虫中Splash的知识总结
Jul 10 Python
Python同时迭代多个序列的方法
Jul 28 Python
python中翻译功能translate模块实现方法
Dec 17 Python
pandas数值排序的实现实例
Jul 25 Python
深入flask之异步非堵塞实现代码示例
Jul 31 #Python
Django框架使用富文本编辑器Uedit的方法分析
Jul 31 #Python
Windows下python3.6.4安装教程
Jul 31 #Python
windows下pycharm安装、创建文件、配置默认模板
Jul 31 #Python
win8下python3.4安装和环境配置图文教程
Jul 31 #Python
Win8.1下安装Python3.6提示0x80240017错误的解决方法
Jul 31 #Python
Win8下python3.5.1安装教程
Jul 29 #Python
You might like
PHP的命令行命令使用指南
2015/08/18 PHP
laravel通过创建自定义artisan make命令来新建类文件详解
2017/08/17 PHP
PHP开发实现微信退款功能示例
2017/11/25 PHP
让JavaScript 轻松支持函数重载 (Part 1 - 设计)
2009/08/04 Javascript
javascript oop开发滑动(slide)菜单控件
2010/08/25 Javascript
将HTMLCollection/NodeList/伪数组转换成数组的实现方法
2011/06/20 Javascript
js中字符替换函数String.replace()使用技巧
2011/08/14 Javascript
js innerHTML 改变div内容的方法
2013/08/03 Javascript
javascript对下拉列表框(select)的操作实例讲解
2013/11/29 Javascript
JavaScript如何实现对数字保留两位小数一位自动补零
2015/12/18 Javascript
JavaScript的Ext JS框架中的GridPanel组件使用指南
2016/05/21 Javascript
jQuery制作网页版选项卡
2016/07/28 Javascript
js 获取范围内的随机数实例代码
2016/08/02 Javascript
AngularJS中$watch和$timeout的使用示例
2016/09/20 Javascript
使用JavaScript解决网页图片拉伸问题(推荐)
2016/11/25 Javascript
关于TypeScript中import JSON的正确姿势详解
2017/07/25 Javascript
vue学习教程之带你一步步详细解析vue-cli
2017/12/26 Javascript
vue实现鼠标移入移出事件代码实例
2019/03/27 Javascript
[03:56]还原FTP电影首映式 DOTA2群星拼出遗迹世界
2014/03/26 DOTA
Python彩色化Linux的命令行终端界面的代码实例分享
2016/07/02 Python
python 实现tar文件压缩解压的实例详解
2017/08/20 Python
python简单操作excle的方法
2018/09/12 Python
python isinstance函数用法详解
2020/02/13 Python
python的json包位置及用法总结
2020/06/21 Python
Node.js 和 Python之间该选择哪个?
2020/08/05 Python
HTML5 Canvas实现放大镜效果示例
2020/03/25 HTML / CSS
英国快时尚女装购物网站:PrettyLittleThing
2018/08/15 全球购物
德国亚洲食品网上商店:asiafoodland.de
2019/12/28 全球购物
华为python面试题
2016/05/03 面试题
送给程序员的20个Java集合面试问题
2014/08/06 面试题
5个HTML5的常用本地存储方式详解与介绍
2021/03/27 HTML / CSS
最新自我评价范文
2013/11/16 职场文书
七一党建活动方案
2014/01/28 职场文书
《姥姥的剪纸》教学反思
2014/02/25 职场文书
2014国庆节餐厅促销活动策划方案
2014/09/16 职场文书
mysql幻读详解实例以及解决办法
2022/06/16 MySQL