2009-02-25 27 views
6

這是一種關閉other question的分支。如果你喜歡,請閱讀它,但沒有必要。基本上,我意識到爲了在大消息上有效地使用C#的BeginReceive(),我需要(a)首先讀取數據包長度,然後讀取恰好那麼多字節或(b)使用結束符包分隔符。我的問題是,這些協議緩衝區中是否存在這些協議?我還沒有使用它們,但通過文檔看起來好像沒有長度標題或分隔符。如何檢測協議緩衝區消息何時完全接收?

如果不是,我該怎麼辦?我應該只是建立消息,然後前綴/後綴長度標頭/ EOP分隔符?

回答

14

您需要在協議中包含大小或結束標記。除了支持任意分解爲單獨數據包的不確定數據流(並且數據包也可能在傳輸中溢出)之外,沒有什麼內置基於流的套接字(TCP/IP)。

一個簡單的方法是每個「消息」都有一個固定大小的頭,包括協議版本和有效載荷大小以及任何其他固定數據。然後是消息內容(有效載荷)。

可以添加一個消息頁腳(固定大小)和一個校驗和甚至一個加密簽名(取決於您的可靠性/安全性要求)。

知道有效載荷的大小可以讓你保持讀取的字節數,這將是足夠的消息的其餘部分(如果讀取與少,做是另一回事讀了剩餘的字節已收到整個消息,直到完成)。

有一個結束消息指示符也可以,但是你需要定義如何處理包含相同的八位位組序列您的郵件...

1

TCP/IP,以及UDP報文包括一些參考它們的大小。 IP header包含一個16位字段,它指定了IP報頭的長度和以字節爲單位的數據長度。 TCP header包含一個4位字段,用32位字指定TCP頭的大小。 UDP header包含一個16位字段,它指定了UDP數據頭的長度(以字節爲單位)。

這是事情。不管你在C#中使用System.Net.Sockets命名空間還是在Win32中使用本地Winsock,你永遠都不會看到IP/TCP/UDP頭文件。這些頭文件被剝離掉了,這樣你在讀取套接字時得到的是實際的有效負載,即發送的數據。

從我見過並使用套接字完成的所有事情的典型模式是,您可以在要發送的數據之前定義一個應用程序級標題。這個頭文件至少應該包含要遵循的數據大小。這將允許您完整閱讀每個「消息」,而不必猜測其大小。您可以隨心所欲地使用它,例如,同步模式,CRC,版本,消息類型等,但「消息」的大小是您真正需要的

對於它的價值,我建議使用頭部而不是包尾分隔符。我不確定EOP定界符是否存在重大缺陷,但頭是我見過的大多數IP協議使用的方法。另外,從一開始就處理一條消息似乎更直觀,而不是等待某種模式出現在我的數據流中,以表明我的消息已完成。

編輯:我只是剛剛意識到谷歌協議緩衝區項目。從我所知道的情況來看,這是WCF的二進制序列化/反序列化方案(我相信這太過簡單化了)。如果您使用的是WCF,則不必擔心發送的消息的大小,因爲WCF管道在幕後處理此問題,這可能是爲什麼您沒有在協議中找到與消息長度相關的任何內容緩衝區文件。然而,就插座而言,知道尺寸將如上所述有極大的幫助。我的猜測是,你將使用Protocol Buffers序列化你的數據,然後在你發送任何應用頭之前發送它。在接收端,您將取消標題,然後對消息的其餘部分進行反序列化。

+0

如果你的意思是protobuf-net,不,它不僅僅是WCF;項目中有套接字示例。 – 2009-03-26 08:31:52

3

我同意馬特,一個頭比一個協議緩衝區的頁腳更好,因爲PB是一個二進制協議,有一個問題是想出一個不是有效的消息序列的頁腳。許多基於頁腳的協議(通常是EOL)會工作,因爲消息內容處於定義範圍內(通常爲0x20 - 0x7F ASCII)。

一個有用的方法是讓你的最低級代碼從套接字讀取緩衝區,並將它們呈現給一個組裝完整消息並記住部分消息的成幀層(我對此提出了一種異步方法(使用CCR) here,儘管用於線路協議)。

爲了保持一致性,您可以始終將消息定義爲具有三個字段的PB消息:固定長度作爲長度,枚舉作爲類型,以及包含實際數據的字節序列。這使您的整個網絡協議保持透明。

6

在派對上遲到的道歉。我是C#實現之一protobuf-net的作者。對於網絡使用,你應該考慮「[De] SerializeWithLengthPrefix」方法 - 這樣,它會自動處理你的長度。源代碼中有例子。

我不會在舊帖子中詳細討論,但如果您想了解更多信息,請添加評論,我會盡快與您聯繫。