2017-05-26 67 views
1

TCP可以任意合併和拆分數據包。因此,假設我這樣做,例如,這樣的電話:當我請求更多的數據比立即可用的時候`recv`會阻塞在TCP套接字上嗎?

`recv(sock, buf, 15, 0)` 

但此時此刻只有5字節的數據立即可用 -

威爾recv()塊,直到15字節的數據是可用的,或將我只能得到5字節?

我這麼問是因爲我想知道如果我能讀,例如uint32_t只是這樣(假設iuint32_t類型的變量):

if(recv(sock, &i, sizeof(uint32_t), 0) < sizeof(uint32_t)) { 
    /* error */ 
} 
i = ntohl(i); 

還是我,而必須做這樣的事情:

unsigned char buff[sizeof(uint32_t)]; 
ssize_t read_already = 0; 
while(read_already != sizeof(uint32_t)) { 
    ssize_t read_now = recv(sock, buff, sizeof(uint32_t)-read_already, 0); 
    if(read_now == -1) { 
     /* error */ 
    } 
    else { 
     read_already += read_now; 
    } 
} 
memcpy(&i, buff, sizeof(uint32_t)); 
i = ntohl(i); 

後者是明顯醜陋,更艱鉅,但可悲的是必要的,如果recv()不會阻止,直到它接收所有請求的數據的情況下,該軟件包被拆分。

回答

1

通常,在沒有設置任何特殊標誌,套接字選項或ioctls的情況下,調用阻塞TCP套接字的recv將返回小於或等於所請求大小的任意數量的字節。但是,除非套接字遠程關閉,信號中斷或處於錯誤狀態,否則它將阻塞,直到至少有1個字節可用。

換句話說,如果你問15個字節,但只有5可用,recv將返回5

應用程序開發人員不應該依賴於數據如何發送或建造和對待自己的套接字能夠隨時返回部分數據流。 (或者正如我告訴其他人一樣,編寫代碼就好像有可能recv一次只返回1個字節)。

用於接收數據的常見循環通常如下所示。請注意在recv調用中在buffer上完成的指針數學運算。

unsigned char buffer[bytes_expected]; 
ssize_t bytes_received = 0; 
while (bytes_received < bytes_expected) 
{ 
    int result = recv(sock, buffer + bytes_received, bytes_expected-bytes_received, 0); 
    if (result == 0) 
    { 
     // socket was closed remotely - break out of the loop 
    } 
    else if (result < 0) 
    { 
     // socket was closed on remote end or hit an error 
     // either way, the socket is likely dead 
     break; 
    } 
    else 
    { 
     bytes_received += result; 
    } 
} 

的例外,我知道是基於作爲最後一個參數來recvMSG_WAITALL標誌。這將持有套接字(塊),直到您獲得length參數所要求的所有字節傳遞給recv(或直到錯誤或套接字關閉)。

man page for recv

MSG_WAITALL(因爲Linux 2.2)該標誌的請求,所述操作 塊,直到完整的請求被滿足。然而,該呼叫可能仍然 返回要接收的下一個數據比所請求如果一個信號被捕獲,錯誤或 斷開發生時,或更少的數據比返回

一個事實,即不同 類型的你甚至被問到這個問題讓你的聯盟高於大多數其他誰是新的插座。在現實世界中存在很多錯誤的代碼,因爲大多數開發人員沒有處理recv返回比預期更少的事情。

+0

謝謝。那麼,我的第二個片段的醜陋和艱辛是不是逃不過? – gaazkam

+1

'MSG_WAITALL'可能會滿足您的需求。你的代碼段中存在一個錯誤(你沒有在你的問題中對「buff」進行指針計算)。查看我提供的代碼示例。 – selbie

相關問題