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】:如果您使用TCPServer
或SimpleHTTPServer
遇到问题,
覆盖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
导入 SocketServer, 套接字 MyThreadingTCPServer 类(SocketServer.ThreadingTCPServer): def server_bind(self): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address)问题是必须先设置 SO_REUSEADDR 套接字选项 地址绑定到套接字。这可以通过子类化来完成 ThreadingTCPServer 并重写 server_bind 方法如下:
【讨论】:
【参考方案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:绑定地址已在使用中