標籤:

為什麼TCP自定義協議報文頭由固定字元如『##』+消息長度+消息體+校驗位組成?

如題,後面都能理解, 但是固定的報文頭 ## 有什麼用處呢?


怎麼沒人回答題主的問題呢……都在撕到底要不要校驗位。
很多協議規定的消息都是沒有前導固定字元的,一上來就是消息長度。
對於有 magic sequence 的協議,這個 sequence 一般有兩個作用,一是方便分辨協議類型(檢測對端是否按照基本法進行)和解析,二是如果傳輸出現了偏差你可以在裡面找找 magic sequence 搶救幾條消息回來(doing 的回答里的「失步」就是這種情況)。有的協議乾脆不寫長度直接用 magic sequence 做消息邊界。
你們問我要不要校驗位,我說要,我就明確告訴你。你們非常熟悉 TCP 的這一套。我是身經百戰了,什麼奇葩設備我沒有見過?你們要知道有些 NAT,看得 內網 IP 的 bit field 就是得 外網 IP 的 bit field,我的協議和它談笑風生。


稍微補充一下吧……

首先已經有人說過了,TCP協議的可靠性…… 並不是100%
http://academic.research.microsoft.com/Paper/22436.aspx

單純的size + data可能導致傳輸數據的錯誤,而且不一定能發現

那麼size + data + check呢?不要##吼不吼啊?

確實可以,但是我舉幾個最簡單的例子,你考慮一下size出現偏差的情況。
看你程序的結構和具體情況吧,有幾種結果,比如你做了判斷,發現size &>&> MAX_SIZE,然後你切斷連接然後等客戶端重連……
比如你沒做判斷,就在那傻等,對面也在等你,然後你有個timeout然後切斷重連……
或者是個實時性和可靠性都有一些要求的應用,比如遊戲之類的,你啥都不知道在那狂吃後面的數據,當然多吃一個都不行啊,回不來了,吃完發現check過不了,然後玩家一看duang地回檔掉線重連了…… 。

加個##的話,出了錯誤一方面好發現,另一方面能控制一下損失…… 但是你仔細一想,這樣搞也有點麻煩啊,到底合不合算呀……

所以,除了簡單地加個##,然後處理一下會遇到的別的麻煩事之外,也有別的搞法,演算法上做處理或者是別的設計之類的……不過這些額…… 你可以去看看那些比較成熟的協議是怎麼乾的嘛,畢竟是具體要求的情況才有得分析才有意義,我就不在這裡廢話了。

也就是說,如果你什麼別的都不做,在一些情況下,單純的 size + data 的做法不一定那麼適用就這麼簡單啦。你總要有一些辦法來防止各種各樣的問題。

用他的話總結來說一個協議的命運啊,不僅要看自身的設計,還要考慮到歷史的行程。


前導「##」用於糾錯作用不大,用於切割上層協議包作用也不大。

通過固定header,解析出長度欄位(len),就能確認msg的大小了,因此不用特殊前導欄位分割。至於len欄位非法,導致後面數據包粘包(這一定是個長連接),想利用前導位重新對齊,我認為更好的方式是斷開連接。
主動斷開連接的好處:1)簡單;2)發送方知道可能傳輸出錯。

竟然不提設置type欄位。
len | type l checksum l msg 才是標配呀。
type欄位對於msg中採用哪種協議序列化是在是太有效了,擴展性大大增強有木有。
接收端拿到type後:
switch type {
case protobuf-request:
do handle1;
case protobuf-response:
do handle2;
case thrift-request:
do handle3;
default:
...

醬紫。。


##的作用參考雲舒的答案

我是出來反對 @pig pig不需要校驗位的觀點,隨便摺疊我

具體:
IP header和TCP header的check sum是一種非常弱的16-bit check sum演算法,把數據當成反碼錶示的16-bit integers,再加到一起。這種checksum演算法能檢出一些簡單的錯誤,而對某些錯誤無能為力,由於是簡單的加法,遇到「和」不變的情況就無法檢查出錯誤(比如交換兩個16-bit整數,加法滿足交換律,結果不變)。乙太網的CRC32隻能保證同一個網段上的通信不會出錯(兩台機器的網線插到同一個交換機上,這時候乙太網的CRC是有用的)。

TCP的可靠性有多高 小節
http://www.cppblog.com/Solstice/default.html?page=3


能問出這種問題…不錯!

作為寫過協議的貨,應該有資格回答。
不過還是反問好了,伸手黨們退散。

1,自定義協議有多少種?
2,協議要發展,兼容性怎麼保證?

看懂這兩個問題,就懂了。


不問是不是,就為為什麼。

誰告訴你tcp需要這樣寫的。

tcp發送方,只需要按下面格式發送:
struct TCPData
{
....Size length;//Size可以是 unit_8,uint_32,uint_64等等。
....Data data; //Data就是你需要傳遞的數據。
}
就行了。

tcp接收方,先接收sizeof( Size )個位元組的length,然後再接收length個位元組的data就行了。
以上是串列發送原則。並行發送,依然是上面這個原則,但需要進行分片包裝,比較複雜,想了解的話,開一個新問題吧。

至於你說的【『##』+消息長度+消息體+校驗位】這種方式,明顯是錯的。
1.如果需要定義不同類型的數據包,只需要:
struct Data
{
....uint_16 MsgType;//消息類型。比如聊天信息為1,傳文件信息為2。
....ChildData childData;//數據正文,比如聊天信息的「你好",或者是要傳輸的圖片文件。
}

2.tcp自帶校驗,不需要在協議里再次校驗。

3.tcp不需要專門的分隔符號。使用我上面說的TCPData結構體,以及發送方式就行了。

------------------------------------

今天看到某些小朋友,跳出來反對老夫不需要校驗位的觀點?老夫正要去評論區和她撕逼,卻發現老夫無法評論。呵呵,小姑娘真有一套。既然不能愉快的撕逼,老夫就把逼撕在這裡:

既然你覺得需要加校驗位的理由是tcp與ip自帶校驗【對某些錯誤無能為力】,那麼請問應該加什麼樣的校驗位才能【對所有錯誤都有能為力】呢?

只要你敢舉例,老夫可以立即和你撕。

.


定義了消息長度才好區分不同的消息。有了消息結構才能解碼。


推薦閱讀:

在博客園上看到<程序員的一天>, 不知道真正的程序員與這篇博客中的相差多少? 望各位前輩能夠解答, 謝謝.
在感覺項目代碼的構架不行的時候,你們會怎麼辦?
有哪些程序員的告白方式?
如何評價 2016 年 IT 業年平均工資破 12 萬元,首次超過金融業,排名各行業門類首位?
程序員在實際工作中寫的代碼和各種學生時代的競賽如ICPC NOI等寫的代碼有什麼區別?

TAG:程序員 | TCPIP |