【问题标题】:How to send the output of a long running Python script over a WebSocket?如何通过 WebSocket 发送长时间运行的 Python 脚本的输出?
【发布时间】:2019-12-23 18:22:03
【问题描述】:

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

我尝试过使用:Python. Redirect stdout to a socket,但这不是 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)

【问题讨论】:

  • 你不能创建一个类似文件的对象,给它设置sys.stdout,然后重写write函数来发送数据吗?
  • @בנימיןכהן 我该怎么做呢?这听起来确实像一个解决方案

标签: python websocket subprocess stdout


【解决方案1】:

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

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()

【讨论】:

  • 你能告诉我这将如何适应我现有的代码
  • self.__ws 的类型是 并且没有发送功能
  • 正如我所说.. 我从未使用过这个库。那么如何获取 websocket 对象呢?
【解决方案2】:

这个独立的例子将

  1. 从网络套接字读取python 脚本
  2. 将脚本写入文件系统
  3. 在禁用输出缓冲的情况下运行脚本
  4. 一次读取一行脚本输出
  5. 将每一行输出写入网络套接字
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 time\n\
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 的使用是基于对这个问题的回答:

Read streaming input from subprocess.communicate()

-u 选项被传递给 pythondisable output buffering

【讨论】:

  • 澄清一下,问题是该命令运行一个包含许多打印语句并运行几分钟的脚本,因此我需要将通过套接字发生的标准输出发送回客户端,而不是之后运行完毕
  • 这是需要添加到问题中的那种细节。从当前版本的问题来看,这些都不是显而易见的。
  • 无需道歉。有时人们没有意识到需要在问题中添加澄清评论,以便每个人都能看到它。
  • 使用更新后的答案,process.stdout 中的`for 行:` 循环不会执行,直到长时间运行的python 脚本完成执行。有没有办法获取实时日志?
  • 这是我正在执行的测试脚本: from time import sleep for i in range(40): print (i) sleep(0.1)
猜你喜欢
  • 1970-01-01
  • 2018-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-12
  • 1970-01-01
相关资源
最近更新 更多