在 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, s...

问题描述:

我有一个关于 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()
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1325  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。它涵盖了从产品概念产生到产品退市的整个生命周期,通过整合跨部门团队、优化流程等方式,显著提升产品开发的效率和质量,进而为项目的成功奠定坚实基础。深入探究IPD流程的五个阶段与项目成功之间...
IPD流程分为几个阶段   4  
  华为作为全球知名的科技企业,其成功背后的管理体系备受关注。IPD(集成产品开发)流程作为华为核心的产品开发管理模式,其中的创新管理与实践更是蕴含着丰富的经验和深刻的智慧,对众多企业具有重要的借鉴意义。IPD流程的核心架构IPD流程旨在打破部门墙,实现跨部门的高效协作,将产品开发视为一个整体的流程。它涵盖了从市场需求分析...
华为IPD是什么   3  
  IPD(Integrated Product Development)研发管理体系作为一种先进的产品开发模式,在众多企业的发展历程中发挥了至关重要的作用。它不仅仅是一套流程,更是一种理念,一种能够全方位提升企业竞争力,推动企业持续发展的有效工具。深入探究IPD研发管理体系如何助力企业持续发展,对于众多渴望在市场中立足并...
IPD管理流程   3  
  IPD(Integrated Product Development)流程管理旨在通过整合产品开发流程、团队和资源,实现产品的快速、高质量交付。在这一过程中,有效降低成本是企业提升竞争力的关键。通过优化IPD流程管理中的各个环节,可以在不牺牲产品质量和性能的前提下,实现成本的显著降低,为企业创造更大的价值。优化产品规划...
IPD流程分为几个阶段   4  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用