首页 > 解决方案 > 将无缓冲的标准输出从子进程管道传输到 websocket

问题描述

您如何将标准输出从子进程传输到 websocket 而无需等待换行符?目前,下面的代码仅在换行符上发送标准输出。

为子进程运行的脚本附加的代码。输出是否没有从那里正确刷新?

发送数据.py:

import asyncio
import websockets
import subprocess
import sys
import os

async def foo(websocket, path):
        print ("socket open")
        await websocket.send("successfully connected")

        with subprocess.Popen(['sudo','python3', '-u','inline_print.py'],stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, universal_newlines=True) as p:
                for line in p.stdout:
                    line = str(line.rstrip())
                    await websocket.send(line)
                    p.stdout.flush()
                for line in p.stderr:
                    line = str(line.rstrip())
                    await websocket.send(line)
                    p.stdout.flush()


start_server = websockets.serve(foo, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

inline_print.py:

from time import sleep
import sys

loading = 'LOADING...LOADING...LOADING...LOADING...LOADING...'
for i in range(50):
    print(loading[i], sep='', end=' ', flush=True)
    sleep(0.1)

如果end=' '更改为,end='\n'则标准输出从send_data.py实时发生。

js客户端:

var ws = new WebSocket('ws://localhost:8765/');

ws.onmessage = function(event) {
  console.log(event.data);
};

我承认这个问题类似于这些:

从子进程中实时捕获标准输出

如何-我-获取​​-实时-信息-从-a-subprocess-popen-in-python-2-5 返回

在运行时拦截子进程的标准输出

但是,如果没有子流程中的换行符,任何解决方案都无法工作。

标签: pythonwebsocketsubprocessstdout

解决方案


如果你写

      for line in p.stdout:

然后你(有点)含蓄地说,你想等待一个完整的线路

你必须使用read(num_bytes)而不是readline()

下面以一个例子来说明:

sub.py:(示例子进程)

import sys, time
for v in range(20):
    print(".", end="")
    sys.stdout.flush()
    if v % 4 == 0:
        print()
    if v % 3 != 0:
        time.sleep(0.5)

rdunbuf.py:(读取标准输出无缓冲的示例)

contextlib, time, subprocess

def unbuffered(proc, stream='stdout'):
    stream = getattr(proc, stream)
    with contextlib.closing(stream):
        while True:
            last = stream.read(80) # read up to 80 chars
            # stop when end of stream reached
            if not last:
                if proc.poll() is not None:
                    break
            else:
                yield last

# open subprocess without buffering and without universal_newlines=True
proc = subprocess.Popen(["./sub.py"], stdout=subprocess.PIPE, bufsize=0)

for l in unbuffered(proc):
    print(l)
print("end")

另请注意,如果您的代码在产生正常输出之前产生大量错误消息,则可能会阻塞,因为您首先尝试读取所有正常输出,然后才从 stderr 读取数据。

无论是标准输出还是标准错误,您都应该像在任何管道缓冲区独立阻塞之前一样读取子进程产生的任何数据。您可以使用select.select()https://docs.python.org/3.8/library/select.html#select.select)来决定是否必须从标准输出或标准错误中读取


推荐阅读