将未缓冲的标准输出从子进程传输到 websocket

Pipe unbuffered stdout from subprocess to websocket

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

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

send_data.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);
};

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

catching-stdout-in-realtime-from-subprocess

how-do-i-get-real-time-information-back-from-a-subprocess-popen-in-python-2-5

intercepting-stdout-of-a-subprocess-while-it-is-running

然而 none 的解决方案在子进程中没有换行符的情况下工作。

如果你写

      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:(示例读取 stddout 无缓冲)

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 读取数据。

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

读取