【Flask】 flask-socketio实现WebSocket

【flask-socektio】html

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

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

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

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

■  概述web

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

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

  从资源占用的角度上来讲,其实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']这样的方法。

相关文章
相关标签/搜索