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编写一个国际象棋AI程序
Nov 28 Python
Python 登录网站详解及实例
Apr 11 Python
详解python异步编程之asyncio(百万并发)
Jul 07 Python
Python 实现两个列表里元素对应相乘的方法
Nov 14 Python
python实现连连看游戏
Feb 14 Python
新手入门学习python Numpy基础操作
Mar 02 Python
解决pyecharts运行后产生的html文件用浏览器打开空白
Mar 11 Python
python使用pyecharts库画地图数据可视化的实现
Mar 25 Python
Python如何实现定时器功能
May 28 Python
Django websocket原理及功能实现代码
Nov 14 Python
最新Python idle下载、安装与使用教程图文详解
Nov 28 Python
使用python对excel表格处理的一些小功能
Jan 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结合jQuery实现找回密码
2015/07/22 PHP
Yii CGridView用法实例详解
2016/07/12 PHP
javascript转换字符串为dom对象(字符串动态创建dom)
2010/05/10 Javascript
关于js类的定义
2011/06/28 Javascript
滚动图片效果 jquery实现回旋滚动效果
2013/01/08 Javascript
angularJS 中$attrs方法使用指南
2015/02/09 Javascript
跟我学习javascript的prototype原型和原型链
2015/11/18 Javascript
JavaScript实现跑马灯抽奖活动实例代码解析与优化(一)
2016/02/16 Javascript
解析Node.js基于模块和包的代码部署方式
2016/02/16 Javascript
jQuery EasyUI 为Combo,Combobox添加清除值功能的实例
2017/04/13 jQuery
js实现图片轮播效果学习笔记
2017/07/26 Javascript
基于js 本地存储(详解)
2017/08/16 Javascript
vue+node+webpack环境搭建教程
2017/11/05 Javascript
Vue.js实现图片的随意拖动方法
2018/03/08 Javascript
微信小程序日历效果
2018/12/29 Javascript
js实现窗口全屏示例详解
2019/09/17 Javascript
微信小程序自定义纯净模态框(弹出框)的实例代码
2020/03/09 Javascript
JQuery插件tablesorter表格排序实现过程解析
2020/05/28 jQuery
Javascript中Math.max和Math.max.apply的区别和用法详解
2020/08/24 Javascript
讲解Python中for循环下的索引变量的作用域
2015/04/15 Python
python笔记:mysql、redis操作方法
2017/06/28 Python
Python编程使用NLTK进行自然语言处理详解
2017/11/16 Python
利用python 更新ssh 远程代码 操作远程服务器的实现代码
2018/02/08 Python
python selenium操作cookie的实现
2020/03/18 Python
python爬虫实例之获取动漫截图
2020/05/31 Python
Python字典dict常用方法函数实例
2020/11/09 Python
Lands’ End官网:经典的美国生活方式品牌
2016/08/14 全球购物
洲际酒店集团美国官网:IHG美国
2017/11/16 全球购物
戴森比利时官方网站:Dyson BE
2020/10/03 全球购物
制药工程专业应届生求职信
2013/09/24 职场文书
库房主管岗位职责
2013/12/31 职场文书
2015年学校政教处工作总结
2015/05/26 职场文书
2015年度优秀员工获奖感言
2015/07/31 职场文书
机关单位2016年法制宣传日活动总结
2016/04/01 职场文书
2019年XX公司的晨会制度及流程!
2019/07/23 职场文书
Java 中的 Unsafe 魔法类的作用大全
2021/06/26 Java/Android