一文徹底搞懂 TCP三次握手虐先、四次揮手過程及原理

TCP 協(xié)議簡述

TCP 提供面向有連接的通信傳輸,面向有連接是指在傳送數(shù)據(jù)之前必須先建立連接派敷,數(shù)據(jù)傳送完成后要釋放連接蛹批。

無論哪一方向另一方發(fā)送數(shù)據(jù)之前,都必須先在雙方之間建立一條連接。在TCP/IP協(xié)議中般眉,TCP協(xié)議提供可靠的連接服務(wù)了赵,連接是通過三次握手進(jìn)行初始化的。
同時由于TCP協(xié)議是一種面向連接的甸赃、可靠的柿汛、基于字節(jié)流的運(yùn)輸層通信協(xié)議,TCP是全雙工模式埠对,所以需要四次揮手關(guān)閉連接络断。

TCP包首部

網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)包由兩部分組成:一部分是協(xié)議所要用到的首部,另一部分是上一層傳過來的數(shù)據(jù)项玛。首部的結(jié)構(gòu)由協(xié)議的具體規(guī)范詳細(xì)定義貌笨。在數(shù)據(jù)包的首部,明確標(biāo)明了協(xié)議應(yīng)該如何讀取數(shù)據(jù)襟沮。反過來說锥惋,看到首部,也就能夠了解該協(xié)議必要的信息以及所要處理的數(shù)據(jù)开伏。包首部就像協(xié)議的臉膀跌。

所以我們在學(xué)習(xí)TCP協(xié)議之前,首先要知道TCP在網(wǎng)絡(luò)傳輸中處于哪個位置固灵,以及它的協(xié)議的規(guī)范捅伤,下面我們就看看TCP首部的網(wǎng)絡(luò)傳輸起到的作用:

網(wǎng)絡(luò)數(shù)據(jù)傳輸過程

下面的圖是TCP頭部的規(guī)范定義,它定義了TCP協(xié)議如何讀取和解析數(shù)據(jù):


TCP頭部

TCP首部承載這TCP協(xié)議需要的各項(xiàng)信息巫玻,下面我們來分析一下:

  • TCP端口號
    TCP的連接是需要四個要素確定唯一一個連接:
    (源IP丛忆,源端口號)+ (目地IP,目的端口號)
    所以TCP首部預(yù)留了兩個16位作為端口號的存儲仍秤,而IP地址由上一層IP協(xié)議負(fù)責(zé)傳遞
    源端口號和目地端口各占16位兩個字節(jié)熄诡,也就是端口的范圍是2^16=65535
    另外1024以下是系統(tǒng)保留的,從1024-65535是用戶使用的端口范圍

  • TCP的序號和確認(rèn)號
    32位序號 seq:Sequence number 縮寫seq 徒扶,TCP通信過程中某一個傳輸方向上的字節(jié)流的每個字節(jié)的序號粮彤,通過這個來確認(rèn)發(fā)送的數(shù)據(jù)有序,比如現(xiàn)在序列號為1000姜骡,發(fā)送了1000导坟,下一個序列號就是2000。
    32位確認(rèn)號 ack:Acknowledge number 縮寫ack圈澈,TCP對上一次seq序號做出的確認(rèn)號惫周,用來響應(yīng)TCP報文段,給收到的TCP報文段的序號seq加1康栈。

  • TCP的標(biāo)志位
    每個TCP段都有一個目的递递,這是借助于TCP標(biāo)志位選項(xiàng)來確定的喷橙,允許發(fā)送方或接收方指定哪些標(biāo)志應(yīng)該被使用,以便段被另一端正確處理登舞。
    用的最廣泛的標(biāo)志是 SYN贰逾,ACKFIN,用于建立連接菠秒,確認(rèn)成功的段傳輸疙剑,最后終止連接。

    1. SYN:簡寫為S践叠,同步標(biāo)志位言缤,用于建立會話連接,同步序列號禁灼;
    2. ACK: 簡寫為.管挟,確認(rèn)標(biāo)志位,對已接收的數(shù)據(jù)包進(jìn)行確認(rèn)弄捕;
    3. FIN: 簡寫為F僻孝,完成標(biāo)志位,表示我已經(jīng)沒有數(shù)據(jù)要發(fā)送了察藐,即將關(guān)閉連接皮璧;
    4. PSH:簡寫為P舟扎,推送標(biāo)志位分飞,表示該數(shù)據(jù)包被對方接收后應(yīng)立即交給上層應(yīng)用,而不在緩沖區(qū)排隊(duì)睹限;
    5. RST:簡寫為R譬猫,重置標(biāo)志位,用于連接復(fù)位羡疗、拒絕錯誤和非法的數(shù)據(jù)包染服;
    6. URG:簡寫為U,緊急標(biāo)志位叨恨,表示數(shù)據(jù)包的緊急指針域有效柳刮,用來保證連接不被阻斷,并督促中間設(shè)備盡快處理痒钝;

