>用户上传了一个我需要在服务器上执行的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)
这个独立的示例将
- 从 Web 套接字读取
python
脚本 - 将脚本写入文件系统
- 在禁用输出缓冲的情况下运行脚本
- 一次读取一行脚本输出
- 将每行输出写入 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()