0

我希望通过 JSON 或其他使用 Ruby 的 RPC 样式服务公开一个交互式命令行程序。我找到了一些技巧来做到这一点,但是在重定向输出和输入时我遗漏了一些东西。

至少在 linux 上的一种方法是将标准输入和标准输出重定向到一个文件,然后与文件读取和写入异步读取和写入该文件。我在谷歌搜索后尝试的另一种方法是使用 open4。这是我到目前为止编写的代码,但是在从标准输出中读取了几行代码后它就卡住了。

require "open4"
include Open4

status = popen4("./srcds_run -console -game tf +map ctf_2fort -maxplayers 6") do |pid, stdin, stdout, stderr|
  puts "PID #{pid}"
  lines=""
  while (line=stdout.gets)
    lines+=line
    puts line
  end
  while (line=stderr.gets)
    lines+=line
    puts line
  end
end

对此的任何帮助或一些见解将不胜感激!

4

3 回答 3

0

我建议使用 Xinetd(或类似的)在某个套接字上运行命令,然后使用 ruby​​ 网络代码。您在此处的代码中已经遇到的问题之一是您的两个 while 循环是连续的,这可能会导致问题。

于 2010-02-28T04:41:13.943 回答
0

编辑

您应该考虑AnyTerm集成。然后,您可以直接公开 AnyTerm,例如通过 Apache mod_proxy,或者让您的 Rails 控制器充当反向代理(处理身份验证/会话验证,然后播放controller.request减去任何 cookie 到localhost:<AnyTerm-daemon-port>,并将 AnyTerm 回复的任何内容作为响应发送回。)

class ConsoleController < ApplicationController
  # AnyTerm speaks via HTTP POST only
  def update
    # validate session
    ...
    # forward request to AnyTerm
    response = Net::HTTP.post_form(URI.parse('http://localhost:#{AnyTermPort}/', request.params))
    headers['Content-Type'] = response['Content-Type']
    render_text response.body, response.status
  end

否则,您需要使用IO::SelectIO::read_noblock来了解何时可以读取数据(从网络或子进程),这样您就不会死锁。也看到这个。还要检查您的 Rails 是否在多线程环境中使用,或者您的 Ruby 版本不受此 IO::Select 错误的影响。

您可以从以下几方面着手:

status = POpen4::popen4("ping localhost") do |stdout, stderr, stdin, pid|  
  puts "PID #{pid}"  
  # our buffers 
  stdout_lines="" 
  stderr_lines=""
  begin
    loop do
      # check whether stdout, stderr or both are 
      #  ready to be read from without blocking 
      IO.select([stdout,stderr]).flatten.compact.each { |io|
        # stdout, if ready, goes to stdout_lines 
        stdout_lines += io.readpartial(1024) if io.fileno == stdout.fileno 
        # stderr, if ready, goes to stdout_lines 
        stderr_lines += io.readpartial(1024) if io.fileno == stderr.fileno 
      }
      break if stdout.closed? && stderr.closed? 
      # if we acumulated any complete lines (\n-terminated) 
      #  in either stdout/err_lines, output them now 
      stdout_lines.sub!(/.*\n/m) { puts $& ; '' } 
      stderr_lines.sub!(/.*\n/m) { puts $& ; '' } 
    end
  rescue EOFError
    puts "Done"
  end 
end 

要同时处理stdin,请更改为:

      IO.select([stdout,stderr],[stdin]).flatten.compact.each { |io|
        # program ready to get stdin? do we have anything for it?
        if io.fileno == stdin.fileno && <got data from client?>
          <write a small chunk from client to stdin>
        end
        # stdout, if ready, goes to stdout_lines 
于 2010-02-28T05:02:05.293 回答
0

您可能会尝试的另一个技巧是在命令中将 stderr 重定向到 stdout,这样您的程序只需读取 stdout。像这样的东西:

popen4("./srcds_run -console -game tf +map ctf_2fort -maxplayers 6 2>&1")

这样做的另一个好处是您可以在程序运行期间按照它们发生的顺序获取所有消息/错误。

于 2010-02-28T06:27:32.553 回答