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

Posted

技术标签:

【中文标题】Python:绑定套接字:“地址已在使用中”【英文标题】:Python: Binding Socket: "Address already in use" 【发布时间】:2011-09-16 19:51:39 【问题描述】:

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

创建的socket会绑定5555端口,问题是连接结束后

comSocket.shutdown(1)
comSocket.close()

使用wireshark,我看到socket被双方的FIN、ACK和ACK关闭了,我不能再使用这个端口了。我收到以下错误:

[ERROR] Address already in use

我想知道如何立即清除端口,以便下次我仍然可以使用同一个端口。

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

setsockopt 似乎无法解决问题 谢谢!

【问题讨论】:

为什么客户端需要特定的端口? 因为我必须把它放到生产服务器中,并且在该服务器中,所有传出连接都被阻止。我需要为套接字指定一个特定端口,以便他们可以在防火墙上设置允许连接通过的规则。 您的网络管理员应该明白,出站流量可以由destination端口控制。 这有足够的信息。问题有 99% 的可能性是由TIME_WAIT 套接字状态引起的,下面的答案有一个解决方案:) 你的操作系统是什么?你通常可以使用netstat查看套接字的状态(查找端口号以识别套接字) 【参考方案1】:

在绑定套接字之前尝试使用SO_REUSEADDR 套接字选项。

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

编辑: 我看到你仍然有这个问题。有一种情况,SO_REUSEADDR 不起作用。如果您尝试绑定套接字并重新连接到同一个目标(启用SO_REUSEADDR),那么TIME_WAIT 仍然有效。但是,它将允许您连接到不同的主机:端口。

我想到了几个解决方案。您可以继续重试,直到您可以再次获得连接。或者如果客户端启动关闭套接字(而不是服务器),那么它应该会神奇地工作。

【讨论】:

还是不能重复使用。在能够重用同一个端口之前我必须等待的时间是 1 分 30 秒 :( 你在bind之前打电话给setsockopt了吗?第一个套接字是用SO_REUSEADDR 创建的,还是只是失败的?等待套接字必须设置 SO_REUSEADDR 才能正常工作 tcp 0 0 98c5-9-134-71-1:freeciv mobile-166-132-02:2345 TIME_WAIT @jcoffland 我同意他不应该使用bind(),但 SO_REUSEADDR 适用于所有套接字,不仅包括 TCP 服务器套接字,还包括 TCP 客户端套接字和 UDP 套接字。不要在这里发布错误信息。 @jcoffland 这是不正确的。在传出套接字上调用 bind 对于端口到端口的交互是合法的 - 许多知名服务都这样做。但确实存在重用端口 uf 通信没有正确关闭的问题【参考方案2】:

这是我测试过的完整代码,绝对不会给我“地址已在使用”错误。您可以将其保存在一个文件中,然后从您要提供的 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

【讨论】:

即使在 httpd.shutdown() 之后你仍然想调用 httpd.server_close() 来完全释放所有资源。 另外,考虑使用 atexit 模块来处理任何意外的存在。【参考方案3】:

根据this link

实际上,SO_REUSEADDR 标志会导致更大的后果: SO_REUSADDR 允许您使用卡在 TIME_WAIT 中的端口,但是 您仍然无法使用该端口建立到最后的连接 把它连接到。什么?假设我选择本地端口 1010,并且 连接到 foobar.com 端口 300,然后在本地关闭,留下 TIME_WAIT 中的端口。我可以立即重用本地端口 1010 连接 到 foobar.com 端口 300 以外的任何地方。

但是,您可以通过确保远程端启动关闭(关闭事件)来完全避免 TIME_WAIT 状态。所以服务器可以通过让客户端先关闭来避免问题。必须设计应用程序协议,以便客户端知道何时关闭。服务器可以安全地关闭以响应来自客户端的 EOF,但是当客户端不正常地离开网络时,它还需要在期望 EOF 时设置超时。在许多情况下,只需在服务器关闭前等待几秒钟就足够了。

我还建议您了解有关网络和网络编程的更多信息。你现在至少应该知道 tcp 协议是如何工作的。该协议非常简单和小,因此将来可能会为您节省大量时间。

使用netstat 命令,您可以轻松查看哪些程序((program_name,pid) tuple)绑定到哪些端口以及套接字当前状态:TIME_WAIT、CLOSING、FIN_WAIT 等。

可以在https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait 找到关于 linux 网络配置的非常好的解释。

【讨论】:

另外,你应该小心你的代码。如果您的代码仍在开发中并且发生了一些异常,则可能无法正确关闭连接,尤其是从服务器端。 你应该公平地引用你从Tom's网络指南复制和粘贴的句子。请更正您的答案。【参考方案4】:

如果您使用TCPServerSimpleHTTPServer 遇到问题, 覆盖SocketServer.TCPServer.allow_reuse_address (python 2.7.x) 或socketserver.TCPServer.allow_reuse_address (python 3.x) 属性

class MyServer(SocketServer.TCPServer):
    allow_reuse_address = True

server = MyServer((HOST, PORT), MyHandler)
server.serve_forever()

【讨论】:

【参考方案5】:

绑定前需要设置allow_reuse_address。代替 SimpleHTTPServer 运行这个 sn-p:

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler, bind_and_activate=False)
httpd.allow_reuse_address = True
httpd.server_bind()
httpd.server_activate()
httpd.serve_forever()

