最近在做一个Flask程序,其中一个需求是前端传递参数,后端接收到后调用命令行,并将控制台打印的日志实时推送到前端显示。经过搜索得知想要实现该功能大概有2种方式:1种是利用调度工具Celery,另1种就是Websocket。

准备

  • 安装Flask-SocketIO
$ pip install flask-socketio
  • 编写一个Flask程序
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
from threading import Lock
import subprocess, gevent
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')

交互

  • 其中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.py
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
from threading import Lock
import subprocess, gevent
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('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


if __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>

参考

最后修改:2020 年 10 月 18 日 11 : 42 PM
如果觉得我的文章对你有帮助,请我吃颗糖吧~