Python:与始终运行的子进程通信

时间:2015-09-22 15:21:39

标签: python python-2.7

我有以下情况。我有一个游戏和两个机器人,他们互相玩这个游戏。所有的通信都是通过stdin和stdout完成的。现在我想写一个引擎,让机器人互相对抗并决定哪个机器人赢了比赛。要做到这一点,我需要从我的引擎运行机器人并通过stdin和stdout与它们进行通信。

因此,为了启动机器人,我已经完成了以下工作:

from subprocess import Popen, PIPE, STDOUT
bot = Popen(['/path/to/bot1'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)

现在我想将游戏状态传输到机器人并请求从我的机器人移动。首先,我想在此过程中编写一些文本行,然后我的引擎应该等待一段时间并从过程中读取结果。请注意,僵尸程序始终在运行,并且不会自行终止。

如何实现这一目标?

2 个答案:

答案 0 :(得分:3)

不幸的是,它可能不如从bot.stdout阅读并写入bot.stdin那么容易。原因是,如果程序执行完毕,某些程序不会刷新到STDOUT,如果它是子进程。解决这个问题的方法是在伪终端下生成它。以下是使用pty模块的示例:

<强> bot1.py

#!/usr/bin/env python

name = raw_input("What is your name?\n")
print "Hello {}, my name is Bot1".format(name)
raw_input() # Bot2: Hello Bot1

<强> bot2.py

#!/usr/bin/env python

raw_input() # Bot1: What is your name?
print "Bot2"
greeting = raw_input() # Bot1: Hello Bot2, my name is Bot1
print "Hello {}".format(greeting.split()[-1])

<强> run.py

#!/usr/bin/env python
import os
import pty
import termios

class Bot:

  def __init__(self, name, program):
    self.name = name

    # Fork a process in a pseudo-terminal
    self._pid, self._fd = pty.fork()

    if self._pid == 0:
      # Child process, replace with program
      os.execvp(program, [program])

    # Turn off echoing for child file descriptor, otherwise it will spew the
    # child's output to the parent's STDOUT.
    attribs = termios.tcgetattr(self._fd)
    attribs[3] = attribs[3] & ~termios.ECHO
    termios.tcsetattr(self._fd, termios.TCSANOW, attribs)

  def speak(self, bot):
    output = self.read()
    bot.write(output)

    print '{} -> {}: {}'.format(self.name, bot.name, output)

  def read(self):
    return os.read(self._fd, 1024).strip()

  def write(self, line):
    os.write(self._fd, line + "\n")

if __name__ == '__main__':

  bot1 = Bot('Bot1', './bot1.py')
  bot2 = Bot('Bot2', './bot2.py')

  # Bot1 -> Bot2: What is your name?
  bot1.speak(bot2)

  # Bot2 -> Bot1: Bot2
  bot2.speak(bot1)

  # Bot1 -> Bot2: Hello Bot2, my name is Bot1
  bot1.speak(bot2)

  # Bot2 -> Bot1: Hello Bot1
  bot2.speak(bot1)

<强>输出

$ ./run.py 
Bot1 -> Bot2: What is your name?
Bot2 -> Bot1: Bot2
Bot1 -> Bot2: Hello Bot2, my name is Bot1
Bot2 -> Bot1: Hello Bot1

这是一个简单的例子,但希望它足以让你入门。

答案 1 :(得分:2)

由于您使用PIPE,因此Popen对象bot包含{stdinstdoutstderr}三个属性{{ 3}}

因此,您可以使用bot.stdout.read()bot.stdin.write()。 正如Aldehir所提到的,你可能必须flush你写的流,以便bot实际收到一些东西。

文档建议使用communicate(在这种情况下完全不适合)的原因是,如果子进程或者您没有快速/经常读取数据,OS管道缓冲区可能会填满,导致陷入僵局。

除非你高速发送大量数据,否则这不会是一个问题。