應(yīng)用層的協(xié)議簡單來說就是程序之間網(wǎng)絡(luò)通信的方式淋昭,相當(dāng)于就是我們說話的方式茵休,兩個人能成功的進(jìn)行溝通需要有兩個先決條件:
1、大家在說同一種語言
一個只會說漢語的人和一個只會說德語的人注定是無法在一起的震叙;一個胡建人和一個河北人雖然會有較高的誤碼率倚评,但基本還是可以談戀愛的。
2单鹿、需要有“斷句”方式的約定
人類的溝通無一例外是通過“說話的停頓”和“對于語言內(nèi)容的理解”來進(jìn)行斷句的:
說話的停頓
“Hey掀宋,你在干啥呢?”仲锄、“寫作業(yè)呢”劲妙、“吃飯了沒”、”一會兒去吃
煎餅“儒喊,理解沒有問題
對于語言內(nèi)容的理解
“Hey你在干啥呢吃飯了沒”镣奋、”寫作業(yè)呢一會兒去吃煎餅“。也能費(fèi)勁理解(NLP)
3澄惊、對于計算機(jī)網(wǎng)絡(luò)
當(dāng)兩臺計算機(jī)需要溝通的時候唆途,情況是十分類似的:
“大家在說同一種語言”很容易理解,我們不能用FTP協(xié)議去訪問HTTP的服務(wù)器掸驱。
“斷句方式“往往被初學(xué)者所忽略肛搬,校園招聘的時候經(jīng)常遇到小朋友做了個“基于socket的文件傳輸工具”,但細(xì)問起傳輸協(xié)議是怎么判定文件傳輸結(jié)束的時候毕贼,有很多人就不明白這個問題的點在哪里温赔,這時我就十分懷疑這個”傳輸工具“只是個Broken Toy。
由于現(xiàn)在廣泛應(yīng)用的TCP協(xié)議是基于流的傳輸協(xié)議(Stream)鬼癣。也就是說陶贼,傳輸層協(xié)議本身沒有“斷句”的功能。
參見:https://en.wikipedia.org/wiki/Transport_layer#COMPARISON1
這就要求我們在協(xié)議層實現(xiàn)”斷句“:
4待秃、 ”斷句“的方式
斷句的方式也是隨著計算機(jī)網(wǎng)絡(luò)的發(fā)展一直在進(jìn)化:
初拜秧,盤古開天辟地,有些類似于PING-PONG協(xié)議的非常簡單的定長協(xié)議章郁,限定消息長度是4個字節(jié)枉氮,協(xié)議的功能也十分之弱智志衍,你發(fā)PING,我回PONG聊替。
1968~1971年誕生的Telnet楼肪、FTP這類化石級的協(xié)議,一般采用的策略就是和我們寫文章一樣的方式:用回車和換行來標(biāo)示一條命令的結(jié)束惹悄。這里細(xì)心的人就會想那遇到消息里本身有\(zhòng)r\n的情況怎么辦呢春叫?答案就是,轉(zhuǎn)義泣港!就是在消息本身帶有\(zhòng)r\n的地方前面加一個”\”字符暂殖。stupid but work
1982年制定的SMTP協(xié)議的Header就沿用了上述的\r\n方式,但SMTP需要傳輸郵件內(nèi)容爷速,郵件附件央星。如果繼續(xù)采用“轉(zhuǎn)義”大法霞怀,恐怕有點吃不消惫东。這時候就有大神提出了用Base64進(jìn)行編碼,把所有字符都轉(zhuǎn)化成a-z, A-Z, 0-9等字符毙石。這樣就就完美解決了這個問題廉沮。但副作用就是數(shù)據(jù)會膨脹1/3左右。
1991年制定HTTP協(xié)議Header繼續(xù)沿用\r\n的方式徐矩,但由于HTTP將會傳輸更多的數(shù)據(jù)滞时,用Base64就太浪費(fèi)寶貴的帶寬資源了。于是我們的前輩們就想出一個好辦法滤灯,在Header里固定增加一個Content-length字段坪稽,用來標(biāo)示,后續(xù)的數(shù)據(jù)大小鳞骤。Haeder和content用\r\n\r\n分割窒百。這樣就解決了問題,有不會造成數(shù)據(jù)膨脹豫尽。(后續(xù)增加的trunk模式這里就不展開說了)
當(dāng)然有朋友就會問了篙梢,有沒有可能不增加額外的字節(jié)解決“斷句”問題?答案是:Yes美旧。1999年XMPP協(xié)議被制定出來的時候渤滞,就實現(xiàn)了這個設(shè)想。XMPP是基于XML的協(xié)議榴嗅,XML的語法解析是有能力判斷XML是否閉合妄呕,從而完成“斷句”。但這種方式有一個較為嚴(yán)重的問題就是嗽测,“斷句”邏輯依賴編碼方式绪励,這樣在工程上的實現(xiàn)難度就會變大。
這里有個有意思的插曲,大家可能發(fā)現(xiàn)GFW有封SSH協(xié)議的能力优炬,按理說SSH是加密的颁井,不應(yīng)該能被識別出來啊,看下圖大家就應(yīng)該明白了:
SSH協(xié)議在client連接成功后蠢护,server端會主動明文發(fā)送ssh的版本信息雅宾。
5、復(fù)雜度和效率的平衡
首先我們介紹一種叫Netstring 的協(xié)議:開頭用ASCII明文標(biāo)記后面數(shù)據(jù)的大小葵硕,然后緊跟一個“:”眉抬,然后是數(shù)據(jù),最后以一個“,”結(jié)尾懈凹。
12:hello world!,
空消息:
0:,
我們在之前幾家公司做項目的時候為了平衡“工程實現(xiàn)復(fù)雜度”和“運(yùn)行效率”蜀变,往往采用類似Netstring的協(xié)議,并進(jìn)一步簡化介评,固定10bytes的ASCII頭表示消息長度库北,后面緊跟數(shù)據(jù)。
0000000002hi
這樣做的優(yōu)勢有:
- ASCII頭方便debug
- 10bytes固定頭長度们陆,易于算法實現(xiàn)寒瓦,消息長度上限大約是10GB,基本夠用
- 頭后面緊跟數(shù)據(jù)坪仇,不需要其它邏輯