2013-02-27 82 views
1

客戶端報告了一個我無法理解的錯誤。基於TCP的客戶端連接到從其接收數據的服務器,很少發送任何內容。通常一切正常,但一旦在一個藍色的月亮像這樣的情況發生:tcp套接字讀取調用如何不能返回

  • 服務器發送一些數據
  • 客戶端接收數據
  • 客戶端處理數據
  • ...和同時服務器發送更多的數據
  • 客戶端處理完
  • 客戶端嘗試讀取插槽
  • 客戶永遠掛着數據在處理
  • 服務器之後的第一個read()語句關閉連接
  • 客戶仍然掛起

這裏是如何的TCP連接建立(刪除所有日誌,返回支票等)

ret = inet_pton(AF_INET, conn->address, &addr.sin_addr); 
addr.sin_port  = htons(conn->port); /* Server port */ 
addr.sin_family  = AF_INET; 
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
connect(sock, (struct sockaddr *) &addr, sizeof(addr)); 

這裏是讀包裝:

int32_t _readn (int fd, uint8_t *vptr, int32_t n) 
{ 
    int32_t nleft; 
    int32_t nread; 
    uint8_t*  ptr; 

    ptr = vptr; 
    nleft = n; 
    while (nleft > 0) { 
    if ((nread = read (fd, ptr, nleft)) < 0) { 
     if (errno == EINTR) { 
     nread = 0; 
     } else { 
     return E_NETWORK_ERROR; 
     } 
    } else if (nread == 0) { 
     break; 
    } 
    nleft -= nread; 
    ptr += nread; 
    } 
    return (n-nleft); 
} 

是否有可能即使在連接關閉之後,讀取調用永遠都會被阻止?

在我的包裝中有沒有一些棘手的錯誤,我沒有注意到可能會導致這種情況?我應該爲連接上的套接字設置一些標誌嗎?

+1

有,爲什麼不使用它無阻塞,把一個理由像fd上的'select'而不是? – Jite 2013-02-27 13:05:47

+0

不,沒有。這樣做可能是一個解決方案。還有其他一些方法我可以想到這將是一個很好的解決方法。但我想知道問題的根源。 – Dariusz 2013-02-27 13:20:50

回答

4

問題的根源在於是否沒有數據讀取將會阻止讀取。例如。如果少於預期的n個字節寫入。這被稱爲阻止讀取。

要發現是否有數據,請使用select,因爲Jite說。

最後,您可能會在防火牆上刪除實時連接。一些防火牆被配置爲切斷已經打開超過給定時間的連接,例如, 30分鐘。然而,這可能不是你所擁有的。

+0

沒有數據=塊不是源 - 這是結果。來源仍未被發現。 – Dariusz 2013-02-27 14:41:09

+0

@DariuszWawer你從哪裏得到'n'?你怎麼知道預計有多少字節? – Ben 2013-02-27 14:51:18

+0

應用層協議提供該信息。我首先閱讀了3個常用的所有消息頭字節,並據此決定要讀取多少數據。 – Dariusz 2013-02-27 14:55:02

2

我結束了使用基於select的函數來檢查數據是否可用。

而神祕的數據丟失背後的原因仍然是未知的(無服務器錯誤convirmed),這似乎這樣的伎倆:

int32_t isReadDataAvailableOnSocket (int sock, uint32_t waitTimeUs) 
{ 
    fd_set fds; 
    int16_t ret = 0; 
    struct timeval timeout; 
    struct timeval* timeoutPtr = NULL; 

    if (waitTimeUs>0) { 
    timeout.tv_sec = waitTimeUs/1000000; 
    timeout.tv_usec = waitTimeUs % 1000000; 
    timeoutPtr = &timeout; 
    } 

    FD_ZERO (&fds); 
    FD_SET (sock, &fds); 

    ret = select (sock+1, &fds, NULL, NULL, timeoutPtr); 
    if (ret == -1) { 
    WARN("select failed for udp socket=[%d]", sock); 
    return E_NETWORK_ERROR; 
    } 
    if (! FD_ISSET(sock, &fds)) 
    { 
    return E_NO_DATA; 
    } 
    else 
    { 
    return 0; 
    } 
} 
+1

網絡丟失數據。 TCP使用「一遍又一遍地喊它直到它到來」的方式來對此進行反擊,但是如果網絡非常不幸,那可能需要無限的時間才能成功... – 2013-03-19 14:32:01

+0

@DonalFellows它不是無限的。嘗試將最終超時並失敗。 – EJP 2017-02-02 18:16:36

+0

發送端將失敗。接收方將繼續等待。 – 2018-01-18 19:10:29