这可以防止服务器在我们有机会设置标志之前绑定。

【讨论】:

子类化 TCPServer 并覆盖属性似乎要容易得多,例如参见 my answer【参考方案6】:

正如 Felipe Cruze 所说,您必须在绑定之前设置 SO_REUSEADDR。我在另一个网站上找到了解决方案 - solution on other site, reproduced below

问题是必须先设置 SO_REUSEADDR 套接字选项 地址绑定到套接字。这可以通过子类化来完成 ThreadingTCPServer 并重写 server_bind 方法如下:

导入 SocketServer, 套接字 MyThreadingTCPServer 类(SocketServer.ThreadingTCPServer): def server_bind(self): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address)

【讨论】:

【参考方案7】:

我发现了这个异常的另一个原因。 当从 Spyder IDE 运行应用程序(在我的情况下是 Raspbian 上的 Spyder3)并且程序被 ^C 或异常终止时,套接字仍然处于活动状态:

sudo netstat -ap | grep 31416
tcp  0  0 0.0.0.0:31416  0.0.0.0:*    LISTEN      13210/python3

再次运行程序发现“地址已在使用”; IDE 似乎将新的“运行”作为一个单独的进程启动,该进程会找到前一个“运行”使用的套接字。

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

没有帮助。

杀死进程 13210 有所帮助。 从命令行启动 python 脚本,如

python3 <app-name>.py

当 SO_REUSEADDR 设置为 true 时,总是运行良好。新的 Thonny IDE 或 Idle3 IDE 没有这个问题。

【讨论】:

【参考方案8】:

socket.socket() 应该在socket.bind() 之前运行并使用REUSEADDR 如上所述

【讨论】:

他更改了问题的内容:***.com/revisions/6380057/4 - 下次之前检查修订:)【参考方案9】:

我知道您已经接受了答案,但我相信问题与在客户端套接字上调用 bind() 有关。这可能没问题,但 bind() 和 shutdown() 似乎不能很好地配合使用。此外,SO_REUSEADDR 通常与侦听套接字一起使用。即在服务器端。

您应该将 ip/port 传递给 connect()。像这样:

comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.connect(('', 5555))

不要调用 bind(),不要设置 SO_REUSEADDR。

【讨论】:

bind()shutdown() 完全没有任何关系,“他们似乎不能很好地配合”的说法是毫无根据的。你错过了他需要使用本地端口 5555 的部分。【参考方案10】:

对我来说,更好的解决方案如下。由于关闭连接的主动权是由服务器完成的,setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 没有任何效果,TIME_WAIT 正在避免在同一端口上建立新连接,但出现错误:

[Errno 10048]: Address already in use. Only one usage of each socket address (protocol/IP address/port) is normally permitted 

我最后采用的方案是让操作系统自己选择端口,如果先例还在TIME_WAIT,则使用另一个端口。

我换了:

self._socket.bind((guest, port))

与:

self._socket.bind((guest, 0))

正如 tcp 地址的python socket documentation 中所示:

如果提供,source_address 必须是 2 元组(主机、端口),以便套接字在连接之前绑定到其源地址。如果主机或端口分别为“”或 0,则将使用操作系统默认行为。

【讨论】:

你也可以省略绑定,但是 OP 声明他必须使用端口 5555,而这个答案没有。【参考方案11】:

另一种解决方案,当然是在开发环境中,例如杀死使用它的进程

def serve():
    server = HTTPServer(('', PORT_NUMBER), BaseHTTPRequestHandler)
    print 'Started httpserver on port ' , PORT_NUMBER
    server.serve_forever()
try:
    serve()
except Exception, e:
    print "probably port is used. killing processes using given port %d, %s"%(PORT_NUMBER,e)
    os.system("xterm -e 'sudo fuser -kuv %d/tcp'" % PORT_NUMBER)
    serve()
    raise e

【讨论】:

杀死进程不会以任何方式影响 TIME_WAIT。【参考方案12】:

我认为最好的方法就是终止该端口上的进程,方法是在终端输入fuser -k [PORT NUMBER]/tcp,例如fuser -k 5001/tcp.

【讨论】:

除非进程已经被杀死,并且它只是让端口打开以被操作系统清理。 杀死进程不会以任何方式影响 TIME_WAIT。【参考方案13】:

我遇到了同样的问题,除了每次重新启动 Raspberry Pi 外,我找不到任何其他解决方案(重用选项不起作用)。然后我找到了解决方法;

comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.close()
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.connect(('', 5555))

也就是说,先定义socket,然后关闭,再定义,这样卡住了就可以使用同一个端口了。

【讨论】:

这并不能真正回答问题。如果您有其他问题,可以点击 进行提问。要在此问题有新答案时收到通知,您可以follow this question。一旦你有足够的reputation,你也可以add a bounty 来引起对这个问题的更多关注。 - From Review

以上是关于Python:绑定套接字:“地址已在使用中”的主要内容,如果未能解决你的问题,请参考以下文章

绑定失败:地址已在使用中

Go Unix Domain Socket:绑定地址已在使用中

java.net.BindException:绑定失败:EADDRINUSE(地址已在使用中)

Python [Errno 98] 地址已在使用中

PHP:TCP 套接字,“无法绑定地址”

Errno 98(地址已在使用中)Python Opencv