2013-01-11 69 views
3

我有一個可能沒有問題的問題要問,我環顧四周,但還沒有看到解決它的直接答案,並認爲我可能會在這裏得到一個快速的答案。在一個使用bsd套接字的簡單的TCP/IP客戶端 - 服務器選擇循環中,如果客戶端發送兩個同時到達服務器的消息,那麼在服務器上調用recv會返回綁定在緩衝區中的兩個消息還是recv強制每個不同的到達消息要單獨閱讀?C++ posix sockets recv功能

我問,因爲我在一個環境中工作,我無法分辨客戶端如何構建它的消息發送。通常recv報告12個字節被讀取,然後是915,然後是12個字節,然後是915,依此類推,以這種交替的12到915模式...但是然後有時它報告927(它是915 + 12)。我一直在想,客戶端在將信息發送到服務器之前將它們中的一部分信息捆綁在一起,或者在調用recv之前到達消息,然後recv同時提取所有待處理字節。所以我想確保我正確理解recv的行爲。我想也許我在這裏理解了一些東西,希望有人能指出來,謝謝!

回答

7

TCP/IP是基於流的傳輸,而不是基於數據報的傳輸。在流中,send()recv()之間不存在1對1的相關性。這僅適用於數據報。所以,你必須做好準備,以處理多種可能性:

  1. send()單一呼叫可以容納在一個TCP數據包,並通過向recv()單個呼叫在完整地閱讀。

  2. send()的單個調用可能會跨越多個TCP數據包,並且需要多次調用recv()才能讀取所有內容。

  3. send()的多次調用可能適合單個TCP數據包,並可通過對recv()的單次調用完全讀取。

  4. send()的多次調用可能會跨越多個TCP數據包,並且需要對每個數據包多次調用recv()

爲了說明這一點,考慮兩個消息被髮送 - send("hello", 5)send("world", 5)。撥打電話recv()時,以下是幾種可能的組合:

"hello" "world" 
"hel" "lo" "world" 
"helloworld" 
"hel" "lo" "worl" "d" 
"he" "llow" "or" "ld" 

獲得創意嗎?這就是TCP/IP的工作原理。每個TCP/IP實現都必須考慮到這種分割。

爲了正確地接收數據,必須有邏輯消息,而不是單個調用send()之間的明確分離的,因爲它可能需要多次調用send()發送單個消息,並且多個recv()調用來接收單個信息完整。因此,以前面的例子進去,讓我們添加的消息之間的分隔符:

send("hello\n", 6); 

send("world", 5); 
send("\n", 1); 

在接收端,你會叫recv()多次都沒關係,直到收到\n字符,然後你會處理你收到的所有東西都可以導致這個角色。如果在完成時剩餘任何讀取數據,請將其保存以備後續處理,並再次開始撥打recv(),直到下一個\n字符爲止,依此類推。

有時,不可能在消息之間放置一個唯一的字符(也許消息體允許使用所有字符,因此沒有可用作分隔符的獨特字符)。在這種情況下,您需要在郵件前加上郵件的長度,作爲前一個整數,結構化郵件頭等。然後,根據需要簡單地撥打recv(),直到收到完整整數/標題,然後您根據需要調用recv()多少次即可讀取與長度/標題指定的字節數相同的字節數。完成後,根據需要保存剩餘的數據,然後重新開始撥打recv()以讀取下一個消息長度/標題,依此類推。

+0

非常感謝您的詳細回覆,非常感謝。瞭解TCP/IP實現的基本細節有助於達成一致,並希望能夠幫助那些不瞭解的人。 – user1930581

1

對於兩個消息在單個recv呼叫中返回(參見Nagle's Algorithm)絕對有效。 TCP/IP保證順序(消息中的字節不會混合)。除了它們在單次調用中一起返回外,單條消息也可能需要多次調用recv(儘管對於所描述的小包來說不太可能)。

+0

謝謝你的直接回應,非常感謝。猜猜我將不得不重做我的代碼來解決這個問題! :P – user1930581

+0

我會說「TCP保證訂單」。 IP對此無能爲力。 – ysdx

0

您唯一能指望的就是字節的順序。你不能指望他們如何分成recv呼叫。有時候,事情會在終端或者一路上合併。事情也可能在一路上分崩離析,因此獨立抵達。這聽起來像你的發件人交替發送12和915,但你不能指望它。

+0

感謝您提供易於理解的答案和建議,我將重新編寫我的代碼來解決此問題。 – user1930581