在 Python 中绑定套接字时出现“地址已被使用”错误
- 2025-02-20 09:24:00
- admin 原创
- 26
问题描述:
我有一个关于 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
" % msg[1])
sys.exit(1)
try:
comSocket.bind(('', 5555))
comSocket.connect()
except socket.error, msg:
sys.stderr.write("[ERROR] %s
" % 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 似乎无法解决问题谢谢!
解决方案 1:
SO_REUSEADDR
在绑定套接字之前尝试使用套接字选项。
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
编辑:
我看你还是遇到了这个问题。有种情况是SO_REUSEADDR
行不通的。如果你尝试绑定套接字并重新连接到同一目标(SO_REUSEADDR
启用),那么TIME_WAIT
仍然有效。但是它将允许你连接到不同的主机:端口。
我想到了一些解决方案。您可以继续重试,直到再次获得连接。或者,如果客户端(而不是服务器)发起套接字的关闭,那么它应该会奇迹般地起作用。
解决方案 2:
这是我测试过的完整代码,绝对不会出现“地址已被使用”错误。您可以将其保存在文件中,并从要提供的 HTML 文件的基本目录中运行该文件。此外,您可以在启动服务器之前以编程方式更改目录
import socket
import SimpleHTTPServer
import socketserver # If using Python 3.0 or higher
# import SocketServer # If using Python 2.7,
# 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
解决方案 3:
根据此链接
实际上,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)元组)绑定到哪些端口以及套接字当前状态是什么:TIME_WAIT,CLOSING,FIN_WAIT等等。
关于 Linux 网络配置的一个非常好的解释可以在 https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait找到。
解决方案 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,而是运行此代码片段:
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()
这会阻止服务器在我们有机会设置标志之前进行绑定。
解决方案 6:
正如 Felipe Cruze 提到的,您必须在绑定之前设置 SO_REUSEADDR。我在另一个网站上找到了解决方案 -另一个网站上的解决方案,如下所示
问题在于,在将地址绑定到套接字之前,必须设置 SO_REUSEADDR 套接字选项。这可以通过子类化 ThreadingTCPServer 并重写 server_bind 方法来实现,如下所示:
导入 SocketServer,套接字
MyThreadingTCPServer类(SocketServer.ThreadingTCPServer):
def server_bind(自身):
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
如上所述
解决方案 9:
我知道您已经接受了答案,但我认为问题与在客户端套接字上调用 bind() 有关。这可能没问题,但 bind() 和 shutdown() 似乎不能很好地协同工作。此外,SO_REUSEADDR 通常与侦听套接字一起使用。即在服务器端。
您应该将 ip/port 传递给 connect()。如下所示:
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.connect(('', 5555))
不要调用bind(),不要设置SO_REUSEADDR。
解决方案 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 套接字文档中指出的那样:
如果提供,source_address 必须是 2 元组 (host, port),套接字在连接之前要绑定到该元组作为其源地址。如果 host 或 port 分别为 '' 或 0,则将使用 OS 默认行为。
解决方案 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
解决方案 12:
我认为最好的方法就是通过在终端中输入来终止该端口上的进程,fuser -k [PORT NUMBER]/tcp
例如fuser -k 5001/tcp
。
解决方案 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))
这意味着,首先定义套接字,关闭它,然后再次定义,这样如果它卡住了,您可以使用相同的端口。
解决方案 14:
Kathiravelu P.,Sarker F. - Python 网络编程手册,第二版 - 2017中已回答了相同的问题。添加以下有用的代码。
# Reusing socket addresses
import socket
import sys
def reuse_socket_addr():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Get the old state of the SO_REUSEADDR option
old_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
print("Old socket state: %s" %old_state)
# Enable the SO_REUSEADDR option
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
new_state = sock.getsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR)
print("New socket state: %s" %new_state)
local_port = 8282
srv = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
srv.bind( ('',local_port) )
srv.listen(1)
print("Listening on port: %s" %local_port)
while True:
try:
connection, addr = srv.accept()
print("Connected by %s: %s" %(addr[0],addr[1]))
except KeyboardInterrupt:
break
except socket.error as msg:
print("%s" %(msg,))
if __name__ == "__main__":
reuse_socket_addr()
- 2025年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)