本文介紹TCP的三次握手與四次揮手,以及TCP 為什么是三次握手羡蛾,而不是兩次或四次评架?
TCP名為傳輸控制協(xié)議眷茁,是一種可靠傳輸控制協(xié)議,其核心思想:既要保證數(shù)據(jù)可靠傳輸纵诞,又要提高傳輸?shù)男噬掀恚萌挝帐终脻M足以上兩方面的需求!
TCP報(bào)文格式
- 源端口和目的端口浙芙,各占2個(gè)字節(jié)登刺,分別寫入源端口和目的端口;
- 序號嗡呼,占4個(gè)字節(jié)纸俭,TCP連接中傳送的字節(jié)流中的每個(gè)字節(jié)都按順序編號。例如南窗,一段報(bào)文的序號字段值是 301 揍很,而攜帶的數(shù)據(jù)共有100字段郎楼,顯然下一個(gè)報(bào)文段(如果還有的話)的數(shù)據(jù)序號應(yīng)該從401開始;
- 確認(rèn)號窒悔,占4個(gè)字節(jié)呜袁,是期望收到對方下一個(gè)報(bào)文的第一個(gè)數(shù)據(jù)字節(jié)的序號。例如简珠,B收到了A發(fā)送過來的報(bào)文阶界,其序列號字段是501,而數(shù)據(jù)長度是200字節(jié)聋庵,這表明B正確的收到了A發(fā)送的到序號700為止的數(shù)據(jù)荐操。因此,B期望收到A的下一個(gè)數(shù)據(jù)序號是701珍策,于是B在發(fā)送給A的確認(rèn)報(bào)文段中把確認(rèn)號置為701;
- 數(shù)據(jù)偏移宅倒,占4位攘宙,它指出TCP報(bào)文的數(shù)據(jù)距離TCP報(bào)文段的起始處有多遠(yuǎn);
- 保留拐迁,占6位蹭劈,保留今后使用,但目前應(yīng)都位0线召;
- 緊急URG铺韧,當(dāng)URG=1,表明緊急指針字段有效缓淹。告訴系統(tǒng)此報(bào)文段中有緊急數(shù)據(jù)哈打;
- 確認(rèn)ACK,僅當(dāng)ACK=1時(shí)讯壶,確認(rèn)號字段才有效料仗。TCP規(guī)定绽慈,在連接建立后所有報(bào)文的傳輸都必須把ACK置1吗货;
- 推送PSH,當(dāng)兩個(gè)應(yīng)用進(jìn)程進(jìn)行交互式通信時(shí)草姻,有時(shí)在一端的應(yīng)用進(jìn)程希望在鍵入一個(gè)命令后立即就能收到對方的響應(yīng)躏吊,這時(shí)候就將PSH=1氛改;
- 復(fù)位RST,當(dāng)RST=1比伏,表明TCP連接中出現(xiàn)嚴(yán)重差錯(cuò)胜卤,必須釋放連接,然后再重新建立連接赁项;
- 同步SYN瑰艘,在連接建立時(shí)用來同步序號是鬼。當(dāng)SYN=1,ACK=0紫新,表明是連接請求報(bào)文均蜜,若同意連接,則響應(yīng)報(bào)文中應(yīng)該使SYN=1芒率,ACK=1囤耳;
- 終止FIN,用來釋放連接偶芍。當(dāng)FIN=1充择,表明此報(bào)文的發(fā)送方的數(shù)據(jù)已經(jīng)發(fā)送完畢,并且要求釋放匪蟀;
- 窗口椎麦,占2字節(jié),指的是通知接收方材彪,發(fā)送本報(bào)文你需要有多大的空間來接受观挎;
- 檢驗(yàn)和,占2字節(jié)段化,校驗(yàn)首部和數(shù)據(jù)這兩部分嘁捷;
- 緊急指針,占2字節(jié)显熏,指出本報(bào)文段中的緊急數(shù)據(jù)的字節(jié)數(shù)雄嚣;
- 選項(xiàng),長度可變喘蟆,定義一些其他的可選的參數(shù)缓升。
以下是TCP的6種標(biāo)志位和2種序號:
- SYN(synchronous) :建立連接,請求同步標(biāo)志蕴轨,為1的時(shí)候?yàn)橛行?/li>
- ACK(acknowledgement 確認(rèn)) :應(yīng)答標(biāo)志仔沿,表示接受到所發(fā)的數(shù)據(jù),1為有效
- PSH(push傳送) :接收方應(yīng)該盡快把這個(gè)報(bào)文交給應(yīng)用層
- FIN(finish結(jié)束) :釋放一個(gè)連接
- RST(reset) :重置連接
- URG(urgent緊急):緊急指針有效
- 順序序號(sequence number):seq序號尺棋,占32位封锉,用來標(biāo)識從TCP源端向目的端發(fā)送的字節(jié)流,發(fā)起方發(fā)送數(shù)據(jù)時(shí)對此進(jìn)行標(biāo)記膘螟。
- 確認(rèn)序號(acknowledge number):ack序號成福,占32位,只有標(biāo)志位ACK為1時(shí)荆残,確認(rèn)序號字段才有效奴艾,Ack=Seq+1。
需要注意的是:
- 不要將確認(rèn)序號ack與標(biāo)志位中的ACK搞混了内斯。
- 確認(rèn)方ack=發(fā)起方seq+1蕴潦,兩端配對像啼。
TCP可靠傳輸?shù)木瑁篢CP連接的一方A,由操作系統(tǒng)動(dòng)態(tài)隨機(jī)選取一個(gè)32位長的序列號(Initial Sequence Number)潭苞,假設(shè)A的初始序列號為1000忽冻,以該序列號為原點(diǎn),對自己將要發(fā)送的每個(gè)字節(jié)的數(shù)據(jù)進(jìn)行編號此疹,1001僧诚,1002,1003…蝗碎,并把自己的初始序列號ISN告訴B湖笨,讓B有一個(gè)思想準(zhǔn)備,什么樣編號的數(shù)據(jù)是合法的蹦骑,什么編號是非法的慈省,比如編號900就是非法的,同時(shí)B還可以對A每一個(gè)編號的字節(jié)數(shù)據(jù)進(jìn)行確認(rèn)眠菇。如果A收到B確認(rèn)編號為2001边败,則意味著字節(jié)編號為1001-2000,共1000個(gè)字節(jié)已經(jīng)安全到達(dá)琼锋。
同理B也是類似的操作,假設(shè)B的初始序列號ISN為2000祟昭,以該序列號為原點(diǎn)缕坎,對自己將要發(fā)送的每個(gè)字節(jié)的數(shù)據(jù)進(jìn)行編號,2001篡悟,2002谜叹,2003…,并把自己的初始序列號ISN告訴A搬葬,以便A可以確認(rèn)B發(fā)送的每一個(gè)字節(jié)荷腊。如果B收到A確認(rèn)編號為4001,則意味著字節(jié)編號為2001-4000急凰,共2000個(gè)字節(jié)已經(jīng)安全到達(dá)女仰。
一句話概括,TCP連接握手抡锈,握的是啥疾忍?通信雙方數(shù)據(jù)原點(diǎn)的序列號!以此核心思想我們來分析二床三、三一罩、四次握手的過程。
四次握手的過程:
1.1 A 發(fā)送同步信號SYN + A's Initial sequence number
1.2 B 確認(rèn)收到A的同步信號撇簿,并記錄 A's ISN 到本地聂渊,命名 B's ACK sequence number
1.3 B發(fā)送同步信號SYN + B's Initial sequence number
1.4 A確認(rèn)收到B的同步信號差购,并記錄 B's ISN 到本地,命名A's ACK sequence number很顯然1.2和1.3 這兩個(gè)步驟可以合并汉嗽,只需要三次握手欲逃,可以提高連接的速度與效率。
二次握手的過程:
- A 發(fā)送同步信號SYN + A's Initial sequence number
- B發(fā)送同步信號SYN + B's Initial sequence number + B's ACK sequence number這里有一個(gè)問題诊胞,A與B就A的初始序列號達(dá)成了一致暖夭,這里是1000。但是B無法知道A是否已經(jīng)接收到自己的同步信號撵孤,如果這個(gè)同步信號丟失了迈着,A和B就B的初始序列號將無法達(dá)成一致。
于是TCP的設(shè)計(jì)者將SYN這個(gè)同步標(biāo)志位SYN設(shè)計(jì)成占用一個(gè)字節(jié)的編號(FIN標(biāo)志位也是)邪码,既然是一個(gè)字節(jié)的數(shù)據(jù)裕菠,按照TCP對有數(shù)據(jù)的TCP segment 必須確認(rèn)的原則,所以在這里A必須給B一個(gè)確認(rèn)闭专,以確認(rèn)A已經(jīng)接收到B的同步信號奴潘。
有童鞋會(huì)說,如果A發(fā)給B的確認(rèn)丟了影钉,該如何画髓?A會(huì)超時(shí)重傳這個(gè)ACK嗎?不會(huì)平委!TCP不會(huì)為沒有數(shù)據(jù)的ACK超時(shí)重傳奈虾。
那該如何是好?B如果沒有收到A的ACK廉赔,會(huì)超時(shí)重傳自己的SYN同步信號肉微,一直到收到A的ACK為止。
三次握手的過程:
第一次握手:Client將標(biāo)志位SYN置為1蜡塌,隨機(jī)產(chǎn)生一個(gè)值seq=J碉纳,并將該數(shù)據(jù)包發(fā)送給Server,Client進(jìn)入SYN_SENT狀態(tài)馏艾,等待Server確認(rèn)劳曹。
第二次握手:Server收到數(shù)據(jù)包后由標(biāo)志位SYN=1知道Client請求建立連接,Server將標(biāo)志位SYN和ACK都置為1琅摩,ack (number )=J+1厚者,隨機(jī)產(chǎn)生一個(gè)值seq=K,并將該數(shù)據(jù)包發(fā)送給Client以確認(rèn)連接請求迫吐,Server進(jìn)入SYN_RCVD狀態(tài)库菲。
-
第三次握手:Client收到確認(rèn)后,檢查ack是否為J+1志膀,ACK是否為1熙宇,如果正確則將標(biāo)志位ACK置為1鳖擒,ack=K+1,并將該數(shù)據(jù)包發(fā)送給Server烫止,Server檢查ack是否為K+1蒋荚,ACK是否為1,如果正確則連接建立成功馆蠕,Client和Server進(jìn)入ESTABLISHED狀態(tài)期升,完成三次握手,隨后Client與Server之間可以開始傳輸數(shù)據(jù)了互躬。
四次揮手的過程
- 客戶端進(jìn)程發(fā)出連接釋放報(bào)文播赁,并且停止發(fā)送數(shù)據(jù)。釋放數(shù)據(jù)報(bào)文首部吼渡,F(xiàn)IN=1容为,其序列號為seq=u(等于前面已經(jīng)傳送過來的數(shù)據(jù)的最后一個(gè)字節(jié)的序號加1),此時(shí)寺酪,客戶端進(jìn)入FIN-WAIT-1(終止等待1)狀態(tài)坎背。 TCP規(guī)定,F(xiàn)IN報(bào)文段即使不攜帶數(shù)據(jù)寄雀,也要消耗一個(gè)序號得滤。
- 服務(wù)器收到連接釋放報(bào)文,發(fā)出確認(rèn)報(bào)文盒犹,ACK=1懂更,ack=u+1,并且?guī)献约旱男蛄刑杝eq=v阿趁,此時(shí)膜蛔,服務(wù)端就進(jìn)入了CLOSE-WAIT(關(guān)閉等待)狀態(tài)坛猪。TCP服務(wù)器通知高層的應(yīng)用進(jìn)程脖阵,客戶端向服務(wù)器的方向就釋放了,這時(shí)候處于半關(guān)閉狀態(tài)墅茉,即客戶端已經(jīng)沒有數(shù)據(jù)要發(fā)送了命黔,但是服務(wù)器若發(fā)送數(shù)據(jù),客戶端依然要接受就斤。這個(gè)狀態(tài)還要持續(xù)一段時(shí)間悍募,也就是整個(gè)CLOSE-WAIT狀態(tài)持續(xù)的時(shí)間。
- 客戶端收到服務(wù)器的確認(rèn)請求后洋机,此時(shí)坠宴,客戶端就進(jìn)入FIN-WAIT-2(終止等待2)狀態(tài),等待服務(wù)器發(fā)送連接釋放報(bào)文(在這之前還需要接受服務(wù)器發(fā)送的最后的數(shù)據(jù))绷旗。
- 服務(wù)器將最后的數(shù)據(jù)發(fā)送完畢后喜鼓,就向客戶端發(fā)送連接釋放報(bào)文副砍,F(xiàn)IN=1,ack=u+1庄岖,由于在半關(guān)閉狀態(tài)豁翎,服務(wù)器很可能又發(fā)送了一些數(shù)據(jù),假定此時(shí)的序列號為seq=w隅忿,此時(shí)心剥,服務(wù)器就進(jìn)入了LAST-ACK(最后確認(rèn))狀態(tài),等待客戶端的確認(rèn)背桐。
- 客戶端收到服務(wù)器的連接釋放報(bào)文后优烧,必須發(fā)出確認(rèn),ACK=1牢撼,ack=w+1匙隔,而自己的序列號是seq=u+1,此時(shí)熏版,客戶端就進(jìn)入了TIME-WAIT(時(shí)間等待)狀態(tài)纷责。*注意此時(shí)TCP連接還沒有釋放,必須經(jīng)過2? ?MSL(最長報(bào)文段壽命)的時(shí)間后撼短,當(dāng)客戶端撤銷相應(yīng)的TCB后再膳,才進(jìn)入CLOSED狀態(tài)。
- 服務(wù)器只要收到了客戶端發(fā)出的確認(rèn)曲横,立即進(jìn)入CLOSED狀態(tài)喂柒。同樣,撤銷TCB后禾嫉,就結(jié)束了這次的TCP連接灾杰。可以看到,服務(wù)器結(jié)束TCP連接的時(shí)間要比客戶端早一些熙参。
FAQ:
- 第一個(gè)包艳吠,即A發(fā)給B的SYN 中途被丟,沒有到達(dá)B
A會(huì)周期性超時(shí)重傳孽椰,直到收到B的確認(rèn) - 第二個(gè)包昭娩,即B發(fā)給A的SYN +ACK 中途被丟,沒有到達(dá)A
B會(huì)周期性超時(shí)重傳黍匾,直到收到A的確認(rèn) - 第三個(gè)包栏渺,即A發(fā)給B的ACK 中途被丟,沒有到達(dá)BA發(fā)完ACK锐涯,單方面認(rèn)為TCP為 Established狀態(tài)磕诊,而B顯然認(rèn)為TCP為Active狀態(tài):
- 假定此時(shí)雙方都沒有數(shù)據(jù)發(fā)送,B會(huì)周期性超時(shí)重傳,直到收到A的確認(rèn)霎终,收到之后B的TCP 連接也為 Established狀態(tài)融痛,雙向可以發(fā)包。
- 假定此時(shí)A有數(shù)據(jù)發(fā)送神僵,B收到A的 Data + ACK雁刷,自然會(huì)切換為established 狀態(tài),并接受A的 Data保礼。
- 假定B有數(shù)據(jù)發(fā)送沛励,數(shù)據(jù)發(fā)送不了,會(huì)一直周期性超時(shí)重傳SYN + ACK炮障,直到收到A的確認(rèn)才可以發(fā)送數(shù)據(jù)目派。
延伸閱讀
TCP工作在網(wǎng)絡(luò)OSI的七層模型中的第四層—Transport層,IP在第三層—Network層胁赢。
數(shù)據(jù)從應(yīng)用層發(fā)下來企蹭,會(huì)在每一層都會(huì)加上頭部信息,進(jìn)行封裝智末,然后再發(fā)送到數(shù)據(jù)接收端谅摄。這個(gè)基本的流程你需要知道,就是每個(gè)數(shù)據(jù)都會(huì)經(jīng)過數(shù)據(jù)的封裝和解封裝的過程系馆。 在OSI七層模型中送漠,每一層的作用和對應(yīng)的協(xié)議如下:
常見面試題
【問題1】為什么連接的時(shí)候是三次握手,關(guān)閉的時(shí)候卻是四次握手由蘑?
答:因?yàn)楫?dāng)Server端收到Client端的SYN連接請求報(bào)文后闽寡,可以直接發(fā)送SYN+ACK報(bào)文。其中ACK報(bào)文是用來應(yīng)答的尼酿,SYN報(bào)文是用來同步的爷狈。但是關(guān)閉連接時(shí),當(dāng)Server端收到FIN報(bào)文時(shí)裳擎,很可能并不會(huì)立即關(guān)閉SOCKET涎永,所以只能先回復(fù)一個(gè)ACK報(bào)文,告訴Client端句惯,"你發(fā)的FIN報(bào)文我收到了"土辩。只有等到我Server端所有的報(bào)文都發(fā)送完了支救,我才能發(fā)送FIN報(bào)文抢野,因此不能一起發(fā)送。故需要四步握手各墨。
【問題2】為什么TIME_WAIT狀態(tài)需要經(jīng)過2MSL(最大報(bào)文段生存時(shí)間)才能返回到CLOSE狀態(tài)指孤?
答:雖然按道理,四個(gè)報(bào)文都發(fā)送完畢,我們可以直接進(jìn)入CLOSE狀態(tài)了恃轩,但是我們必須假象網(wǎng)絡(luò)是不可靠的结洼,有可以最后一個(gè)ACK丟失。所以TIME_WAIT狀態(tài)就是用來重發(fā)可能丟失的ACK報(bào)文叉跛。在Client發(fā)送出最后的ACK回復(fù)松忍,但該ACK可能丟失。Server如果沒有收到ACK筷厘,將不斷重復(fù)發(fā)送FIN片段鸣峭。所以Client不能立即關(guān)閉,它必須確認(rèn)Server接收到了該ACK酥艳。Client會(huì)在發(fā)送出ACK之后進(jìn)入到TIME_WAIT狀態(tài)摊溶。Client會(huì)設(shè)置一個(gè)計(jì)時(shí)器,等待2MSL的時(shí)間充石。如果在該時(shí)間內(nèi)再次收到FIN莫换,那么Client會(huì)重發(fā)ACK并再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)骤铃。MSL指一個(gè)片段在網(wǎng)絡(luò)中最大的存活時(shí)間拉岁,2MSL就是一個(gè)發(fā)送和一個(gè)回復(fù)所需的最大時(shí)間。如果直到2MSL惰爬,Client都沒有再次收到FIN膛薛,那么Client推斷ACK已經(jīng)被成功接收,則結(jié)束TCP連接补鼻。
【問題3】為什么不能用兩次握手進(jìn)行連接哄啄?
答:3次握手完成兩個(gè)重要的功能,既要雙方做好發(fā)送數(shù)據(jù)的準(zhǔn)備工作(雙方都知道彼此已準(zhǔn)備好)风范,也要允許雙方就初始序列號進(jìn)行協(xié)商咨跌,這個(gè)序列號在握手過程中被發(fā)送和確認(rèn)。
現(xiàn)在把三次握手改成僅需要兩次握手硼婿,死鎖是可能發(fā)生的锌半。作為例子,考慮計(jì)算機(jī)S和C之間的通信寇漫,假定C給S發(fā)送一個(gè)連接請求分組刊殉,S收到了這個(gè)分組,并發(fā) 送了確認(rèn)應(yīng)答分組州胳。按照兩次握手的協(xié)定记焊,S認(rèn)為連接已經(jīng)成功地建立了,可以開始發(fā)送數(shù)據(jù)分組栓撞”槟ぃ可是碗硬,C在S的應(yīng)答分組在傳輸中被丟失的情況下,將不知道S 是否已準(zhǔn)備好瓢颅,不知道S建立什么樣的序列號恩尾,C甚至懷疑S是否收到自己的連接請求分組。在這種情況下挽懦,C認(rèn)為連接還未建立成功翰意,將忽略S發(fā)來的任何數(shù)據(jù)分 組,只等待連接確認(rèn)應(yīng)答分組信柿。而S在發(fā)出的分組超時(shí)后猎物,重復(fù)發(fā)送同樣的分組。這樣就形成了死鎖角塑。
【問題4】如果已經(jīng)建立了連接蔫磨,但是客戶端突然出現(xiàn)故障了怎么辦?
TCP還設(shè)有一個(gè)逼粤妫活計(jì)時(shí)器堤如,顯然,客戶端如果出現(xiàn)故障窒朋,服務(wù)器不能一直等下去搀罢,白白浪費(fèi)資源。服務(wù)器每收到一次客戶端的請求后都會(huì)重新復(fù)位這個(gè)計(jì)時(shí)器侥猩,時(shí)間通常是設(shè)置為2小時(shí)榔至,若兩小時(shí)還沒有收到客戶端的任何數(shù)據(jù),服務(wù)器就會(huì)發(fā)送一個(gè)探測報(bào)文段欺劳,以后每隔75秒鐘發(fā)送一次唧取。若一連發(fā)送10個(gè)探測報(bào)文仍然沒反應(yīng),服務(wù)器就認(rèn)為客戶端出了故障划提,接著就關(guān)閉連接枫弟。
參考鏈接
https://mp.weixin.qq.com/s/NIjxgx4NPn7FC4PfkHBAAQ
https://blog.csdn.net/huangshulang1234/article/details/79061438
https://blog.csdn.net/qzcsu/article/details/72861891
https://blog.csdn.net/qq_38950316/article/details/81087809