TCP 三次握手建立連接

所謂三次握手(Three-way Handshake)秉颗,是指建立一個 TCP 連接時,需要客戶端和服務(wù)器總共發(fā)送3個報文送矩。

三次握手的目的是連接服務(wù)器指定端口蚕甥,建立 TCP 連接,并同步連接雙方的序列號和確認(rèn)號栋荸,交換 TCP 窗口大小信息菇怀。在 socket 編程中凭舶,客戶端執(zhí)行 connect() 時。將觸發(fā)三次握手爱沟。

三次握手過程的示意圖如下:


三次握手建立連接
  • 第一次握手
    客戶端將TCP報文標(biāo)志位SYN置為1帅霜,隨機(jī)產(chǎn)生一個序號值seq=J,保存在TCP首部的序列號(Sequence Number)字段里呼伸,指明客戶端打算連接的服務(wù)器的端口义屏,并將該數(shù)據(jù)包發(fā)送給服務(wù)器端,發(fā)送完畢后蜂大,客戶端進(jìn)入SYN_SENT狀態(tài)闽铐,等待服務(wù)器端確認(rèn)。

  • 第二次握手
    服務(wù)器端收到數(shù)據(jù)包后由標(biāo)志位SYN=1知道客戶端請求建立連接奶浦,服務(wù)器端將TCP報文標(biāo)志位SYN和ACK都置為1兄墅,ack=J+1,隨機(jī)產(chǎn)生一個序號值seq=K澳叉,并將該數(shù)據(jù)包發(fā)送給客戶端以確認(rèn)連接請求隙咸,服務(wù)器端進(jìn)入SYN_RCVD狀態(tài)。

  • 第三次握手
    客戶端收到確認(rèn)后成洗,檢查ack是否為J+1五督,ACK是否為1,如果正確則將標(biāo)志位ACK置為1瓶殃,ack=K+1充包,并將該數(shù)據(jù)包發(fā)送給服務(wù)器端,服務(wù)器端檢查ack是否為K+1遥椿,ACK是否為1基矮,如果正確則連接建立成功,客戶端和服務(wù)器端進(jìn)入ESTABLISHED狀態(tài)冠场,完成三次握手家浇,隨后客戶端與服務(wù)器端之間可以開始傳輸數(shù)據(jù)了。

注意:我們上面寫的ack和ACK碴裙,不是同一個概念:

  • 小寫的ack代表的是頭部的確認(rèn)號Acknowledge number钢悲, 縮寫ack,是對上一個包的序號進(jìn)行確認(rèn)的號舔株,ack=seq+1莺琳。
  • 大寫的ACK,則是我們上面說的TCP首部的標(biāo)志位督笆,用于標(biāo)志的TCP包是否對上一個包進(jìn)行了確認(rèn)操作芦昔,如果確認(rèn)了,則把ACK標(biāo)志位設(shè)置成1娃肿。

下面我自己做實(shí)驗(yàn)咕缎,開一個HTTP服務(wù)珠十,監(jiān)聽80端口,然后使用Tcpdump命令抓包凭豪,看一下TCP三次握手的過程:

sudo tcpdump -n -t -S -i enp0s3  port 80 

