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

时间:2011-06-17 00:12:11

标签: python sockets tcp port

我对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似乎无法解决问题 谢谢!

12 个答案:

答案 0 :(得分:101)

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

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

修改 我发现你仍然遇到麻烦。有一种情况SO_REUSEADDR不起作用。如果您尝试绑定套接字并重新连接到同一目标(启用SO_REUSEADDR),则TIME_WAIT仍然有效。但是,它允许您连接到不同的主机:端口。

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

答案 1 :(得分:24)

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

答案 2 :(得分:12)

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

但是,您可以通过确保远程端启动闭包(close事件)来完全避免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

答案 3 :(得分:6)

您需要在绑定之前设置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()

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

答案 4 :(得分:6)

如果您使用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 :(得分:3)

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

  

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

import SocketServer, socket

class MyThreadingTCPServer(SocketServer.ThreadingTCPServer):
    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)

答案 6 :(得分:2)

socket.socket()应该在socket.bind()之前运行,并使用REUSEADDR作为上述

答案 7 :(得分:2)

对我来说,更好的解决方案如下。由于关闭连接的主动权是由服务器完成的,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,则将使用操作系统默认行为。

答案 8 :(得分:1)

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

你应该传递和ip /端口连接()。像这样:

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

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

答案 9 :(得分:1)

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

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

答案 10 :(得分:1)

我发现了这个例外的另一个原因。 当从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似乎开始新的'run'作为一个单独的进程,它找到前一个'run'使用的套接字。

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

没有帮助。

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

python3 <app-name>.py
当SO_REUSEADDR设置为true时,

始终运行良好。新的Thonny IDE或Idle3 IDE没有这个问题。

答案 11 :(得分:0)

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