问题描述
在线编译器 这是我的网站,用户可以在其中运行控制台程序.
online compiler this is my website where users can run console programs.
目前,用户在运行程序之前必须输入程序输入.我正在尝试为程序构建实时用户输入(希望提供与他们在笔记本电脑上运行程序相同的体验).
At present, the user has to enter the program input before running the program. I am trying to build live user input for the program(want to give the same experience as they run programs on their laptop).
在为实现这一目标而进行的研究中,我发现了一种使用 websocket 来流式标准输出和标准输入的解决方案.
In research to achieve this I came across a solution to stream stdout and stdin with websocket.
我的实现
# coding: utf-8
import subprocess
import thread
from tornado.websocket import WebSocketHandler
from nbstreamreader import NonBlockingStreamReader as NBSR
class WSHandler(WebSocketHandler):
def open(self):
self.write_message("connected")
self.app = subprocess.Popen(['sh', 'app/shell.sh'], stdout=subprocess.PIPE, stdin=subprocess.PIPE,
shell=False)
self.nbsr = NBSR(self.app.stdout)
thread.start_new_thread(self.soutput, ())
def on_message(self, incoming):
self.app.stdin.write(incoming)
def on_close(self):
self.write_message("disconnected")
def soutput(self):
while True:
output = self.nbsr.readline(0.1)
# 0.1 secs to let the shell output the result
if not output:
print 'No more data'
break
self.write_message(output)
nbstreamreader.py
nbstreamreader.py
from threading import Thread
from Queue import Queue, Empty
class NonBlockingStreamReader:
def __init__(self, stream):
'''
stream: the stream to read from.
Usually a process' stdout or stderr.
'''
self._s = stream
self._q = Queue()
def _populateQueue(stream, queue):
'''
Collect lines from 'stream' and put them in 'quque'.
'''
while True:
line = stream.readline()
if line:
queue.put(line)
else:
raise UnexpectedEndOfStream
self._t = Thread(target=_populateQueue,
args=(self._s, self._q))
self._t.daemon = True
self._t.start() # start collecting lines from the stream
def readline(self, timeout=None):
try:
return self._q.get(block=timeout is not None,
timeout=timeout)
except Empty:
return None
class UnexpectedEndOfStream(Exception): pass
shell.sh
#!/usr/bin/env bash
echo "hello world"
echo "hello world"
read -p "Your first name: " fname
read -p "Your last name: " lname
echo "Hello $fname $lname ! I am learning how to create shell scripts"
此代码流标准输出直到 shell.sh 代码到达读取语句.
This code streams stdout un-till shell.sh code reaches read statement.
请指导我做错了什么.为什么它不等待标准输入并在完成程序执行之前打印没有更多数据"?
Please guide me what wrong I am doing. Why it doesn't wait for stdin and reaches print 'No more data' before complete program executions?
测试它的源代码https://github.com/mryogesh/streamconsole.git
推荐答案
除非您在 100 毫秒内发送输入,否则您的 readline()
方法会超时,这会中断循环.您没有看到 read -p
提示的原因是缓冲(因为 readline 和管道缓冲).最后,您的示例 javascript 不会发送尾随换行符,因此 read
不会返回.
Your readline()
method times out unless you send input within 100ms, which then breaks the loop. The reason you don't see the read -p
prompt is buffering (because of readline and pipe buffering). Finally, your example javascript doesn't send a trailing newline, so read
will not return.
如果您增加超时,请添加一个换行符,并且 找到解决缓冲问题的方法,您的示例应该基本上可以工作.
If you increase the timeout, include a newline, and find a way to work around buffering issues, your example should basically work.
我也会使用 tornado.process 和协程而不是子进程和线程:
I'd also use tornado.process and coroutines instead of subprocess and thread:
from tornado import gen
from tornado.process import Subprocess
from tornado.ioloop import IOLoop
from tornado.iostream import StreamClosedError
from tornado.websocket import WebSocketHandler
class WSHandler(WebSocketHandler):
def open(self):
self.app = Subprocess(['script', '-q', 'sh', 'app/shell.sh'], stdout=Subprocess.STREAM, stdin=Subprocess.STREAM)
IOLoop.current().spawn_callback(self.stream_output)
def on_message(self, incoming):
self.app.stdin.write(incoming.encode('utf-8'))
@gen.coroutine
def stream_output(self):
try:
while True:
line = yield self.app.stdout.read_bytes(1000, partial=True)
self.write_message(line.decode('utf-8'))
except StreamClosedError:
pass
这篇关于使用 websocket 直播标准输出和标准输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!