第一次握手焙蹭,標(biāo)志位Flags=S
IP 10.0.2.2.51323 > 10.0.2.15.80: Flags [S], seq 84689409, win 65535, options [mss 1460], length 0
第二次握手,標(biāo)志位Flags=[S.]
IP 10.0.2.15.80 > 10.0.2.2.51323: Flags [S.], seq 1893430205, ack 84689410, win 64240, options [mss 1460], length 0
第三次握手嫂伞,標(biāo)志位Flags=[.]
IP 10.0.2.2.51323 > 10.0.2.15.80: Flags [.], ack 1893430206, win 65535, length 0
建立連接后孔厉,客戶端發(fā)送http請求 
IP 10.0.2.2.51321 > 10.0.2.15.80: Flags [P.], seq 1:753, ack 1, win 65535, length 752: HTTP: GET / HTTP/1.1

tcpdump命令解析一下:
-i : 指定抓包的網(wǎng)卡是enp0s3
-n: 把域名轉(zhuǎn)成IP顯示
-t: 不顯示時間
-S: 序列號使用絕對數(shù)值,不指定-S的話帖努,序列號會使用相對的數(shù)值
port: 指定監(jiān)聽端口是80
host:指定監(jiān)聽的主機(jī)名

我們看下實(shí)戰(zhàn)中TCP的三次握手過程:

  • 第一次握手撰豺,客戶端51323端口號向服務(wù)器端80號端口發(fā)起連接,此時標(biāo)志位flags=S拼余,即SYN=1標(biāo)志污桦,表示向服務(wù)端發(fā)起連接的請求,同時生成序列號seq=84689409
  • 第二次握手匙监,服務(wù)端標(biāo)志位flags=[S.]凡橱,即SYN+ACK標(biāo)志位設(shè)置為1,表示對上一個請求連接的報文進(jìn)行確認(rèn)亭姥,同時設(shè)置ack=seq+1=184689410稼钩,生成序列號seq=1893430205
  • 第三次握手,客戶端對服務(wù)端的響應(yīng)進(jìn)行確認(rèn)达罗,所以此時標(biāo)志位是[.]即ACK=1坝撑,同時返回對上一個報文的seq的確認(rèn)號,ack=1893430206

至此氮块,三次握手完成绍载,一個TCP連接建立完成,接下來就是雙端傳輸數(shù)據(jù)了

為什么需要三次握手滔蝉?

我們假設(shè)client發(fā)出的第一個連接請求報文段并沒有丟失,而是在某個網(wǎng)絡(luò)結(jié)點(diǎn)長時間的滯留了塔沃,以致延誤到連接釋放以后的某個時間才到達(dá)server蝠引。

本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段后蛀柴,就誤認(rèn)為是client再次發(fā)出的一個新的連接請求螃概。于是就向client發(fā)出確認(rèn)報文段,同意建立連接鸽疾。

假設(shè)不采用“三次握手”吊洼,那么只要server發(fā)出確認(rèn),新的連接就建立了制肮。由于現(xiàn)在client并沒有發(fā)出建立連接的請求冒窍,因此不會理睬server的確認(rèn)递沪,也不會向server發(fā)送數(shù)據(jù)。但server卻以為新的運(yùn)輸連接已經(jīng)建立综液,并一直等待client發(fā)來數(shù)據(jù)款慨。這樣,server的很多資源就白白浪費(fèi)掉了谬莹。

所以檩奠,采用“三次握手”的辦法可以防止上述現(xiàn)象發(fā)生。例如剛才那種情況附帽,client不會向server的確認(rèn)發(fā)出確認(rèn)埠戳。server由于收不到確認(rèn),就知道client并沒有要求建立連接蕉扮。

TCP 三次握手跟現(xiàn)實(shí)生活中的人與人打電話是很類似的:

三次握手:
“喂乞而,你聽得到嗎?”
“我聽得到呀慢显,你聽得到我嗎爪模?”
“我能聽到你,今天 balabala……”

經(jīng)過三次的互相確認(rèn)荚藻,大家就會認(rèn)為對方對聽的到自己說話屋灌,并且愿意下一步溝通,否則应狱,對話就不一定能正常下去了共郭。

TCP 四次揮手關(guān)閉連接

