如何启动一个连接到你监听的套接字的进程?

2 投票
3 回答
3258 浏览
提问于 2025-04-16 15:56

我有一个进程是通过 subprocess.Popen() 启动的,它试图连接到一个我的代码也在监听的套接字。问题是,如果我的代码先开始监听这个套接字,就无法启动子进程。因为它被 sock.accept() 阻塞了,当 sock.accept() 超时后,显然在运行 subprocess.Popen() 时并没有在监听。如果我的代码先启动子进程,它会尝试连接套接字,但在任何代码能够监听之前就失败了。

那么,有什么想法可以解决这个问题吗?看起来我需要以非阻塞的方式开始监听,然后再启动进程,但我有点困惑,因为即使我使用 select() 来处理队列,最终还是会调用 sock.accept(),从而阻塞代码……我想是这样。

无论如何,任何方向上的建议都非常有用!我不太想这样做,但如果能让事情更简单,我也不反对使用 Twisted。

编辑 1:我会尝试找一些代码出来,我得查看我以前的提交记录来找到一个可用的版本。基本上,我觉得我的代码不是问题所在。我认为我只是实现得不对。

例如,如果我启动我的套接字监听器,然后在命令行中手动启动这个 subprocess.Popen() 进程,它连接得很好。这是因为命令行已经在监听。我 相信 我的问题只是一个“鸡和蛋”的问题。在我的代码中,如果我先启动进程,它会立即失败,因为还没有套接字服务器在监听 。然而,如果我先启动套接字服务器,它会因为阻塞而超时,在完成阻塞之前不会启动任何子进程。我认为我的解决方案在于非阻塞代码,但我对如何正确实现这一点非常不熟悉。我看到很多提到 select() 的地方,但它们 看起来 会在同一点阻塞,也就是 sock.accept()。我说“看起来”是因为我还没有实现 select() 版本。如果我错了,请告诉我。

编辑 2:这是代码中的套接字部分……请注意,这目前设置为非阻塞……

 90         # Create our socket stream to listen on.
 91         serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 92
 93         #serv.settimeout(5)
 94         serv.setblocking(0)
 95
 96         # Bind the address.
 97         serv.bind(('', self.PORT))
 98         serv.listen(5)
 99
100         try:
101             # Now start listening for a connection!
102             (self._sock, remote_address) = serv.accept()
103         except socket.timeout:
104             logger.debug('Socket connection failed!')
105             raise DBGPServerNotFoundError(
106                 'No connection was established coming from '
107                 '"%(address)s:%(port)i".' % {
108                     'address':self.ADDRESS,
109                     'port':self.PORT,
110                 })
111         else:
112             logger.debug('Socket connection established! The other end of '
113                          'the connection is at "%s:%i".' % remote_address)
114         finally:
115             serv.close()

这是错误信息……

  File "/home/lee/projects/vim-debug/repo/vimbug/dbgp.py", line 100, in connect
    (self._sock, remote_address) = serv.accept()
  File "/usr/lib/python2.6/socket.py", line 197, in accept
    sock, addr = self._sock.accept()
error: [Errno 11] Resource temporarily unavailable

启动子进程的代码在不同的模块中(具体来说是单元测试),但这里也列出来以防万一。请注意 con 是一个容器对象,con.connect() 是上面代码的函数。

 56     con.connect()
 57     pydbgp_proc = subprocess.Popen(
 58         ('pydbgp.py', '-d', 'localhost:%i' % OPTIONS['pydbgp_port'],
 59         OPTIONS['debug_file']),
 60         stdout=subprocess.PIPE,
 61         stderr=subprocess.PIPE,)

编辑 3:稍微重写了一下代码,尝试在调用 sock.accept() 之前连接到套接字。看看是否会失败 :)

编辑 4:好的,稍微重写了一下代码……还是出现同样的错误。有什么想法?(另外……这些编辑内容越来越多了……在 StackOverflow 上有没有什么推荐的方式来进行这些大更新/编辑?)

代码:

 77     def listen(self):
 78         # Create our socket stream to listen on.
 79         serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 80
 81         #serv.settimeout(5)
 82         serv.setblocking(0)
 83
 84         # Bind the address.
 85         serv.bind(('', self.PORT))
 86         serv.listen(5)
 87         self.serv = serv
 88
 89     def accept(self):
 90         (newsock, newaddr) = self.serv.accept()

调用代码:

 57     con.listen()
 58     pydbgp_proc = subprocess.Popen(
 59         ('pydbgp.py', '-d', 'localhost:%i' % OPTIONS['pydbgp_port'],
 60         OPTIONS['debug_file']),
 61         stdout=subprocess.PIPE,
 62         stderr=subprocess.PIPE,)
 63     con.accept()

错误:

       File "/home/lee/projects/vim-debug/repo/vimbug/dbgp.py", line 90, in accept
    (newsock, newaddr) = self.serv.accept()
  File "/usr/lib/python2.6/socket.py", line 197, in accept
    sock, addr = self._sock.accept()
error: [Errno 11] Resource temporarily unavailable

有什么想法?

编辑 5:我把 accept 函数改成了以下 select() 的实现,结果打印出了 'Not ready..?'

 89     def accept(self):
 90         rfds, wfds, xfds = select.select([self.serv], [], [], 1)
 91
 92         if self.serv in rfds:
 93             print 'Read ready..?'
 94             (newsock, newaddr) = self.serv.accept()
 95         else:
 96             print 'Not ready..?'

3 个回答

0

有几种方法可以解决这个问题。最简单的方法就是让子进程在连接之前等几秒钟。

不过不太明白你为什么要这么做,你能告诉我们你的目标是什么吗?

1

Doug Hellman的Python每周模块是一个很好的地方,可以找到关于模块基本用法的介绍,比如subprocess模块。

如果这些都不行的话,提供一些出问题的代码会帮助我们更好地回答你的问题。

1

你在监听代码中有没有设置 socket.setblocking(0) 呢?

在你启动监听服务器之后,你应该可以通过 select() 函数来查看状态……这是一个在 Debian Lenny 和 Python 2.5 下运行良好的例子……

import socket
import select

SERVER_SOCKADDR = ("", 424242)

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)      # <------------------
server.bind(SERVER_SOCKADDR)
server.listen(5)

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setblocking(0)

result = client.connect_ex(SERVER_SOCKADDR)

rfds, wfds, xfds = select.select([server], [client], [], 1)
if server in rfds:
    print "Server socket: accept does not block"
    sockfd, addr = server.accept()    # sockfd.send() and sockfd.recv() to 
                                      # write and read the stream...
    sockfd.setblocking(0)
    print sockfd, addr
else:
    print "Server socket: accept blocks"
if client in wfds:
    print "Client socket: write does not block"
else:
    print "Client socket: write blocks"


server.close()
client.close()

然后我运行这个……

[mpenning@Bucksnort ~]$ python socket_test.py
Server socket: accept does not block
<socket._socketobject object at 0xb75764c4> ('127.0.0.1', 35810)
Client socket: write does not block
[mpenning@Bucksnort ~]$

撰写回答