2013-05-27 149 views
3

我正在寫一個服務器端客戶端程序與下面的代碼片段接收數據。套接字recv上select()編輯套接字失敗與etimedout

ret_l = select(readfds+1, &readfds, NULL, NULL ,NULL); 
    if(ret_l != -1) 
    { 
     if(FD_ISSET(myfd, &readfds)) 
     { 
      ret_l = recv(myfd, buf, size_of_buf_array, 0); 
      if(ret_l == -1) 
       return ; 
     } 
    } 

據我所知,recv在select()ed文件描述符應該接收數據沒有失敗。但是,在我的代碼中收回錯誤ETIMEDOUT失敗。有人請告訴我爲什麼會發生這種情況。還請告訴我一些解決方法,即使在ETIMEDOUT之後也能完全接收數據。

+1

代碼中'readfds'的類型是什麼?從我在文檔中看到的,它應該是一個'fd_set',一個struct {int n,int []},所以在第一個參數中沒有什麼可以安全地使用'+ 1'。你有沒有黑客入侵系統,偶然使用'int readfds'? – quetzalcoatl

+1

這裏沒有代碼在適當的地方檢查errno。您是如何決定獲得ETIMEDOUT的? – nos

+0

當我使用我的公司框架時,我刪除了一些框架細節並複製了需要理解的代碼。 readfds是fd_set類型,select的第一個參數是max(socket函數返回的fds)+1 – syam

回答

0

只是一個瘋狂的猜測。當TCP連接丟失時。 select會返回並將此fd設置爲可讀。但是recv將失敗,並顯示錯誤ETIMEDOUT。

0

一個可能的原因是套接字選項SO_RCVLOWAT

如果它的值大於1,那麼linux的select返回即使只有一個字節可用,並聲稱該套接字是可讀的。

當在這種情況下調用recv時,它會阻塞,直到發生超時(由SO_RCVTIMEO設置),因爲可用字節數小於低水位標記。

因此,檢查您的代碼是否更改SO_RCVLOWAT的值。默認值是1。

的更多信息:here

的選擇(2)和輪詢(2)目前SYS-TEM調用不尊重Linux上的 SO_RCVLOWAT設置,並且標記可讀在套接字即使是 也只有一個字節的數據可用。從套接字 後面的讀取將阻塞,直到SO_RCVLOWAT字節可用。

+0

爲什麼在這種情況下會得到ETIMEDOUT? – nos

+0

很明顯,SO_RCVTIMEO也發生了變化。如果沒有改變,那麼我的回答並不能解決OP的問題。 – SKi

+0

嗯,根本不清楚SO_RCVTIMEO是否發生了變化,在這種情況下,errno應該是EAGAIN。 ETIMEDOUT也可能在其他情況下發生。 – nos

4

有用於看到ETIMEDOUT三種可能原因:

  1. 連接內recv超時,這是不太可能甚至一度(但肯定不是幾次)發生。
  2. 您沒有檢查成功connect,並且連接從未成功建立(也許防火牆正在刪除連接嘗試?)。這是可能的原因。
  3. 你的套接字實現被破壞,這是不太可能的。

select不會產生ETIMEDOUT,只有connectrecv可能。雖然select在極少數情況下可以在沒有任何東西可以收到時報告準備情況(較早的Linux內核,這大概已經修復),但是在這種情況下唯一會發生的情況是阻止recv

recv可能會產生此錯誤,但一旦連接建立後連接就不會超時 - 如果您沒有拉電纜,或者正如nos所指出的那樣,NAT網關可能會在幾分鐘後做任何事情。如果可以建立連接,則有一條路由,另一端正在收聽,所以通常沒有超時的原因(當然,這可能,只是不可能一直髮生)。這個錯誤當然最終會發生如果由於某種原因連接真的超時(不管是否阻塞),但是如果有的話,這是一個非常特殊的情況,而不是一個普通的情況。

connect失敗是由於許多原因(不可到達,防火牆,服務器進程未運行等)而可能會看到的情況,並且每次嘗試時都會經常發生,只要導致它持續的條件。

至於在ETIMEDOUT之後完全接收數據的解決方法,沒有。 read會給你什麼它的緩衝區(直到你在函數調用中指定的最大值),或阻塞或失敗。這三件事之一,沒有別的,永遠。
一旦它失敗了,你已經擁有了在失敗之前可用的所有東西(在你的末端沒有更多東西需要讀取),現在連接消失了,即套接字不再可用。
您可以做的唯一事情就是創建一個新的套接字並建立一個新的連接,然後重試。

+0

請注意,在建立連接後,連接超時(即,您正在建立的連接上發送數據,但沒有收到TCP響應,通常爲ACK,TCP將超時連接,因爲重新傳輸計時器已過期)非常常見例如由於NAT網關和防火牆悄無聲息地將連接超時,所以長時間連接一段時間只能傳輸很少或沒有數據。 – nos

+0

是的,一個NAT網關或狀態防火牆超時後,說5分鐘無所事事可能是一個合理的理由,如果你真的沒有發送這麼長的時間。儘管對我來說,這個問題更像是OP發送了一些東西(大概是在局域網中,至少這是我在編寫服務器和客戶端時測試的地方),並且無法接收。 – Damon

1

呃,應該不會是

select(myfd+1,&readfds,NULL,NULL,NULL) 

1

在套接字上啓用TCP keepalive導致ETIMEDOUT errno從recv()返回。

ETIMEDOUT可以返回發送()如果另一端在一段時間後沒有確認重傳的數據。還請檢查TCP_USER_TIMEOUT套接字選項,這也會導致套接字上的ETIMEDOUT錯誤。

您可以從着名的「Unix網絡編程」中查到this chapter