四次揮手即終止TCP連接,就是指斷開一個TCP連接時疾呻,需要客戶端和服務(wù)端總共發(fā)送4個包以確認(rèn)連接的斷開除嘹。在socket編程中,這一過程由客戶端或服務(wù)端任一方執(zhí)行close來觸發(fā)岸蜗。
由于TCP連接是全雙工的尉咕,因此,每個方向都必須要單獨(dú)進(jìn)行關(guān)閉璃岳,這一原則是當(dāng)一方完成數(shù)據(jù)發(fā)送任務(wù)后年缎,發(fā)送一個FIN來終止這一方向的連接,收到一個FIN只是意味著這一方向上沒有數(shù)據(jù)流動了铃慷,即不會再收到數(shù)據(jù)了单芜,但是在這個TCP連接上仍然能夠發(fā)送數(shù)據(jù),直到這一方向也發(fā)送了FIN犁柜。首先進(jìn)行關(guān)閉的一方將執(zhí)行主動關(guān)閉洲鸠,而另一方則執(zhí)行被動關(guān)閉。

四次揮手過程的示意圖如下:


四次揮手關(guān)閉連接

揮手請求可以是Client端馋缅,也可以是Server端發(fā)起的扒腕,我們假設(shè)是Client端發(fā)起:

  • 第一次揮手: Client端發(fā)起揮手請求绢淀,向Server端發(fā)送標(biāo)志位是FIN報文段,設(shè)置序列號seq袜匿,此時更啄,Client端進(jìn)入FIN_WAIT_1狀態(tài),這表示Client端沒有數(shù)據(jù)要發(fā)送給Server端了居灯。
  • 第二次分手:Server端收到了Client端發(fā)送的FIN報文段祭务,向Client端返回一個標(biāo)志位是ACK的報文段,ack設(shè)為seq加1怪嫌,Client端進(jìn)入FIN_WAIT_2狀態(tài)义锥,Server端告訴Client端,我確認(rèn)并同意你的關(guān)閉請求岩灭。
  • 第三次分手: Server端向Client端發(fā)送標(biāo)志位是FIN的報文段拌倍,請求關(guān)閉連接,同時Client端進(jìn)入LAST_ACK狀態(tài)噪径。
  • 第四次分手 : Client端收到Server端發(fā)送的FIN報文段柱恤,向Server端發(fā)送標(biāo)志位是ACK的報文段,然后Client端進(jìn)入TIME_WAIT狀態(tài)找爱。Server端收到Client端的ACK報文段以后梗顺,就關(guān)閉連接。此時车摄,Client端等待2MSL的時間后依然沒有收到回復(fù)寺谤,則證明Server端已正常關(guān)閉,那好吮播,Client端也可以關(guān)閉連接了变屁。

為什么連接的時候是三次握手,關(guān)閉的時候卻是四次握手意狠?

建立連接時因?yàn)楫?dāng)Server端收到Client端的SYN連接請求報文后粟关,可以直接發(fā)送SYN+ACK報文。其中ACK報文是用來應(yīng)答的摄职,SYN報文是用來同步的誊役。所以建立連接只需要三次握手。

由于TCP協(xié)議是一種面向連接的谷市、可靠的、基于字節(jié)流的運(yùn)輸層通信協(xié)議击孩,TCP是全雙工模式迫悠。
這就意味著,關(guān)閉連接時巩梢,當(dāng)Client端發(fā)出FIN報文段時创泄,只是表示Client端告訴Server端數(shù)據(jù)已經(jīng)發(fā)送完畢了艺玲。當(dāng)Server端收到FIN報文并返回ACK報文段,表示它已經(jīng)知道Client端沒有數(shù)據(jù)發(fā)送了鞠抑,但是Server端還是可以發(fā)送數(shù)據(jù)到Client端的饭聚,所以Server很可能并不會立即關(guān)閉SOCKET,直到Server端把數(shù)據(jù)也發(fā)送完畢搁拙。
當(dāng)Server端也發(fā)送了FIN報文段時秒梳,這個時候就表示Server端也沒有數(shù)據(jù)要發(fā)送了,就會告訴Client端箕速,我也沒有數(shù)據(jù)要發(fā)送了酪碘,之后彼此就會愉快的中斷這次TCP連接。

