为什么用Popen启动的一个Python脚本会导致两个脚本的socket连接中断?

2 投票
1 回答
1847 浏览
提问于 2025-04-18 07:59

我有两个非常简单的Python脚本,它们通过套接字进行通信。目前这两个脚本都在同一台Windows电脑上运行。

这是controller.py的内容:

import socket
import time
import sys
from subprocess import Popen, CREATE_NEW_CONSOLE

HOST = '192.168.1.107'          # The remote host
PORT = 50557                        # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    s.connect((HOST, PORT))
except:
    Popen([sys.executable, 'driver.py'], creationflags=CREATE_NEW_CONSOLE)
    time.sleep(0.2);
    s.connect((HOST, PORT))

s.send(sys.argv[1])
data = s.recv(1024)

s.close()
print 'Received', repr(data)

这是driver.py的内容:

import socket

HOST = ''                                # Symbolic name meaning the local host
PORT = 50557                    # Arbitrary non-privileged port

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)

while 1:
    print 'Waiting for controller connection.'
    conn, addr = s.accept()
    print 'Connected to ', addr
    while 1:
        data = conn.recv(1024)
        print 'Recieved ', data
        if not data: break
        if data == 'Q': quit()
        print 'A.'      
        conn.send(data[::-1])
        print 'B.'
    print 'C.'

conn.close()        

如果我打开两个cmd窗口,并分别运行python <文件名>.py <参数>,一切都能正常工作。我可以让driver.py一直运行,然后反复运行controller.py,直到我通过发送“Q”来结束driver。

在代码中,try/except语句会在连接无法建立时打开一个新窗口并运行driver.py。理论上,这样做是为了确保接收方在发送任何数据之前已经在运行。这个方法几乎能奏效,但不知为什么,driver.py在第二个while循环中卡住了,我也说不清楚原因。以下是连续调用controller.py的输出:

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

>python controller.py FirstMsg
Received 'gsMtsriF'

>python controller.py SecondMsg
Received 'gsMdnoceS'

>python controller.py Q
Received ''

>python controller.py ThirdMsg
Received 'gsMdrihT'

>python controller.py FourthMsg

(在第四条消息后,controller.py会一直卡住)

这是driver.py的输出:

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

>python driver.py
Waiting for controller connection.
Connected to  ('192.168.1.107', 49915)
Recieved  FirstMsg
A.
B.
Recieved
C.
Waiting for controller connection.
Connected to  ('192.168.1.107', 49916)
Recieved  SecondMsg
A.
B.
Recieved
C.
Waiting for controller connection.
Connected to  ('192.168.1.107', 49917)
Recieved  Q

(然后它会返回到正常的命令提示符。) 新创建的窗口中显示的是: 等待控制器连接。 已连接到 ('192.168.1.107', 49918) 收到 第三条消息 A. B.

所以在这个奇怪的新窗口中(我刚注意到它看起来和行为都不像普通的cmd窗口),程序似乎在data = conn.recv(1024)这行卡住了。

为什么driver.py在新窗口中表现得不同,我该如何解决这个问题呢?

1 个回答

2

你提到了套接字和Popen,但我会忽略这些,直接给你提供一个解决方案,适合你展示的用例——在网络上进行远程通信。

使用套接字时,很容易遇到无尽的问题。如果你能控制两个端点,使用zeromq进行通信会让你的编程变得轻松有趣。

你可以直接使用zeromq(如果你查看pyzmq的例子,会发现它们非常简短易懂,并且在很多复杂场景中表现良好),但今天我会展示如何使用zerorpc库,这样远程调用会更简单。

安装zerorpc

$ pip install zerorpc

编写你的worker.py

def reverse(text):
    """ return reversed argument """
    return text[::-1]

def makebig(text):
    """ turn text to uppercase letters """
    return text.upper()

从命令行调用worker的方法

启动服务器,提供worker.py的功能

$ zerorpc --server --bind tcp://*:5555 worker
binding to "tcp://*:5555"
serving "worker"

从命令行使用这些服务

简单的调用,报告可以调用的功能

$ zerorpc tcp://localhost:5555
connecting to "tcp://localhost:5555"
makebig turn text to uppercase letters 
reverse return reversed argument 

然后调用这些功能:

$ zerorpc tcp://localhost:5555 reverse "alfabeta"
connecting to "tcp://localhost:5555"
'atebafla'

$ zerorpc tcp://localhost:5555 makebig "alfabeta"
connecting to "tcp://localhost:5555"
'ALFABETA'

注意:到目前为止,我们只写了7行代码,就已经可以远程调用了。

从Python代码中使用

命令行工具zerorpc只是一个方便的工具,但你也可以选择不使用它,直接用纯Python来集成。

从Python代码中调用client.py

import zerorpc

client = zerorpc.Client()
url = "tcp://localhost:5555"
client.connect(url)

print client.reverse("alfabeta")
print client.makebig("alfabeta")

然后运行它

$ python client.py
atebafla
ALFABETA

从Python代码中运行远程服务器server.py

import zerorpc
import worker

url = "tcp://*:5555"

srv = zerorpc.Server(worker)
srv.bind(url)
srv.run()

如果之前用zerorpc启动的旧服务器还在运行,先把它停掉。

然后启动我们的Python版本的服务器:

$ python server.py

(它不会打印任何东西,只是等待提供服务)。

最后,从你的Python代码中调用它

$ python client.py 
atebafla
ALFABETA

总结

  • 使用zeromq或zerorpc大大简化了情况
  • 尝试以不同的顺序启动client.py和server.py
  • 试着运行多个客户端
  • 尝试从另一台计算机(通过网络)调用服务
  • zerorpc提供了更多的模式(流、发布/订阅),可以查看zerorpc测试套件
  • zerorpc解决了集成问题,让你可以专注于编写实际的功能

撰写回答