如何启动一个连接到你监听的套接字的进程?
我有一个进程是通过 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 个回答
有几种方法可以解决这个问题。最简单的方法就是让子进程在连接之前等几秒钟。
不过不太明白你为什么要这么做,你能告诉我们你的目标是什么吗?
Doug Hellman的Python每周模块是一个很好的地方,可以找到关于模块基本用法的介绍,比如subprocess模块。
如果这些都不行的话,提供一些出问题的代码会帮助我们更好地回答你的问题。
你在监听代码中有没有设置 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 ~]$