Python:绑定套接字:“地址已经在使用”
我有一个关于TCP / IPnetworking上的客户端套接字的问题。 假设我使用
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)
创build的套接字将绑定到端口5555.问题是,在结束连接后
comSocket.shutdown(1) comSocket.close()
使用wireshark,我看到双方的FIN,ACK和ACKclosures的套接字,我不能再次使用该端口。 我得到以下错误:
[ERROR] Address already in use
我想知道怎样才能立即清除端口,以便下次我仍然可以使用同一个端口。
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
setsockopt似乎不能解决问题谢谢!
在绑定套接字之前尝试使用SO_REUSEADDR
套接字选项。
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
编辑:我看到你仍然有这个麻烦。 有一个SO_REUSEADDR
不起作用的情况。 如果尝试绑定套接字并重新连接到相同的目标(启用了SO_REUSEADDR
),则TIME_WAIT
仍然有效。 但是,它将允许您连接到不同的主机:端口。
想到几个解决scheme。 您可以继续重试,直到重新获得连接。 或者,如果客户端启动closures套接字(而不是服务器),那么它应该神奇地工作。
这是我testing完整的代码,绝对不会给我一个“地址已被使用”的错误。 您可以将其保存在一个文件中,然后从要提供的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
实际上,SO_REUSEADDR标志可能会导致更大的后果:SO_REUSADDR允许您使用TIME_WAIT卡住的端口,但仍不能使用该端口build立到最后连接的地方的连接。 什么? 假设我select本地端口1010,并连接到foobar.com端口300,然后closures本地,在TIME_WAIT留下该端口。 我可以立即重用本地端口1010连接到foobar.com端口300以外的任何地方。
但是,通过确保远程端启动closures(closures事件),您可以完全避免TIME_WAIT状态。 所以服务器可以通过让客户先closures来避免问题。 应用程序协议的devise必须使客户端知道何时closures。 服务器可以安全地closures来响应来自客户端的EOF,但是当客户端不正常地离开networking时,它也将需要设置超时以预期EOF。 在许多情况下,在服务器closures之前等待几秒钟就足够了。
我也build议你学习更多关于networking和networking编程。 你现在至less应该如何使用TCP协议。 该协议是相当微不足道的,因此,可以为您节省大量的时间在未来。
使用netstat
命令可以很容易地看到哪些程序((程序名,pid)元组)被绑定到哪个端口以及什么是套接字当前状态:TIME_WAIT,CLOSING,FIN_WAIT等等。
一个非常好的linuxnetworkingconfiguration的解释可以发现https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait 。
绑定前需要设置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()
这可以防止服务器在我们有机会设置标志之前进行绑定。
如果您使用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()
socket.socket()
应该在socket.bind()
之前运行,并使用REUSEADDR
正如Felipe Cruze提到的,你必须在绑定之前设置SO_REUSEADDR。 我在另一个网站上find了解决scheme – 在其他网站上的解决scheme,转载如下
问题是必须在地址绑定到套接字之前设置SO_REUSEADDR套接字选项。 这可以通过inheritanceThreadingTCPServer并覆盖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)
对我来说,更好的解决办法是以下几点。 由于closures连接的动作是由服务器完成的,因此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
我终于使用该解决scheme让操作系统select端口本身,如果先例仍在TIME_WAIT中,则使用另一个端口。
我replace了:
self._socket.bind((guest, port))
有:
self._socket.bind((guest, 0))
正如在tcp地址的python套接字文档中指出的那样:
如果提供,在连接之前,source_address必须是套接字要绑定的2元组(主机,端口)。 如果主机或端口分别为“0”或0,则将使用操作系统默认行为。
我知道你已经接受了一个答案,但我相信这个问题与在客户端套接字上调用bind()有关。 这可能是好的,但bind()和shutdown()似乎并不能很好地发挥作用。 另外,SO_REUSEADDR通常与侦听套接字一起使用。 即在服务器端。
你应该传递和ip /端口连接()。 喜欢这个:
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) comSocket.connect(('', 5555))
不要调用bind(),不要设置SO_REUSEADDR。
另一个解决scheme当然是在开发环境中,例如使用它的杀死进程
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
我认为最好的方法是通过inputterminalfuser -k [PORT NUMBER]/tcp
,例如fuser -k 5001/tcp
来终止该端口上的进程。
我发现只是在while循环中使用这个修复错误
if KeyboardInterrupt == 1 : sys.exit(1);