序
這是 TCP 協(xié)議系列文章的第二篇,上一篇文章中對 TCP 協(xié)議進(jìn)行了簡單介紹,由介紹可知,建立在 TCP 協(xié)議上的應(yīng)用需要先建立一個(gè)TCP 連接,才能收發(fā)數(shù)據(jù),本博文就主要介紹 TCP 連接的建立與終止的過程,及在這個(gè)過程中需要注意的問題。
3 次握手建立 TCP 連接
1、 第一次握手:客戶端發(fā)送 SYN 包( SYN = 1 && 序號 = x )到服務(wù)器,并進(jìn)入 SYN_SEND 狀態(tài),等待服務(wù)器確認(rèn);
2、 第二次握手:服務(wù)器收到 SYN 包,必須確認(rèn)客戶的 SYN(ACK = 1 && ack = x+1),同時(shí)自己也發(fā)送一個(gè)SYN包(SYN = 1 && 序號 = y),即 SYN + ACK 包,此時(shí)服務(wù)器進(jìn)入SYN_RECV 狀態(tài);
3、第三次握手:客戶端收到服務(wù)器的 SYN+ACK 包,向服務(wù)器發(fā)送確認(rèn)包 ACK (ACK = 1 && ack=y+1),此包發(fā)送完畢,客戶端進(jìn)入 ESTABLISHED 狀態(tài),服務(wù)端收到客戶端的確認(rèn)包后,也進(jìn)入 ESTABLISHED 狀態(tài),完成三次握手。
** 注意 **:握手過程中傳送的包里不包含數(shù)據(jù),三次握手完畢后,客戶端與服務(wù)器才正式開始傳送數(shù)據(jù)。理想狀態(tài)下,TCP 連接一旦建立,在通信雙方中的任何一方主動關(guān)閉連接之前,TCP 連接都將被一直保持下去。

4 次揮手?jǐn)嚅_ TCP 連接
與建立連接的 “三次握手” 類似,斷開一個(gè) TCP 連接則需要 “四次揮手”。
1、 第一次揮手:主動關(guān)閉方發(fā)送一個(gè) FIN(進(jìn)入FIN_WAIT_1 狀態(tài)),用來關(guān)閉主動方到被動關(guān)閉方的數(shù)據(jù)傳送,也就是主動關(guān)閉方告訴被動關(guān)閉方:我已經(jīng)不會再給你發(fā)數(shù)據(jù)了(當(dāng)然,在 FIN 包之前發(fā)送出去的數(shù)據(jù),如果沒有收到對應(yīng)的 ACK 確認(rèn)報(bào)文,主動關(guān)閉方依然會重發(fā)這些數(shù)據(jù)),但是,此時(shí)主動關(guān)閉方還可以接受數(shù)據(jù)。
2、 第二次揮手:被動關(guān)閉方收到 FIN 包后,發(fā)送一個(gè) ACK 給對方(進(jìn)入CLOSE_WAIT狀態(tài)),確認(rèn)序號為收到序號+ 1(與 SYN 相同,一個(gè) FIN 占用一個(gè)序號)。主動關(guān)閉方收到該包后進(jìn)入 FIN_WAIT_2 狀態(tài)。
3、 第三次揮手:被動關(guān)閉方發(fā)送一個(gè) FIN(進(jìn)入 LAST_ACK狀態(tài)),用來關(guān)閉被動關(guān)閉方到主動關(guān)閉方的數(shù)據(jù)傳送,也就是告訴主動關(guān)閉方,我的數(shù)據(jù)也發(fā)送完了,不會再給你發(fā)數(shù)據(jù)了。
4、 第四次揮手:主動關(guān)閉方收到 FIN 后,發(fā)送一個(gè) ACK 給被動關(guān)閉方(進(jìn)入 TIME_WAIT 狀態(tài)),確認(rèn)序號為收到序號 + 1。
5、 被動關(guān)閉方收到確認(rèn)包后,進(jìn)入 CLOSED 狀態(tài),主動關(guān)閉方在發(fā)送完確認(rèn)包并經(jīng)過 2 MSL 時(shí)間后,進(jìn)入 CLOSED 狀態(tài)。至此,4 次揮手過程結(jié)束,TCP 連接終止。

