Проблема здесь в том, что вы отправляете 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 не будет выполняться должным образом, и сценарий будет выполняться без ожидания. Почему это происходит?
Как сказано в в документах:
В Unix с shell=True
… Если args является последовательностью, первый элемент определяет командную строку, а любые дополнительные элементы будут рассматриваться как дополнительные аргументы для самой оболочки.
Итак, subprocess.Popen(["nc", "-l", "5050"], shell=True)
делает /bin/sh -c 'nc' -l 5050
, а sh
не знает, что делать с этими аргументами.
Вероятно, вы делаете использование массива аргументов, но тогда вам придется избавиться от shell=True
, что в любом случае является хорошей идеей, потому что оболочка здесь вам не поможет.
Еще кое-что:
lookup_client.send_signal(subprocess.signal.SIGINT)
print lookup_client.poll()
Это может вывести либо -2, либо None, в зависимости от того, закончил ли клиент отвечать на SIGINT
и был ли он убит до того, как вы его poll
. Если вы действительно хотите получить это -2, вам нужно вызвать wait
, а не poll
(или сделать что-то еще, например выполнить цикл до тех пор, пока poll
не вернет значение, отличное от None).
Наконец, почему ваш исходный код не работал? Ну, отправка SIGINT
асинхронна; нет никакой гарантии относительно того, когда это может вступить в силу. В качестве одного из примеров того, что может пойти не так, это может произойти еще до того, как клиент откроет сокет, и в этом случае сервер все еще сидит в ожидании клиента, который никогда не появляется.
Вы можете добавить time.sleep(5)
перед вызовом signal
, чтобы проверить это, но очевидно, что это не настоящее исправление или даже приемлемый хак; это полезно только для тестирования проблемы. Что вам нужно сделать, так это не убивать клиента, пока он не сделает все, что вы от него хотите. Для сложных случаев вам нужно будет создать какой-то механизм для этого (например, чтение его stdout), в то время как для простых случаев communicate
уже все, что вам нужно (и в первую очередь нет причин убивать ребенка).
person
abarnert
schedule
20.11.2012
shell=True
при использовании массива аргументов вместо строки? А вы на какой платформе? В Unix с shell=True, если args является последовательностью, первый элемент определяет командную строку, а любые дополнительные элементы будут рассматриваться как дополнительные аргументы для самой оболочки. - person abarnert   schedule 21.11.2012lookup_client
одной строкой:lookup_client.communicate("magic\n")
. Это отправит строку в ееstdin
, отправит EOF, а затем дождется ее выхода, что именно то, что вы хотите. Это может быть неприменимо для вашего реального варианта использования. - person abarnert   schedule 21.11.2012write
иclose
, когда закончите. Теоретически это не совсем безопасно, потому что буфер stdin клиента может заблокироваться, так что вам нужно делать все те причудливые вещи, которыеcommunicate
делает под капотом; на практике я не думаю, что это будет проблемой сnc
. - person abarnert   schedule 21.11.2012