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多线程爬虫简单示例
Mar 04 Python
快速入门python学习笔记
Dec 06 Python
python版学生管理系统
Jan 10 Python
django 按时间范围查询数据库实例代码
Feb 11 Python
python中找出numpy array数组的最值及其索引方法
Apr 17 Python
python切片及sys.argv[]用法详解
May 25 Python
Python实现的连接mssql数据库操作示例
Aug 17 Python
Python 3 实现定义跨模块的全局变量和使用教程
Jul 07 Python
Python爬虫 scrapy框架爬取某招聘网存入mongodb解析
Jul 31 Python
python实现邮件循环自动发件功能
Sep 11 Python
如何使用python写截屏小工具
Sep 29 Python
Python使用Kubernetes API访问集群
May 30 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中通过加号合并数组的一个简单方法分享
2011/01/27 PHP
PHP+SQL 注入攻击的技术实现以及预防办法
2011/01/27 PHP
PHP 查找字符串常用函数介绍
2012/06/07 PHP
php学习笔记之基础知识
2014/11/08 PHP
php实现删除空目录的方法
2015/03/16 PHP
JQuery 学习笔记 选择器之四
2009/07/23 Javascript
基于jquery实现漂亮的动态信息提示效果
2011/08/02 Javascript
jquery form 隐藏的input 选择
2014/04/29 Javascript
深入了解Node.js中的一些特性
2014/09/25 Javascript
Windows系统下使用Sublime搭建nodejs环境
2015/04/13 NodeJs
如何通过js实现图片预览功能【附实例代码】
2016/03/30 Javascript
jQuery子元素过滤选择器用法示例
2016/09/09 Javascript
jquery删除table当前行的实例代码
2016/10/07 Javascript
BootStrap组件之进度条的基本用法
2017/01/19 Javascript
jQuery Masonry瀑布流布局神器使用详解
2017/05/25 jQuery
Angularjs自定义指令实现分页插件(DEMO)
2017/09/16 Javascript
angularJS自定义directive之带参方法传递详解
2018/10/09 Javascript
详解Vue串联过滤器的使用场景
2020/04/30 Javascript
Vue使用轮询定时发送请求代码
2020/08/10 Javascript
wepy--用vantUI 实现上弹列表并选择相应的值操作
2020/11/03 Javascript
python操作gmail实例
2015/01/14 Python
Python基于Tkinter的HelloWorld入门实例
2015/06/17 Python
在django中使用自定义标签实现分页功能
2017/07/04 Python
Python实现基本数据结构中队列的操作方法示例
2017/12/04 Python
用Python shell简化开发
2018/08/08 Python
Python如何爬取微信公众号文章和评论(基于 Fiddler 抓包分析)
2019/06/28 Python
使用python将mysql数据库的数据转换为json数据的方法
2019/07/01 Python
python 实现GUI(图形用户界面)编程详解
2019/07/17 Python
在Python中获取操作系统的进程信息
2019/08/27 Python
波兰运动鞋网上商店:e-Sporting
2018/02/16 全球购物
《故都的秋》教学反思
2014/04/15 职场文书
交警失职检讨书
2015/01/26 职场文书
您对思维方式了解多少?
2019/12/09 职场文书
Redis安装启动及常见数据类型
2021/04/14 Redis
微信小程序实现聊天室功能
2021/06/14 Javascript
使用Canvas绘制一个游戏人物属性图
2022/03/25 Javascript