我认为我根本不正确理解python子进程,但这里有一个简单的例子来说明我对此感到困惑的一点:
#!/usr/bin/env python
import subprocess
lookup_server = subprocess.Popen("nc -l 5050", shell=True)
lookup_client = subprocess.Popen("nc localhost 5050", shell=True, stdin=subprocess.PIPE)
print lookup_client.poll()
lookup_client.stdin.write("magic\n")
print lookup_client.poll()
lookup_client.send_signal(subprocess.signal.SIGINT)
print lookup_client.poll()
lookup_server.wait()
print "Lookup server terminated properly"
输出返回
None
None
None
永远不会完成。为什么是这样?此外,如果我将Popen的第一个参数更改为所有这些参数的数组,则所有nc调用都不会正确执行,并且脚本会在没有等待的情况下运行。为什么会这样?
最终,我遇到了一个更大的程序中的问题,该程序使用netcat和另一个本地运行的程序而不是两个版本的nc来执行类似的操作。无论哪种方式,我都无法正确地写入或读取它们。但是,当我在python控制台中运行它们时,一切都按照我的预期运行。这一切让我非常沮丧。如果您有任何见解,请告诉我们!
编辑:我在Ubuntu Linux 12.04上运行它,当我man nc
时,我得到了BSD通用命令手册,所以我假设这是BSD netcat。
答案 0 :(得分:3)
此处的问题是您正在向流程发送SIGINT
。如果您只是close
stdin
,nc
将关闭其套接字并退出,这就是您想要的。
听起来你真的在实际程序中使用nc
作为客户端(虽然不是服务器),这意味着你有两个简单的修复:
而不是lookup_client.send_signal(subprocess.signal.SIGINT)
,只需lookup_client.stdin.close()
。 nc
会在输入中将此视为EOF,并正常退出,此时您的服务器也将退出。
#!/usr/bin/env python
import subprocess
lookup_server = subprocess.Popen("nc -l 5050", shell=True)
lookup_client = subprocess.Popen("nc localhost 5050", shell=True, stdin=subprocess.PIPE)
print lookup_client.poll()
lookup_client.stdin.write("magic\n")
lookup_client.stdin.close()
print lookup_client.poll()
lookup_server.wait()
print "Lookup server terminated properly"
当我运行它时,最常见的输出是:
None
None
magic
Lookup server terminated properly
有时第二个None
代替0
,和/或它来自magic
而不是之前,但除此之外,它总是全部四行。 (我在OS X上运行。)
对于这个简单的情况(尽管可能不是你的真实情况),只需使用communicate
而不是手动尝试。
#!/usr/bin/env python
import subprocess
lookup_server = subprocess.Popen("nc -l 5050", shell=True)
lookup_client = subprocess.Popen("nc localhost 5050", shell=True, stdin=subprocess.PIPE)
print lookup_client.communicate("magic\n")
lookup_server.wait()
print "Lookup server terminated properly"
同时
此外,如果我将Popen的第一个参数更改为所有这些参数的数组,则没有一个nc调用正确执行,并且脚本会在没有等待的情况下运行。为什么会这样?
正如the docs所说:
在Unix上
shell=True
...如果args是一个序列,第一项指定命令字符串,任何其他项将被视为shell本身的附加参数。
因此,subprocess.Popen(["nc", "-l", "5050"], shell=True)
执行/bin/sh -c 'nc' -l 5050
,sh
不知道如何处理这些参数。
你可能做想要使用args数组,但是你必须摆脱shell=True
- 这无论如何都是个好主意,因为shell没有帮助你这里。
还有一件事:
lookup_client.send_signal(subprocess.signal.SIGINT)
print lookup_client.poll()
这可能会打印-2或无,具体取决于客户端是否已完成对SIGINT
的响应并在poll
之前被杀死。如果你想真正获得-2,你必须调用wait
而不是poll
(或做其他事情,比如循环,直到poll
返回非 - )。
最后,为什么原始代码不起作用?好吧,发送SIGINT
是异步的;它无法保证何时生效。对于可能出错的一个例子,它可能会在客户端打开套接字之前生效,在这种情况下,服务器仍然在等待从未出现的客户端。
你可以在time.sleep(5)
来电之前投入signal
进行测试 - 但显然这不是真正的修复,甚至是可接受的黑客攻击;它只对测试问题有用。你需要做的就是杀死客户端,直到它完成了你想做的一切。对于复杂的情况,你需要建立一些机制来做到这一点(例如,阅读它的标准输出),而对于简单的情况,communicate
已经是你需要的一切(并且没有理由在第一次杀死孩子位)。
答案 1 :(得分:2)
你对nc
的调用很糟糕,如果我在命令行中调用它会发生什么:
# Server window:
[vyktor@grepfruit ~]$ nc -l 5050
# Client Window
[vyktor@grepfruit ~]$ nc localhost 5050
[vyktor@grepfruit ~]$ echo $?
1
这意味着(1
)失败中的$?
。
使用-p
:
-p, --local-port=NUM local port number
NC开始收听,所以:
# Server window
[vyktor@grepfruit ~]$ nc -l -p 5050
# Keeps handing
# Client window
[vyktor@grepfruit ~]$ echo Hi | nc localhost 5050
# Keeps hanging
将-c
添加到客户端调用后:
-c, --close close connection on EOF from stdin
你最终会得到这个:
# Client window
[vyktor@grepfruit ~]$ echo Hi | nc localhost 5050 -c
[vyktor@grepfruit ~]$
# Server window
[vyktor@grepfruit ~]$ nc -l -p 5050
Hi
[vyktor@grepfruit ~]$
所以你需要这段python代码:
#!/usr/bin/env python
import subprocess
lookup_server = subprocess.Popen("nc -l -p 5050", shell=True)
lookup_client = subprocess.Popen("nc -c localhost 5050", shell=True,
stdin=subprocess.PIPE)
lookup_client.stdin.write("magic\n")
lookup_client.stdin.close() # This
lookup_client.send_signal(subprocess.signal.SIGINT) # or this kill
lookup_server.wait()
print "Lookup server terminated properly"