2009-07-06 104 views
4

我有一個測試應用程序,打開一個套接字,通過此套接字發送一些東西,然後關閉它。這是循環完成5-10.000次。問題是,經過反覆3,4000我得到這個類型的錯誤:套接字綁定錯誤

java.net.BindException: Address already in use: connect 

我甚至設定的插座中immediattly使用,但錯誤依然存在

try 
{ 
    out_server.write(m.ToByteArray()); 
    socket_server.setReuseAddress(true); 
    socket_server.close(); 
} 
catch(Exception e) 
{ 
    e.printStackTrace(); 
    System.out.println(i+" unable to register with the server"); 
} 

我能怎麼做解決這個問題 ?

回答

11

我想你可能會太快。

大多數操作系統在任何時候都可以打開的套接字數量有限制,但實際上這比其更糟。

當套接字關閉時,它會處於特定的時間等待狀態一段時間。這通常是數據包生存時間值的兩倍,並且可以確保網絡中沒有數據包正在通往套接字的路徑中。

一旦該時間到期,您可以確定網絡中的所有數據包已經死亡。套接字被置於特殊狀態,以便在關閉網絡時在網絡中傳出的數據包可以在它們死亡之前到達時被捕獲並丟棄。

我認爲這就是你的情況,套接字沒有像你想象的那樣被快速釋放。

我們有一個類似的代碼打開了很多短暫的會話問題。它運行良好一段時間,但隨後硬件變得更快,允許在給定的時間段內打開更多的硬件。這表現爲無法開放更多會議。

檢查此的一種方法是從命令行執行netstat -a並查看有多少會話實際處於等待狀態。

如果情況確實如此,有幾種方法可以處理它。

  • 重新使用您的會話,無論是手動或通過維護連接池。
  • 在每個連接中引入延遲嘗試並停止達到飽和點。
  • 直到你達到飽和,然後然後修改你的行爲,比如在一個while語句內運行你的連接邏輯,每次在完全放棄之前每次重試最多60次,延遲時間爲2秒。這可以讓您全速運行,只有在出現問題時纔會放慢速度。

這最後的子彈一點值得一些擴展。實際上,我們在上述應用中使用了一種退避策略,如果它在抱怨,那麼它將逐漸減少資源提供者的負載,而不是30秒的延遲,我們選擇延遲一秒,然後選擇兩秒,然後是四個等等。

退避策略的一般過程如下,它可以用於資源可能暫時短缺的任何情況。在下面的僞代碼中提到的動作就是在你的情況下打開一個套接字。

set maxdelay to 16 # maximum time period between attempts 
set maxtries to 10 # maximum attempts 

set delay to 0 
set tries to 0 
while more actions needed: 
    if delay is not 0: 
     sleep delay 
    attempt action 
    if action failed: 
     add 1 to tries 
     if tries is greater than maxtries: 
      exit with permanent error 
     if delay is 0: 
      set delay to 1 
     else: 
      double delay 
      if delay is greater than maxdelay: 
       set delay to maxdelay 
    else: 
     set delay to 0 
     set tries to 0 

這使得處理能夠以全速在絕大多數情況下運行,但回退時的錯誤開始發生,希望給資源提供者的時間來恢復。延遲的逐漸增加允許更嚴重的資源限制來恢復,並且最大的嘗試可以捕捉到您所稱的永久性錯誤(或需要很長時間才能恢復的錯誤)。

+0

你必須配置插座TIME_WAIT等相關paramters根據不同的操作系統(機器要連接到) – 2009-07-06 06:37:50

+2

它並不總是一個好主意,用這些參數撥弄。大多數應該針對網絡特性進行調整,然後您的應用程序應該進行調整。在不減少生存時間的情況下減少time_wait將導致虛假數據包到達。將TTL減少到數據包無法到達目的地意味着大量丟棄的數據包。理想情況下,您應該保持連接處於打開狀態(手動或連接池)或調整應用程序行爲(例如在他的回答中提及@Stu提及的延遲)。 – paxdiablo 2009-07-06 06:40:44

2

我的建議:

  • 沖洗寫操作之後的插座
  • 在上述方法

@Pax的末尾添加一個微小的睡眠(?〜50ms)內,有一個好點關於之後的插座狀態。嘗試你的代碼,讓它失敗,然後做一個netstat並分析它(或張貼在這裏)

+0

其實我有一個睡眠時間,但它只有5ms。 – 2009-07-06 06:48:31

1

我同意別人,你用盡套接字端點。然而,從你的例子來看,這並不是100%清晰的,因爲大概這個例外來自一個connect()或bind()調用,這可能是其他一些高級Java方法的基礎。

還應該強調,用盡端點並不是套接字庫的某種限制,而是任何TCP/IP實現中非常重要的部分。您需要保留關於舊連接的信息一段時間,以便舊連接的遲到IP包丟失。

setReuseAddress()對應於低級別的SO_REUSEADDR套接字選項,並且只在服務器執行listen()時才適用。

+1

SO_REUSEADDR套接字選項僅適用於服務器,這是個誤導。這個選項可以被服務器和客戶端使用。 – 2009-07-06 08:29:32

2

什麼操作系統?如果您使用的是Windows,並且我猜測您是,那麼您可以擁有的客戶端連接數有限制(這是由MaxUserPort註冊表項配置的,默認情況下,它恰好爲4000;請參閱http://technet.microsoft.com/en-us/library/aa995661.aspxhttp://www.tech-archive.net/Archive/Windows/microsoft.public.windows.server.general/2008-09/msg00611.html有關更改它的詳細信息)。這一點,加上你從客戶端啓動套接字的事實,因此在你客戶端累積的套接字狀態可能是你的問題的原因。

請注意,TIME_WAIT積累問題的解決方案不是擺脫TCP堆棧的參數,使問題消失。 TIME_WAIT存在一個很好的理由,並刪除或縮短它可能會導致你的新問題!

因此,假設您在Windows機器上,第一步是調整您的MaxUserPort值,以便您有更多動態端口可用於您的出站連接。接下來,如果這不能解決你的問題,你可以考慮連接的哪一端應該以TIME_WAIT(假設你可以控制在你的連接上使用的協議......)發出'active close '是最後一個以TIME_WAIT結尾的,所以如果你可以改變一些東西以便你的服務器發出主動關閉,那麼TIME_WAIT插座將會積累在服務器上,而不是在客戶端上,這對你來說會更好一些。 ..

0

如果示例代碼實際上是你如何執行循環,你可能有錯誤的順序的東西。

java docs for setReuseAddress say:當套接字綁定後啓用或禁用SO_REUSEADDR時的行爲(請參閱isBound())未定義。

嘗試在bind()或connect()之前將調用移動到某處。

0

在使用socket.close()後不會立即關閉套接字並且循環執行(在循環中它會嘗試使用相同的ip和端口的套接字連接),所以請將套接字置空。

socket_server.close();

socket_server = null;

感謝 蘇尼爾·庫馬爾Sahoo