為什么要等待2MSL盐茎?

MSL:報文段最大生存時間兴垦,它是任何報文段被丟棄前在網(wǎng)絡(luò)內(nèi)的最長時間。
有以下兩個原因:

  • 第一點(diǎn):保證TCP協(xié)議的全雙工連接能夠可靠關(guān)閉
    由于IP協(xié)議的不可靠性或者是其它網(wǎng)絡(luò)原因字柠,導(dǎo)致了Server端沒有收到Client端的ACK報文探越,那么Server端就會在超時之后重新發(fā)送FIN,如果此時Client端的連接已經(jīng)關(guān)閉處于CLOESD狀態(tài)窑业,那么重發(fā)的FIN就找不到對應(yīng)的連接了钦幔,從而導(dǎo)致連接錯亂,所以数冬,Client端發(fā)送完最后的ACK不能直接進(jìn)入CLOSED狀態(tài)节槐,而要保持TIME_WAIT,當(dāng)再次收到FIN的收拐纱,能夠保證對方收到ACK铜异,最后正確關(guān)閉連接。
  • 第二點(diǎn):保證這次連接的重復(fù)數(shù)據(jù)段從網(wǎng)絡(luò)中消失
    如果Client端發(fā)送最后的ACK直接進(jìn)入CLOSED狀態(tài)秸架,然后又再向Server端發(fā)起一個新連接揍庄,這時不能保證新連接的與剛關(guān)閉的連接的端口號是不同的,也就是新連接和老連接的端口號可能一樣了东抹,那么就可能出現(xiàn)問題:如果前一次的連接某些數(shù)據(jù)滯留在網(wǎng)絡(luò)中蚂子,這些延遲數(shù)據(jù)在建立新連接后到達(dá)Client端,由于新老連接的端口號和IP都一樣缭黔,TCP協(xié)議就認(rèn)為延遲數(shù)據(jù)是屬于新連接的食茎,新連接就會接收到臟數(shù)據(jù),這樣就會導(dǎo)致數(shù)據(jù)包混亂馏谨。所以TCP連接需要在TIME_WAIT狀態(tài)等待2倍MSL别渔,才能保證本次連接的所有數(shù)據(jù)在網(wǎng)絡(luò)中消失。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市哎媚,隨后出現(xiàn)的幾起案子喇伯,更是在濱河造成了極大的恐慌,老刑警劉巖拨与,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稻据,死亡現(xiàn)場離奇詭異,居然都是意外死亡买喧,警方通過查閱死者的電腦和手機(jī)捻悯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岗喉,“玉大人秋度,你說我怎么就攤上這事∏玻” “怎么了荚斯?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長查牌。 經(jīng)常有香客問我事期,道長,這世上最難降的妖魔是什么纸颜? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任兽泣,我火速辦了婚禮,結(jié)果婚禮上胁孙,老公的妹妹穿的比我還像新娘唠倦。我一直安慰自己,他們只是感情好涮较,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布稠鼻。 她就那樣靜靜地躺著,像睡著了一般狂票。 火紅的嫁衣襯著肌膚如雪候齿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天闺属,我揣著相機(jī)與錄音慌盯,去河邊找鬼。 笑死掂器,一個胖子當(dāng)著我的面吹牛亚皂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播国瓮,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼孕讳,長吁一口氣:“原來是場噩夢啊……” “哼匠楚!你這毒婦竟也來了巍膘?” 一聲冷哼從身側(cè)響起厂财,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎峡懈,沒想到半個月后璃饱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肪康,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年荚恶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片磷支。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡谒撼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雾狈,到底是詐尸還是另有隱情廓潜,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布善榛,位于F島的核電站辩蛋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏移盆。R本人自食惡果不足惜悼院,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咒循。 院中可真熱鬧据途,春花似錦、人聲如沸叙甸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚁署。三九已至便脊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間光戈,已是汗流浹背哪痰。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留久妆,地道東北人晌杰。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像筷弦,于是被迫代替她去往敵國和親肋演。 傳聞我的和親對象是個殘疾皇子抑诸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容