2010-10-07 54 views
12

我在我的應用程序中出現了一個似乎不可重現的失敗。我有一個TCP套接字連接失敗,應用程序試圖重新連接它。在connect()嘗試重新連接的第二次調用中,我得到了errno == EADDRNOTAVAIL的錯誤結果,其中connect()的手冊頁表示:「指定的地址不能從本地機器獲得。」爲什麼connect()會給EADDRNOTAVAIL?

看着連接()的調用,第二個參數看起來是錯誤所指向的地址,但據我所知,這個參數是遠程主機的TCP套接字地址,所以我對引用本地機器的手冊頁感到困惑。這個地址到遠程TCP套接字主機是不是從我的本地機器可用?如果是這樣,爲什麼會這樣呢?它必須在連接失敗之前第一次成功調用connect(),並且它試圖重新連接並得到這個錯誤。 connect()的參數兩次都是相同的。

這個錯誤是一個短暫的錯誤,如果我嘗試再次調用connect可能會消失,如果我等了很長時間?如果不是,我應該如何嘗試從這個故障中恢復?

+0

我在一個大的Redis集羣中有類似的問題。你的用例是什麼? – Riccardo 2013-11-30 13:39:40

回答

19

檢查此鏈接

http://www.toptip.ca/2010/02/linux-eaddrnotavail-address-not.html

編輯:是的,我打算增加更多,但必須有削減,因爲緊急

你關閉嘗試重新連接之前插座?關閉會告訴系統socketpair(ip/port)現在是空閒的。

以下是一些其他項目也看:

  • 如果本地端口已連接到指定的遠程IP和端口(即存在已經是一個相同的socketpair),您會收到此錯誤(見錯誤鏈接在下面)。
  • 綁定一個不是本地套接字的地址會產生這個錯誤。如果一臺機器的IP地址是127.0.0.1和1.2.3.4,並且你試圖綁定到1.2.3.5,你將會得到這個錯誤。
  • EADDRNOTAVAIL:指定的地址在遠程機器上不可用,或者名稱結構的地址字段全爲零。

鏈接與你類似的(答案是接近底部)

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4294599

看來你的插座基本上停留在TCP內部的國家之一,並且加入了延遲的錯誤重新連接可能會解決您的問題,因爲它們似乎在該錯誤報告中完成了。

+0

鏈接是有幫助的,但只是放在鏈接不是,特別是當這麼多的鏈接變得陳舊和無用。 – 2010-10-08 00:05:28

+0

你有沒有好回答,大衛。 – slezica 2010-10-08 12:45:25

2

如果無效端口給出,如0

+2

作爲目標端口。如果它作爲綁定的本地端口提供,則它是有效的。 – EJP 2014-07-12 09:41:37

0

另一件事來檢查這也有可能發生的是,接口是向上。最近我在使用網絡名稱空間時感到困惑,因爲它似乎創建了一個新的網絡名稱空間,它產生了一個完全獨立的回送接口,但並未啓動(至少與Debian wheezy的版本有關)。這讓我逃了一段時間,因爲人們通常並不認爲迴環一直處於關閉狀態。

1

如果您不願意更改可用臨時端口的數量(如David所建議的那樣),或者您需要的連接數超過理論最大值,還有其他兩種方法可以減少正在使用的端口數。但是,它們在不同程度上違反了TCP標準,因此應謹慎使用它們。

第一個是打開SO_LINGER零秒超時,迫使TCP堆棧發送一個RST數據包並刷新連接狀態。然而,有一個微妙之處:你應該在套接字文件描述符上調用shutdown,然後再發送close,這樣就有機會在RST數據包之前發送一個FIN數據包。因此,代碼看起來類似:

shutdown(fd, SHUT_RDWR); 
struct linger linger; 
linger.l_onoff = 1; 
linger.l_linger = 0; 
// todo: test for error 
setsockopt(fd, SOL_SOCKET, SO_LINGER, 
      (char *) &linger, sizeof(linger)); 
close(fd); 

服務器應該只看到一個過早的連接復位,如果FIN包獲取與RST包重新排序。

請參閱TCP option SO_LINGER (zero) - when it's required瞭解更多詳情。 (實驗上,它似乎並不無論你在哪裏設置setsockopt

二是使用SO_REUSEADDR和明確bind(即使你的客戶端),這將允許你的Linux你什麼時候使用這個臨時端口在他們完成等待之前跑步。請注意,您必須使用bindINADDR_ANY和端口0,否則SO_REUSEADDR不受尊重。你的代碼看起來是這樣的:

int opts = 1; 
// todo: test for error 
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 
     (char *) &opts, sizeof(int)); 

struct sockaddr_in listen_addr; 
listen_addr.sin_family = AF_INET; 
listen_addr.sin_port = 0; 
listen_addr.sin_addr.s_addr = INADDR_ANY; 
// todo: test for error 
bind(fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr)); 

// todo: test for addr 
// saddr is the struct sockaddr_in you're connecting to 
connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)); 

此選項是不太好的,因爲你仍然飽和,內部內核數據結構按netstat -an | grep -e tcp -e udp | wc -l TCP連接。但是,在這種情況發生之前,您不會開始重用端口。

+0

將'SO_LINGER'設置爲零解決了我的問題。謝謝。 – Eric 2016-02-08 08:43:17

相關問題