Python:绑定套接字:“地址已在使用”
我有一个关于TCP/IP网络中客户端套接字的问题。假设我使用了
try:
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error, msg:
sys.stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(1)
try:
comSocket.bind(('', 5555))
comSocket.connect()
except socket.error, msg:
sys.stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(2)
这样创建的套接字会绑定到5555端口。问题是,在结束连接后
comSocket.shutdown(1)
comSocket.close()
我用wireshark查看,发现套接字是通过FIN、ACK和双方的ACK关闭的,但我无法再次使用这个端口。我收到以下错误信息:
[ERROR] Address already in use
我想知道如何能立即清理这个端口,以便下次还能使用同样的端口。
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
setsockopt似乎无法解决这个问题。谢谢!
14 个回答
根据这个链接
其实,SO_REUSEADDR这个标志可能会带来更大的影响: SO_REUSEADDR允许你使用一个处于TIME_WAIT状态的端口,但你仍然不能用这个端口去连接到它最后连接的地方。什么意思呢?假设我选择本地端口1010,连接到foobar.com的300端口,然后在本地关闭,导致这个端口进入TIME_WAIT状态。我可以马上重新使用本地端口1010去连接其他地方,但不能连接foobar.com的300端口。
不过,你可以通过确保远程端先发起关闭(关闭事件)来完全避免TIME_WAIT状态。所以服务器可以通过让客户端先关闭来避免问题。应用协议必须设计得让客户端知道什么时候可以关闭。服务器可以在收到客户端的EOF(文件结束)时安全地关闭,但它也需要设置一个超时,以防客户端不正常断开网络。在很多情况下,服务器在关闭之前等几秒钟就足够了。
我还建议你多了解一下网络和网络编程。你至少应该知道TCP协议是怎么工作的。这个协议其实很简单,了解它可以为你将来节省很多时间。
使用netstat
命令,你可以轻松查看哪些程序((程序名, pid)元组)绑定了哪些端口,以及当前的套接字状态:TIME_WAIT、CLOSING、FIN_WAIT等等。
关于Linux网络配置的一个非常好的解释可以在这里找到。
这是我测试过的完整代码,绝对不会出现“地址已被使用”的错误。你可以把它保存到一个文件里,然后在你想要提供服务的HTML文件的根目录下运行这个文件。此外,你还可以在启动服务器之前,通过程序的方式切换目录。
import socket
import SimpleHTTPServer
import SocketServer
# import os # uncomment if you want to change directories within the program
PORT = 8000
# Absolutely essential! This ensures that socket resuse is setup BEFORE
# it is bound. Will avoid the TIME_WAIT issue
class MyTCPServer(SocketServer.TCPServer):
def server_bind(self):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = MyTCPServer(("", PORT), Handler)
# os.chdir("/My/Webpages/Live/here.html")
httpd.serve_forever()
# httpd.shutdown() # If you want to programmatically shut off the server
在绑定套接字之前,试着使用 SO_REUSEADDR
这个选项。
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
补充:我看到你还是遇到问题了。其实有一种情况是 SO_REUSEADDR
不会起作用的。如果你尝试绑定一个套接字,并且重新连接到同一个目标(即使启用了 SO_REUSEADDR
),那么 TIME_WAIT
状态仍然会存在。不过,你可以连接到不同的主机和端口。
有几个解决办法可以考虑。你可以继续尝试,直到能够重新连接。或者,如果是客户端主动关闭了套接字(而不是服务器),那么这时候应该就能顺利连接了。