相關(guān)問題
** 為什么需要通過 3 次握手建立 TCP 連接? **
主要是為了防止兩次握手情況下已失效的連接請求報(bào)文段突然又傳送到服務(wù)端,而產(chǎn)生的錯(cuò)誤。舉例如下:客戶 A 向服務(wù)器 B 發(fā)出 TCP 連接請求,第一個(gè)連接請求報(bào)文在網(wǎng)絡(luò)的某個(gè)節(jié)點(diǎn)長時(shí)間滯留,A 超時(shí)后認(rèn)為報(bào)文丟失,于是再重傳一次連接請求,B 收到后建立連接。數(shù)據(jù)傳輸完畢后雙方斷開連接。而此時(shí),前一個(gè)滯留在網(wǎng)絡(luò)中的連接請求到達(dá)了服務(wù)端B,而 B 認(rèn)為 A 又發(fā)來連接請求,若采用的是“兩次握手”,則這種情況下 B 認(rèn)為傳輸連接已經(jīng)建立,并一直等待 A 傳輸數(shù)據(jù),而 A 此時(shí)并無連接請求,因此不予理睬,這樣就造成了 B 的資源白白浪費(fèi)了;但此時(shí)若是使用“三次握手”,則 B 向 A 返回確認(rèn)報(bào)文段,由于是一個(gè)失效的請求,因此 A 不予理睬,建立連接失敗。第三次握手的作用:防止已失效的連接請求報(bào)文段突然又傳送到了服務(wù)器。
** 關(guān)閉 TCP 連接一定需要 4 次揮手嗎? **
不一定,4 次揮手關(guān)閉 TCP 連接是最安全的做法。但在有些時(shí)候,我們不喜歡 TIME_WAIT 狀態(tài)(如當(dāng) MSL 數(shù)值設(shè)置過大導(dǎo)致服務(wù)器端有太多 TIME_WAIT 狀態(tài)的 TCP連接,減少這些條目數(shù)可以更快地關(guān)閉連接,為新連接釋放更多資源),這時(shí)我們可以通過設(shè)置 SOCKET 變量的 SO_LINGER 標(biāo)志來避免 SOCKET 在 close() 之后進(jìn)入 TIME_WAIT狀態(tài),這時(shí)將通過發(fā)送 RST 強(qiáng)制終止 TCP 連接(取代正常的 TCP 四次握手的終止方式)。但這并不是一個(gè)很好的主意,TIME_WAIT 對于我們來說往往是有利的。
** TIME_WAIT 狀態(tài)的作用 **
1、為了保證客戶端發(fā)送的最后一個(gè) ACK 報(bào)文段能夠達(dá)到服務(wù)器。 這個(gè) ACK 報(bào)文段可能丟失,因而使處在 LAST_ACK 狀態(tài)的服務(wù)器收不到確認(rèn)。這樣的話, 服務(wù)器會超時(shí)重傳FIN+ACK 報(bào)文段,客戶端就能在2MSL時(shí)間內(nèi)收到這個(gè)重傳的 FIN+ACK 報(bào)文段,接著客戶端重傳一次確認(rèn),重啟計(jì)時(shí)器。最后,客戶端和服務(wù)器都正常進(jìn)入到 CLOSED 狀態(tài)。如果客戶端在 TIME_WAIT 狀態(tài)不等待一段時(shí)間,而是在發(fā)送完 ACK 報(bào)文后立即釋放連接,那么就無法收到服務(wù)器重傳的 FIN+ACK 報(bào)文段,因而也不會再發(fā)送一次確認(rèn)報(bào)文。這樣,服務(wù)器就無法按照正常步驟進(jìn)入 CLOSED 狀態(tài)。
2、防止已失效的連接請求報(bào)文段出現(xiàn)在本連接中??蛻舳嗽诎l(fā)送完最后一個(gè) ACK 確認(rèn)報(bào)文段后,再經(jīng)過時(shí)間 2MSL,就可以使本連接持續(xù)的時(shí)間內(nèi)所產(chǎn)生的所有報(bào)文段都從網(wǎng)絡(luò)中消失。這樣就可以使下一個(gè)新的連接中不會出現(xiàn)這種舊的連接請求報(bào)文段。
** 注意 **:服務(wù)器結(jié)束 TCP 連接的時(shí)間要比客戶端早一些,因?yàn)榭蛻魴C(jī)(最先提出 close 請求的一端)最后要等待 2MSL 后才可以進(jìn)入 CLOSED 狀態(tài)。