使用flask_socketio实现服务端向客户端定时推送

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

  双向通道意味着在链接时,服务端随时能够发送消息给客户端,反之亦然,这在一些须要即时通信的场景好比多人聊天室很是重要。html

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

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

1、安装python

pip install flask-socketio

 

2、实现对flask的封装jquery

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服务器的启动web

 

3、服务端向客户端推送ajax

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

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

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})是收不到的。

 

4、定时推送

  实验的目的是服务端定时发送一个随机数到客户端,而且客户端能够及时显示。

  一开始,我在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)

 

5、客户端

  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就能够了

 

6、结果

 

  开发者工具的console能够查看日志

 

相关文章
相关标签/搜索