2011-06-17 88 views
52

我有一個關於TCP/IP網絡上的客戶端套接字的問題。比方說,我用Python:綁定套接字:「地址已在使用」

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的似乎並不能夠解決問題 謝謝!

+0

爲什麼客戶端需要特定的端口? – 2011-06-17 00:17:29

+1

因爲我必須將其放入生產服務器,並在該服務器中,所有傳出連接都被阻止。我需要爲套接字指定一個特定的端口,以便他們可以在防火牆上設置一條規則,允許連接通過。 – 2011-06-17 00:46:31

+0

我建議你複製並粘貼實際的代碼。你上面寫的有一個明顯的錯誤,會阻止你看到你聲稱已經看到的行爲。這讓讀者不知道你有什麼疏忽告訴我們可能會導致你的問題。 – 2011-06-17 01:01:06

回答

82

嘗試在綁定套接字之前使用套接字選項SO_REUSEADDR

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

編輯: 我看到你仍然有這個麻煩。有一種情況下SO_REUSEADDR不起作用。如果嘗試綁定套接字並重新連接到相同的目標(啓用了SO_REUSEADDR),則TIME_WAIT仍然有效。但是,它將允許您連接到不同的主機:端口。

一些解決方案浮現在腦海。您可以繼續重試,直到您再次獲得連接。或者,如果客戶端啓動關閉套接字(而不是服務器),那麼它應該神奇地工作。

+0

Still無法重複使用它。在重新使用相同端口之前,我必須等待的時間是1分30秒:( – 2011-06-17 00:38:37

+4

是否在'bind'之前調用'setsockopt'?是使用'SO_REUSEADDR'創建的第一個套接字,還是僅僅是失敗的套接字? 等待的套接字必須有'SO_REUSEADDR'才能正常工作 – lunixbochs 2011-06-17 01:00:58

+0

是的,我確實包含了comSocket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1),但仍然不起作用 – 2011-06-17 01:04:31

2

socket.socket()socket.bind()之前應該運行和使用REUSEADDR如說

2

正如菲利普·科魯茲所提到的,必須結合之前設置SO_REUSEADDR。我發現在其他網站上的解決方案 - solution on other site, reproduced below

The problem is that the SO_REUSEADDR socket option must be set before the address is bound to the socket. This can be done by subclassing ThreadingTCPServer and overriding the server_bind method as follows:

 
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) 

1

我知道你已經接受一個答案,但我相信問題與調用bind()上的客戶端套接字做。這可能是好的,但bind()和shutdown()似乎並沒有很好地發揮作用。另外,SO_REUSEADDR通常與偵聽套接字一起使用。即在服務器端。

你應該通過和ip/port來連接()。像這樣:

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

不要調用bind(),不要設置SO_REUSEADDR。

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 

我終於用解決方案,讓OS選擇端口本身,如果先例仍在TIME_WAIT中,則使用另一個端口。

我代替:

self._socket.bind((guest, port)) 

有:

self._socket.bind((guest, 0)) 

正如有人指出的TCP地址python socket documentation

If supplied, source_address must be a 2-tuple (host, port) for the socket to bind to as its source address before connecting. If host or port are ‘’ or 0 respectively the OS default behavior will be used.

21

下面是完整的代碼,我經過測試,絕對不會給我一個「地址已被使用」的錯誤。您可以將它保存在一個文件中,然後從要提供的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 
+0

即使在httpd.shutdown()之後,您仍然想調用httpd.server_close()來完全釋放所有資源。 – Androbin 2018-01-05 23:18:05

+0

另外,考慮使用atexit模塊,以防意外存在。 – Androbin 2018-01-05 23:18:59

10

其實之前,SO_REUSEADDR標誌可能會導致更大的後果:SO_REUSADDR允許您使用該卡在TIME_WAIT端口,但仍無法使用端口建立到最後連接的地方的連接。什麼?假設我選擇本地端口1010,並連接到foobar.com端口300,然後關閉本地,將該端口保留在TIME_WAIT中。我可以立即重新使用本地端口1010連接到foobar.com端口300以外的任何地方。

但是,您可以通過確保遠程端啓動關閉(關閉事件)來完全避免TIME_WAIT狀態。所以服務器可以通過讓客戶先關閉來避免問題。應用程序協議必須設計成客戶端知道什麼時候關閉。服務器可以安全地關閉以響應來自客戶端的EOF,但是當客戶端非正常離開網絡時,它還需要在預期EOF時設置超時。在許多情況下,在服務器關閉之前等待幾秒鐘就足夠了。

我還建議您瞭解有關網絡和網絡編程的更多信息。您現在至少應該如何使用tcp協議。該協議是相當平凡和小,因此,可以爲您節省大量的時間在未來。

使用netstat命令,您可以輕鬆查看哪些程序((程序名,pid)元組)綁定到哪些端口以及什麼是套接字當前狀態:TIME_WAIT,CLOSING,FIN_WAIT等。

對linux網絡配置的一個很好的解釋可以在https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait找到。

+0

另外,你應該小心你的代碼。如果您的代碼仍在開發中,並且發生了一些異常,則連接可能無法正確關閉,尤其是從服務器端關閉。 – 2014-01-07 15:20:04

+5

您應該公平並引用您從[Tom's](http://hea-www.harvard.edu/~fine/Tech/addrinuse.html)網絡指南中複製和粘貼的句子。請更正您的答案。 – HelloWorld 2015-09-22 22:46:25

0

另一種解決方案,在課程的開發環境,是殺過程中使用它,例如

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 
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() 

這可以防止服務器之前,我們有機會來設置標誌結合。

+0

繼承TCPServer並覆蓋屬性似乎更容易,請參閱[我的答案](http://stackoverflow.com/questions/6380057/python-binding-socket-address-already-in-use/35363839#35363839)示例 – Andrei 2016-02-12 13:30:15

0

我認爲最好的辦法就是殺通過輸入終端fuser -k [PORT NUMBER]/tcp,例如在該端口上的過程fuser -k 5001/tcp

+0

除非進程已經被殺死,並且它只是將端口打開以便被os清理。 – 2017-06-27 15:40:16

相關問題