头图:https://cdn.naraku.cn/imgs/Flask-SocketIO.jpg
摘要:一个Flask-SocketIO小Demo
最近在做一个Flask程序,其中一个需求是前端传递参数,后端接收到后调用命令行,并将控制台打印的日志实时推送到前端显示。经过搜索得知想要实现该功能大概有2种方式:1种是利用调度工具Celery,另1种就是Websocket。
准备
安装
Flask-SocketIO库$ pip install flask-socketio
编写一个Flask程序
from flask import Flask, render_template, requestfrom flask_socketio import SocketIO, emitfrom threading import Lockimport subprocess, geventasync_mode = Noneapp = Flask(__name__)app.config['SECRET_KEY'] = 'secret!'socketio = SocketIO(app)thread = Nonethread_lock = Lock()@app.route('/')def index():return render_template('index.html')
交互
其中
poll()函数有如下返回值,这里判断状态不为None即判断为运行结束并跳出循环- 0, 正常结束
- 1, sleep
- 2, 子进程不存在
- -15, Kill
- None, 正在运行
@socketio.on('imessage', namespace='/job')def ping(message):url = message['data']cmd = f'ping {url}'p = subprocess.Popen(cmd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False)while True:for line in iter(p.stdout.readline, b''):line = line.decode('gbk')emit('message', {'data': line})if p.poll() is not None:break
先与前端进行连接
$(document).ready(function() {namespace = '/job';var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);// 连接后发送日志socket.on('connect', function(){console.log('[+] Connected')});});
后端通过
emit函数将内容发送到前端// 接收后端消息// 这里的message对应while循环中emit('message', {'data': line})socket.on('message', function(msg) {console.log(msg.data)$('#log').append('<br>' + $('<div/>').text(msg.data).html());});
前端也可以通过
emit向后端发送数据// 点击发送时将text框的内容发送到后端// 这里的imessage对应@socketio.on('imessage', namespace='/job')$('form#emit').submit(function(event) {socket.emit('imessage', {data: $('#emit_data').val()});return false;});
代码
app.pyfrom flask import Flask, render_template, requestfrom flask_socketio import SocketIO, emitfrom threading import Lockimport subprocess, geventasync_mode = Noneapp = Flask(__name__)app.config['SECRET_KEY'] = 'secret!'socketio = SocketIO(app)thread = Nonethread_lock = Lock()@app.route('/')def index():return render_template('index.html')@socketio.on('imessage', namespace='/job')def ping(message):url = message['data']cmd = f'ping {url}'p = subprocess.Popen(cmd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False)while True:for line in iter(p.stdout.readline, b''):line = line.decode('gbk')emit('message', {'data': line})if p.poll() is not None:breakif __name__ == '__main__':socketio.run(app)
index.html<!DOCTYPE html><html><head><meta charset="UTF-8"><title>SocketIO</title><script src="//code.jquery.com/jquery-3.2.1.slim.min.js"></script><script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script><script type="text/javascript">$(document).ready(function() {namespace = '/job';var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);// 连接后发送日志socket.on('connect', function(){console.log('[+] Connected')});// 接收后端消息socket.on('message', function(msg) {console.log(msg.data)$('#log').append('<br>' + $('<div/>').text(msg.data).html());});// 点击发送时将text框的内容发送到后端$('form#emit').submit(function(event) {socket.emit('imessage', {data: $('#emit_data').val()});return false;});});</script></head><body><form id="emit" method="POST" action='#'><input type="text" name="emit_data" id="emit_data" placeholder="Message"><input type="submit" value="发送"></form><h2>Receive:</h2><div id="log"></div></body></html>
参考
- Flask-SocketIO官方文档翻译
- 使用flask_socketio实现客户端间即时通信
