??TCP協(xié)議的概念
TCP(Transmission Control Protocol 傳輸控制協(xié)議)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議。
??TCP協(xié)議段格式
-
源/目的端口號(hào):表示數(shù)據(jù)是從哪個(gè)進(jìn)程來,到哪個(gè)進(jìn)程去;
-
32位序號(hào)/32位確認(rèn)號(hào):后面詳細(xì)講;
-
4位TCP報(bào)頭長度:表示該TCP頭部有多少個(gè)32位bit(有多少個(gè)4字節(jié));所以TCP頭部最大長度是15 * 4 = 60
-
6位標(biāo)志位:
URG:緊急指針是否有效
ACK:確認(rèn)號(hào)是否有效
PSH:提示接收端應(yīng)用程序立刻從TCP緩沖區(qū)把數(shù)據(jù)讀走
RST:對方要求重新建立連接;我們把攜帶RST標(biāo)識(shí)的稱為復(fù)位報(bào)文段
SYN:請求建立連接;我們把攜帶SYN標(biāo)識(shí)的稱為同步報(bào)文段
FIN:通知對方,本端要關(guān)閉了,我們稱攜帶FIN標(biāo)識(shí)的為結(jié)束報(bào)文段 -
16位窗口大?。汉竺嬖僬f
-
16位校驗(yàn)和:發(fā)送端填充,CRC校驗(yàn)。接收端校驗(yàn)不通過,則認(rèn)為數(shù)據(jù)有問題。此處的檢驗(yàn)和不
光包含TCP首部,也包含TCP數(shù)據(jù)部分 -
16位緊急指針:標(biāo)識(shí)哪部分?jǐn)?shù)據(jù)是緊急數(shù)據(jù);
-
40字節(jié)頭部選項(xiàng):這里博主不做講解
??TCP的特性
-
TCP提供一種面向連接的, 可靠的字節(jié)流服務(wù);
-
在一個(gè)TCP連接中,僅有兩方進(jìn)行彼此通信。廣播和多播不能用于TCP;
-
TCP使用校驗(yàn)和, 確認(rèn)和重傳機(jī)制來保證可靠傳輸;
-
TCP使用累積確認(rèn)
-
TCP使用滑動(dòng)窗口機(jī)制來實(shí)現(xiàn)流量控制,通過動(dòng)態(tài)改變窗口的大小進(jìn)行擁塞控制
上述特性,在下面的TCP原理里面回進(jìn)行一一介紹
??TCP原理
TCP對數(shù)據(jù)傳輸提供的管控機(jī)制,主要體現(xiàn)在兩個(gè)方面:安全和效率。
這些機(jī)制和多線程的設(shè)計(jì)原則類似:保證數(shù)據(jù)傳輸安全的前提下,盡可能的提高傳輸效率。
??確認(rèn)應(yīng)答機(jī)制(安全機(jī)制)
確認(rèn)應(yīng)答機(jī)制圖示如下
單看這一幅圖還是比較懵逼的,接下來我為大家解答一下
我們知道TCP是屬于可靠傳輸,它就為了解決UDP不可靠傳輸而發(fā)明的。我們有了確認(rèn)應(yīng)答機(jī)制后,我們每發(fā)送一個(gè)消息,都能收到對方的一個(gè)回應(yīng),確保自己知道自己的消息發(fā)過去了。
就像一個(gè)小伙子給他女神發(fā)消息說“我請你吃飯好嗎?”,然后收到了女神的回復(fù)“好啊好啊”,小伙的請求收到了確認(rèn)
在這個(gè)傳輸過程中了,我們用上述提到的ACK來表示請求和應(yīng)答報(bào)文
-
ACK=0,表示是發(fā)送報(bào)文
-
ACK=1,表示是應(yīng)答報(bào)文
但是光有確認(rèn)應(yīng)答還是不夠,比如出現(xiàn)以下情況
小伙子對女神說“我請你吃飯好嗎?”,然后這時(shí)候女生還沒有回復(fù),然后小伙又發(fā)了一句“做我女朋友好嗎?”,這時(shí)候女神回消息了,回了兩句為“滾”,“好啊好啊”
那么這時(shí)候的小伙就懵了,小伙就想
- 女神是現(xiàn)在不想吃飯,給我發(fā)了一句滾,她還是愿意做為我的女朋友的
- 女神不想做我女朋友,但是想和一起吃飯
這時(shí)候的小伙也就迷茫了,女神到底什么意思呢?
這時(shí)候我們?yōu)榱私鉀Q這一類問題,我們引入一個(gè)序號(hào)和確認(rèn)序號(hào),發(fā)送數(shù)據(jù)是帶上序號(hào),確認(rèn)應(yīng)答時(shí)也帶上一個(gè)確認(rèn)序號(hào)一一對應(yīng)
做法不同的是
TCP將每個(gè)字節(jié)的數(shù)據(jù)都進(jìn)行了編號(hào)。即為序列號(hào)
每一個(gè)ACK都帶有對應(yīng)的確認(rèn)序列號(hào),意思是告訴發(fā)送者,我已經(jīng)收到了哪些數(shù)據(jù);下一次你從哪里開始發(fā)。
這樣就保證了確認(rèn)應(yīng)答不會(huì)出錯(cuò)
??超時(shí)重傳機(jī)制(安全機(jī)制)
有了確認(rèn)應(yīng)答后,我們的可靠性已經(jīng)得到大大的提升,但是UDP中出現(xiàn)的丟包問題還是沒有得到解決
意思也就是主機(jī)A向主機(jī)B發(fā)送數(shù)據(jù),一定時(shí)間后,并沒有收到應(yīng)答,這里主機(jī)A沒有收到應(yīng)答報(bào)文有兩種情況
- 主機(jī)A發(fā)送數(shù)據(jù)給B之后,可能因?yàn)榫W(wǎng)絡(luò)擁堵等原因,數(shù)據(jù)無法到達(dá)主機(jī)B
- 主機(jī)B收到主機(jī)A的數(shù)據(jù)之后,做出應(yīng)答后,應(yīng)答報(bào)文沒有到達(dá)主機(jī)A
這兩種情況都當(dāng)成第一種情況處理,客戶端會(huì)進(jìn)行重傳數(shù)據(jù)
這時(shí)候有的人可能會(huì)想,如果是第一種情況,重傳數(shù)據(jù)就好,但是如果出現(xiàn)第二種情況,那么這些數(shù)據(jù)不是相同了嗎,不就出現(xiàn)數(shù)據(jù)重復(fù)了嗎?
這時(shí)候我們可以利用前面提到的序列號(hào),其實(shí)我們這里的主機(jī)B這里接收就像一個(gè)優(yōu)先級的隊(duì)列,我們會(huì)對傳來的數(shù)據(jù)按照序列號(hào)進(jìn)行排序,如果序列號(hào)相同,該隊(duì)列還可以起到一個(gè)去重的效果
那么我們又會(huì)想超時(shí)的時(shí)間如何確定?
-
最理想的情況下,找到一個(gè)最小的時(shí)間,保證 “確認(rèn)應(yīng)答一定能在這個(gè)時(shí)間內(nèi)返回”。
-
但是這個(gè)時(shí)間的長短,隨著網(wǎng)絡(luò)環(huán)境的不同,是有差異的。
-
如果超時(shí)時(shí)間設(shè)的太長,會(huì)影響整體的重傳效率;
-
如果超時(shí)時(shí)間設(shè)的太短,有可能會(huì)頻繁發(fā)送重復(fù)的包;
TCP為了保證無論在任何環(huán)境下都能比較高性能的通信,因此會(huì)動(dòng)態(tài)計(jì)算這個(gè)最大超時(shí)時(shí)間
- Linux中(BSD Unix和Windows也是如此),超時(shí)以500ms為一個(gè)單位進(jìn)行控制,每次判定超時(shí)重發(fā)的超時(shí)時(shí)間都是500ms的整數(shù)倍。
- 如果重發(fā)一次之后,仍然得不到應(yīng)答,等待 2*500ms 后再進(jìn)行重傳。
- 如果仍然得不到應(yīng)答,等待 4*500ms 進(jìn)行重傳。依次類推,以指數(shù)形式遞增。
- 累計(jì)到一定的重傳次數(shù),TCP認(rèn)為網(wǎng)絡(luò)或者對端主機(jī)出現(xiàn)異常,強(qiáng)制關(guān)閉連接。
??三次握手四次揮手(安全機(jī)制)
由于這里內(nèi)容較多,我單獨(dú)寫了一篇進(jìn)行介紹,大家可以博主寫的《【JavaEE初階】 TCP三次握手四次揮手(超詳細(xì)版)》進(jìn)行學(xué)習(xí)觀看。
??滑動(dòng)窗口(效率機(jī)制)
關(guān)于該部分的內(nèi)容博主在《【JavaEE初階】 TCP滑動(dòng)窗口與流量控制和擁塞控制》有詳細(xì)講解。
??流量控制(安全機(jī)制)
關(guān)于該部分的內(nèi)容博主在《【JavaEE初階】 TCP滑動(dòng)窗口與流量控制和擁塞控制》有詳細(xì)講解。
??擁塞控制(安全機(jī)制)
關(guān)于該部分的內(nèi)容博主在《【JavaEE初階】 TCP滑動(dòng)窗口與流量控制和擁塞控制》有詳細(xì)講解。
??延遲應(yīng)答(效率機(jī)制)
如果接收數(shù)據(jù)的主機(jī)立刻返回ACK應(yīng)答,這時(shí)候返回的窗口可能比較小
- 假設(shè)接收端緩沖區(qū)為1M。一次收到了500K的數(shù)據(jù);如果立刻應(yīng)答,返回的窗口就是500K;
- 但實(shí)際上可能處理端處理的速度很快,10ms之內(nèi)就把500K數(shù)據(jù)從緩沖區(qū)消費(fèi)掉了;
- 在這種情況下,接收端處理還遠(yuǎn)沒有達(dá)到自己的極限,即使窗口再放大一些,也能處理過來;
- 如果接收端稍微等一會(huì)再應(yīng)答,比如等待200ms再應(yīng)答,那么這個(gè)時(shí)候返回的口大小就是1M;
注意:一定要記得,窗口越大,網(wǎng)絡(luò)吞吐量就越大,傳輸效率就越高。我們的目標(biāo)是在保證網(wǎng)絡(luò)不擁塞的情況下盡量提高傳輸效率;
那么所有的包都可以延遲應(yīng)答么?肯定也不是,是存在一定限制的
- 數(shù)量限制:每隔N個(gè)包就應(yīng)答一次;
- 時(shí)間限制:超過最大延遲時(shí)間就應(yīng)答一次;
具體的數(shù)量和超時(shí)時(shí)間,依操作系統(tǒng)不同也有差異;一般N取2,超時(shí)時(shí)間取200ms
??捎帶應(yīng)答(效率機(jī)制)
在延遲應(yīng)答的基礎(chǔ)上,我們發(fā)現(xiàn),很多情況下,客戶端服務(wù)器在應(yīng)用層也是 “一發(fā)一收” 的。
意味著客戶端給服務(wù)器說了 “How are you”,服務(wù)器也會(huì)給客戶端回一個(gè) “Fine, thank you”;
那么這個(gè)時(shí)候ACK就可以搭順風(fēng)車,和服務(wù)器回應(yīng)的 “Fine,thank you” 一起回給客戶端
??面向字節(jié)流的粘包問題
首先我們需要明確的是
我們在開發(fā)中,創(chuàng)建一個(gè)TCP的socket,同時(shí)在內(nèi)核中創(chuàng)建一個(gè) 發(fā)送緩沖區(qū) 和一個(gè) 接收緩沖區(qū)
- 調(diào)用write時(shí),數(shù)據(jù)會(huì)先寫入發(fā)送緩沖區(qū)中;
- 如果發(fā)送的字節(jié)數(shù)太長,會(huì)被拆分成多個(gè)TCP的數(shù)據(jù)包發(fā)出;
- 如果發(fā)送的字節(jié)數(shù)太短,就會(huì)先在緩沖區(qū)里等待,等到緩沖區(qū)長度差不多了,或者其他合適的時(shí)機(jī)發(fā)送出去;
- 接收數(shù)據(jù)的時(shí)候,數(shù)據(jù)也是從網(wǎng)卡驅(qū)動(dòng)程序到達(dá)內(nèi)核的接收緩沖區(qū);
- 然后應(yīng)用程序可以調(diào)用read從接收緩沖區(qū)拿數(shù)據(jù);
- 另一方面,TCP的一個(gè)連接,既有發(fā)送緩沖區(qū),也有接收緩沖區(qū),那么對于這一個(gè)連接,既可以讀數(shù)據(jù),也可以寫數(shù)據(jù)。這個(gè)概念叫做 全雙工
由于緩沖區(qū)的存在,TCP程序的讀和寫不需要一一匹配,例如:
- 寫100個(gè)字節(jié)數(shù)據(jù)時(shí),可以調(diào)用一次write寫100個(gè)字節(jié),也可以調(diào)用100次write,每次寫一個(gè)字節(jié);
- 讀100個(gè)字節(jié)數(shù)據(jù)時(shí),也完全不需要考慮寫的時(shí)候是怎么寫的,既可以一次read 100個(gè)字節(jié),也可以一次read一個(gè)字節(jié),重復(fù)100次;
那什么是粘包問題呢?
首先要明確,粘包問題中的 “包” ,是指的應(yīng)用層的數(shù)據(jù)包。
在TCP的協(xié)議頭中,沒有如同UDP一樣的 “報(bào)文長度” 這樣的字段,但是有一個(gè)序號(hào)這樣的字段。
站在傳輸層的角度,TCP是一個(gè)一個(gè)報(bào)文過來的。按照序號(hào)排好序放在緩沖區(qū)中。
站在應(yīng)用層的角度,看到的只是一串連續(xù)的字節(jié)數(shù)據(jù)。
那么應(yīng)用程序看到了這么一連串的字節(jié)數(shù)據(jù),就不知道從哪個(gè)部分開始到哪個(gè)部分,是一個(gè)完整的應(yīng)用層數(shù)據(jù)包。
那么如何避免粘包問題呢?歸根結(jié)底就是一句話,明確兩個(gè)包之間的邊界
- 對于定長的包,保證每次都按固定大小讀取即可;例如上面的Request結(jié)構(gòu),是固定大小的,那么就從緩沖區(qū)從頭開始按sizeof(Request)依次讀取即可;
- 對于變長的包,可以在包頭的位置,約定一個(gè)包總長度的字段,從而就知道了包的結(jié)束位置;
- 對于變長的包,還可以在包和包之間使用明確的分隔符(應(yīng)用層協(xié)議,是程序猿自己來定的,只要保證分隔符不和正文沖突即可)
??思考:
對于UDP協(xié)議來說,是否也存在 “粘包問題” 呢?
- 對于UDP,如果還沒有上層交付數(shù)據(jù),UDP的報(bào)文長度仍然在。同時(shí),UDP是一個(gè)一個(gè)把數(shù)據(jù)交付給應(yīng)用層。就有很明確的數(shù)據(jù)邊界。
- 站在應(yīng)用層的站在應(yīng)用層的角度,使用UDP的時(shí)候,要么收到完整的UDP報(bào)文,要么不收。不會(huì)出現(xiàn)"半個(gè)"的情況。
??TCP異常情況
進(jìn)程終止:進(jìn)程終止會(huì)釋放文件描述符,仍然可以發(fā)送FIN。和正常關(guān)閉沒有什么區(qū)別。
機(jī)器重啟:和進(jìn)程終止的情況相同。
機(jī)器掉電/網(wǎng)線斷開:接收端認(rèn)為連接還在,一旦接收端有寫入操作,接收端發(fā)現(xiàn)連接已經(jīng)不在了,就會(huì)進(jìn)行reset。即使沒有寫入操作,TCP自己也內(nèi)置了一個(gè)保活定時(shí)器,會(huì)定期詢問對方是否還在。
如果對方不在,也會(huì)把連接釋放。另外,應(yīng)用層的某些協(xié)議,也有一些這樣的檢測機(jī)制。
例如HTTP長連接中,也會(huì)定期檢測對方的狀態(tài)。例如QQ,在QQ斷線之后,也會(huì)定期嘗試重新連接
??TCP小結(jié)
為什么TCP這么復(fù)雜?因?yàn)橐WC可靠性,同時(shí)又盡可能的提高性能。
-
可靠性:
-
- 校驗(yàn)和
-
- 序列號(hào)(按序到達(dá))
-
- 確認(rèn)應(yīng)答
-
- 超時(shí)重發(fā)
-
- 連接管理
-
- 流量控制
-
- 擁塞控制
-
提高性能:
-
- 滑動(dòng)窗口
-
- 快速重傳
-
- 延遲應(yīng)答
-
- 捎帶應(yīng)答
-
其他:
-
- 定時(shí)器(超時(shí)重傳定時(shí)器,?;疃〞r(shí)器,TIME_WAIT定時(shí)器等)
??基于TCP應(yīng)用層協(xié)議
-
HTTP
-
HTTPS
-
SSH
-
elnet
-
FTP
-
SMTP
當(dāng)然,也包括你自己寫TCP程序時(shí)自定義的應(yīng)用層協(xié)議;文章來源:http://www.zghlxwxcb.cn/news/detail-753268.html
?總結(jié)
關(guān)于《【JavaEE初階】 TCP協(xié)議詳細(xì)解析》就講解到這兒,感謝大家的支持,歡迎各位留言交流以及批評指正,如果文章對您有幫助或者覺得作者寫的還不錯(cuò)可以點(diǎn)一下關(guān)注,點(diǎn)贊,收藏支持一下!文章來源地址http://www.zghlxwxcb.cn/news/detail-753268.html
到了這里,關(guān)于【JavaEE初階】 TCP協(xié)議詳細(xì)解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!