在 Windows 7 上:
鉴于此服务器代码:
# in server.py
if __name__ == '__main__':
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# uncommenting this won't help
#serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversock.bind(('',8888))
serversock.listen(5)
# accept and receive dummy data from client
clientsock,address = serversock.accept()
data = clientsock.recv(1024)
# as long as calc.exe is running, I can't do this again
subprocess.Popen(r"c:\windows\system32\calc.exe")
# letting client close first still won't help
time.sleep(3)
# closing won't help either
clientsock.close()
serversock.close()
和客户端代码
# in client.py
if __name__ == '__main__':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 8888))
s.sendall('Hello, world')
# close early to help prevent TIME_WAIT on server, but doesn't help
s.close()
首先运行服务器,然后客户端将启动计算器应用程序。
虽然计算器应用程序仍在运行,但我无法再次运行服务器。它会抱怨
python server.py (ok)
python client.py (ok)
python server.py (boom!)
socket.error: [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted
如果我关闭计算器应用程序,运行服务器再次正常... 这不会发生在 Mac 上。
启用 SO_REUSEADDR 只会使错误消失,但客户端无法访问服务器。
在上面的例子中,我特意让客户端先关闭,这样服务器套接字就不会进入TIME_WAIT。
所以问题:
- 我是否遇到了服务器上的 TIME_WAIT 问题?
- 服务器中是否有未关闭的套接字/文件描述符?
- 为什么 SO_REUSEADDR 在这种情况下无济于事?客户端可以来自同一个端口吗?
- 子进程可以挂在某些描述符上吗?
- 我能做些什么呢?
解决方案:
问题在于计算器的父进程持有一些文件描述符。因此,将 close_fds=True 添加到 Popen 将确保所有内容都正确发布。
subprocess.Popen(r"c:\windows\system32\calc.exe", close_fds=True)