如何通过 WebSocket 发送长时间运行的 Python 脚本的输出?



>用户上传了一个我需要在服务器上执行的python文件,并发回通过WebSocket创建的stdout。执行的python文件将运行几分钟,我需要通过套接字返回stdout,因为它们是实时"打印"出来的,而不是在脚本完成时。

我试过使用:Python。将标准输出重定向到套接字,但这不是 WebSocket,我的 React 前端无法成功连接到它。(如果你能解决这个问题,那也会解决我的问题(

我也尝试使用websocketd但由于我无法在每个用户添加的打印语句后添加sys.stdout.flush(),因此无法解决我的问题。

我也尝试使用子进程的 PIPE 功能,但有相同的刷新问题

async def time(websocket, path):
while True:
data = "test"
await websocket.send(data)
# Run subprocess to execute python file in here
# sys.stdout => websocket.send             
start_server = websockets.serve(time, "127.0.0.1", 5678)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

这是我正在使用的python测试脚本:

from time import sleep
for i in range(40):
print(i)
sleep(0.1)

这个独立的示例将

  1. 从 Web 套接字读取python脚本
  2. 将脚本写入文件系统
  3. 在禁用输出缓冲的情况下运行脚本
  4. 一次读取一行脚本输出
  5. 将每行输出写入 Web 套接字
import asyncio
import websockets
import subprocess
async def time(websocket, path):
script_name = 'script.py'
script = await websocket.recv()
with open(script_name, 'w') as script_file:
script_file.write(script)
with subprocess.Popen(['python3', '-u', script_name],
stdout=subprocess.PIPE,
bufsize=1,
universal_newlines=True) as process:
for line in process.stdout:
line = line.rstrip()
print(f"line = {line}")
await websocket.send(line)
start_server = websockets.serve(time, "127.0.0.1", 5678)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

我使用以下javascript代码来测试服务器:

const WebSocket = require('ws');
let socket = new WebSocket("ws://127.0.0.1:5678");
socket.onopen = function(e) {
let script = '
import timen
for x in range(100):n
print(f"x = {x}")n
time.sleep(0.25)n
';
console.log("sending data...");
socket.send(script);
console.log("done.");
};
socket.onmessage = function(event) {
console.log(event.data.toString());
};
socket.onerror = function(event) {
console.log(event);
};

Popen的使用基于对这个问题的答案:

从子进程读取流输入.communication((

-u选项传递给python以禁用输出缓冲。

这个类将用作服务器的包装器

import sys

class ServerWrapper:
def __init__(self, ws):
self.__ws = ws
sys.stdout = self
def write(self, data):
self.__ws.send(data)
def close(self):
sys.stdout = sys.__stdout__

您需要使用 Websocket 对其进行初始化。

每次调用 print 时都会调用 write 函数(因为我们sys.stdout更改为自定义输出。

然后,在完成脚本执行后,您可以使用close还原标准输出


import asyncio
import websockets
import subprocess
import sys

class ServerWrapper:
def __init__(self, ws):
self.__ws = ws
sys.stdout = self
def write(self, data):
self.__ws.send(data)
def close(self):
sys.stdout = sys.__stdout__

async def time(websocket, path):
wrapper = ServerWrapper(websocket)
# get commands and execute them as you would normally do
# you don't need to worry about reading output and sending it

start_server = websockets.serve(time, "127.0.0.1", 5678)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

最新更新