Python:绑定套接字:“地址已在使用”

109 投票
14 回答
308407 浏览
提问于 2025-04-16 19:44

我有一个关于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 个回答

24

根据这个链接

其实,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网络配置的一个非常好的解释可以在这里找到

37

这是我测试过的完整代码,绝对不会出现“地址已被使用”的错误。你可以把它保存到一个文件里,然后在你想要提供服务的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
152

在绑定套接字之前,试着使用 SO_REUSEADDR 这个选项。

comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

补充:我看到你还是遇到问题了。其实有一种情况是 SO_REUSEADDR 不会起作用的。如果你尝试绑定一个套接字,并且重新连接到同一个目标(即使启用了 SO_REUSEADDR),那么 TIME_WAIT 状态仍然会存在。不过,你可以连接到不同的主机和端口。

有几个解决办法可以考虑。你可以继续尝试,直到能够重新连接。或者,如果是客户端主动关闭了套接字(而不是服务器),那么这时候应该就能顺利连接了。

撰写回答