TCP 三次握手和四次揮手

原文鏈接:http://www.reibang.com/p/ac16c211729a

任 TCP 虐我千百遍渊鞋,我仍待 TCP 如初戀。

遙想LZ當(dāng)年校招時(shí)常因 TCP 面試題被刷弱卡,真是又愛又恨….

過去不會(huì)沒關(guān)系,今天就讓我們來(lái)消除這份恐懼责掏,微笑著勇敢的面對(duì)它吧砍鸠!

所以LZ整理了關(guān)于 TCP 三次握手和四次揮手的面試題型,跟大家一起探討探討驶赏。

1.TCP 基本認(rèn)識(shí)

image

2.TCP 連接建立

image

3.TCP 連接斷開

image

4.Socket 編程

image

PS:本次文章不涉及 TCP 流量控制炸卑、擁塞控制、可靠性傳輸?shù)确矫嬷R(shí)煤傍,這些留在下篇哈盖文!

正文

01 TCP 基本認(rèn)識(shí)

瞧瞧 TCP 頭格式

我們先來(lái)看看 TCP 頭的格式,標(biāo)注顏色的表示與本文關(guān)聯(lián)比較大的字段蚯姆,其他字段不做詳細(xì)闡述五续。

image

TCP 頭格式

序列號(hào):在建立連接時(shí)由計(jì)算機(jī)生成的隨機(jī)數(shù)作為其初始值,通過 SYN 包傳給接收端主機(jī)龄恋,每發(fā)送一次數(shù)據(jù)疙驾,就「累加」一次該「數(shù)據(jù)字節(jié)數(shù)」的大小。用來(lái)解決網(wǎng)絡(luò)包亂序問題郭毕。

確認(rèn)應(yīng)答號(hào):指下一次「期望」收到的數(shù)據(jù)的序列號(hào)它碎,發(fā)送端收到這個(gè)確認(rèn)應(yīng)答以后可以認(rèn)為在這個(gè)序號(hào)以前的數(shù)據(jù)都已經(jīng)被正常接收。用來(lái)解決不丟包的問題显押。

控制位:

ACK:該位為 1 時(shí)链韭,「確認(rèn)應(yīng)答」的字段變?yōu)橛行В琓CP 規(guī)定除了最初建立連接時(shí)的 SYN 包之外該位必須設(shè)置為 1 煮落。

RST:該位為 1 時(shí),表示 TCP 連接中出現(xiàn)異常必須強(qiáng)制斷開連接踊谋。

SYC:該位為 1 時(shí)蝉仇,表示希望建立連,并在其「序列號(hào)」的字段進(jìn)行序列號(hào)初始值的設(shè)定。

FIN:該位為 1 時(shí)轿衔,表示今后不會(huì)再有數(shù)據(jù)發(fā)送沉迹,希望斷開連接。當(dāng)通信結(jié)束希望斷開連接時(shí)害驹,通信雙方的主機(jī)之間就可以相互交換 FIN 位置為 1 的 TCP 段鞭呕。

為什么需要 TCP 協(xié)議?TCP 工作在哪一層宛官?

IP 層是「不可靠」的葫松,它不保證網(wǎng)絡(luò)包的交付、不保證網(wǎng)絡(luò)包的按序交付底洗、也不保證網(wǎng)絡(luò)包中的數(shù)據(jù)的完整性腋么。

image

OSI 參考模型與 TCP/IP 的關(guān)系

如果需要保障網(wǎng)絡(luò)數(shù)據(jù)包的可靠性,那么就需要由上層(傳輸層)的 TCP 協(xié)議來(lái)負(fù)責(zé)亥揖。

因?yàn)?TCP 是一個(gè)工作在傳輸層可靠數(shù)據(jù)傳輸?shù)姆?wù)珊擂,它能確保接收端接收的網(wǎng)絡(luò)包是無(wú)損壞、無(wú)間隔费变、非冗余和按序的摧扇。

什么是 TCP ?

TCP 是面向連接的挚歧、可靠的扛稽、基于字節(jié)流的傳輸層通信協(xié)議。

image

面向連接:一定是「一對(duì)一」才能連接昼激,不能像 UDP 協(xié)議 可以一個(gè)主機(jī)同時(shí)向多個(gè)主機(jī)發(fā)送消息庇绽,也就是一對(duì)多是無(wú)法做到的;

可靠的:無(wú)論的網(wǎng)絡(luò)鏈路中出現(xiàn)了怎樣的鏈路變化橙困,TCP 都可以保證一個(gè)報(bào)文一定能夠到達(dá)接收端瞧掺;

字節(jié)流:消息是「沒有邊界」的,所以無(wú)論我們消息有多大都可以進(jìn)行傳輸凡傅。并且消息是「有序的」辟狈,當(dāng)「前一個(gè)」消息沒有收到的時(shí)候,即使它先收到了后面的字節(jié)已經(jīng)收到夏跷,那么也不能扔給應(yīng)用層去處理哼转,同時(shí)對(duì)「重復(fù)」的報(bào)文會(huì)自動(dòng)丟棄。

什么是 TCP 連接槽华?

我們來(lái)看看 RFC 793 是如何定義「連接」的:

Connections:

The reliability and flow control mechanisms described above require that TCPs initialize and maintain certain status information for each data stream.

The combination of this information, including sockets, sequence numbers, and window sizes, is called a connection.

簡(jiǎn)單來(lái)說就是壹蔓,用于保證可靠性和流量控制維護(hù)的某些狀態(tài)信息,這些信息的組合猫态,包括Socket佣蓉、序列號(hào)和窗口大小稱為連接披摄。

image

所以我們可以知道,建立一個(gè) TCP 連接是需要客戶端與服務(wù)器端達(dá)成上述三個(gè)信息的共識(shí)勇凭。

Socket:由 IP 地址和端口號(hào)組成

序列號(hào):用來(lái)解決亂序問題等

窗口大小:用來(lái)做流量控制

如何唯一確定一個(gè) TCP 連接呢疚膊?

TCP 四元組可以唯一的確定一個(gè)連接,四元組包括如下:

源地址

源端口

目的地址

目的端口

image

TCP 四元組

源地址和目的地址的字段(32位)是在 IP 頭部中虾标,作用是通過 IP 協(xié)議發(fā)送報(bào)文給對(duì)方主機(jī)寓盗。

源端口和目的端口的字段(16位)是在 TCP 頭部中,作用是告訴 TCP 協(xié)議應(yīng)該把報(bào)文發(fā)給哪個(gè)進(jìn)程璧函。

有一個(gè) IP 的服務(wù)器監(jiān)聽了一個(gè)端口傀蚌,它的 TCP 的最大連接數(shù)是多少?

服務(wù)器通常固定在某個(gè)本地端口上監(jiān)聽柳譬,等待客戶端的連接請(qǐng)求喳张。

因此,客戶端 IP 和 端口是可變的美澳,其理論值計(jì)算公式如下:

image

對(duì) IPv4销部,客戶端的 IP 數(shù)最多為 2 的 32 次方,客戶端的端口數(shù)最多為 2 的 16次方制跟,也就是服務(wù)端單機(jī)最大 TCP 連接數(shù)舅桩,約為 2 的 48 次方。

當(dāng)然雨膨,服務(wù)端最大并發(fā) TCP 連接數(shù)遠(yuǎn)不能達(dá)到理論上限擂涛。

首先主要是文件描述符限制,Socket 都是文件聊记,所以首先要通過 ulimit 配置文件描述符的數(shù)目撒妈;

另一個(gè)是內(nèi)存限制,每個(gè) TCP 連接都要占用一定內(nèi)存排监,操作系統(tǒng)是有限的狰右。

UDP 和 TCP 有什么區(qū)別呢?分別的應(yīng)用場(chǎng)景是舆床?

UDP 不提供復(fù)雜的控制機(jī)制棋蚌,利用 IP 提供面向「無(wú)連接」的通信服務(wù)。

UDP 協(xié)議真的非常簡(jiǎn)挨队,頭部只有 8 個(gè)字節(jié)( 64 位)谷暮,UDP 的頭部格式如下:

image

UDP 頭部格式

目標(biāo)和源端口:主要是告訴 UDP 協(xié)議應(yīng)該把報(bào)文發(fā)給哪個(gè)進(jìn)程。

包長(zhǎng)度:該字段保存了 UDP 首部的長(zhǎng)度跟數(shù)據(jù)的長(zhǎng)度之和盛垦。

校驗(yàn)和:校驗(yàn)和是為了提供可靠的 UDP 首部和數(shù)據(jù)而設(shè)計(jì)湿弦。

TCP 和 UDP 區(qū)別:

1. 連接

TCP 是面向連接的傳輸層協(xié)議,傳輸數(shù)據(jù)前先要建立連接腾夯。

UDP 是不需要連接省撑,即刻傳輸數(shù)據(jù)赌蔑。

2. 服務(wù)對(duì)象

TCP 是一對(duì)一的兩點(diǎn)服務(wù),即一條連接只有兩個(gè)端點(diǎn)竟秫。

UDP 支持一對(duì)一、一對(duì)多跷乐、多對(duì)多的交互通信

3. 可靠性

TCP 是可靠交付數(shù)據(jù)的肥败,數(shù)據(jù)可以無(wú)差錯(cuò)、不丟失愕提、不重復(fù)馒稍、按需到達(dá)。

UDP 是盡最大努力交付浅侨,不保證可靠交付數(shù)據(jù)纽谒。

4. 擁塞控制、流量控制

TCP 有擁塞控制和流量控制機(jī)制如输,保證數(shù)據(jù)傳輸?shù)陌踩浴?/p>

UDP 則沒有鼓黔,即使網(wǎng)絡(luò)非常擁堵了,也不會(huì)影響 UDP 的發(fā)送速率不见。

5. 首部開銷

TCP 首部長(zhǎng)度較長(zhǎng)澳化,會(huì)有一定的開銷,首部在沒有使用「選項(xiàng)」字段時(shí)是 20 個(gè)字節(jié)稳吮,如果使用了「選項(xiàng)」字段則會(huì)變長(zhǎng)的缎谷。

UDP 首部只有 8 個(gè)字節(jié),并且是固定不變的灶似,開銷較小列林。

TCP 和 UDP 應(yīng)用場(chǎng)景:

由于 TCP 是面向連接,能保證數(shù)據(jù)的可靠性交付酪惭,因此經(jīng)常用于:

FTP 文件傳輸

HTTP / HTTPS

由于 UDP 面向無(wú)連接希痴,它可以隨時(shí)發(fā)送數(shù)據(jù),再加上UDP本身的處理既簡(jiǎn)單又高效撞蚕,因此經(jīng)常用于:

包總量較少的通信润梯,如 DNS 、SNMP 等

視頻甥厦、音頻等多媒體通信

廣播通信

為什么 UDP 頭部沒有「首部長(zhǎng)度」字段纺铭,而 TCP 頭部有「首部長(zhǎng)度」字段呢?

原因是 TCP 有可變長(zhǎng)的「選項(xiàng)」字段刀疙,而 UDP 頭部長(zhǎng)度則是不會(huì)變化的舶赔,無(wú)需多一個(gè)字段去記錄 UDP 的首部長(zhǎng)度。

為什么 UDP 頭部有「包長(zhǎng)度」字段谦秧,而 TCP 頭部則沒有「包長(zhǎng)度」字段呢竟纳?

先說說 TCP 是如何計(jì)算負(fù)載數(shù)據(jù)長(zhǎng)度:

image

其中 IP 總長(zhǎng)度 和 IP 首部長(zhǎng)度撵溃,在 IP 首部格式是已知的。TCP 首部長(zhǎng)度锥累,則是在 TCP 首部格式已知的缘挑,所以就可以求得 TCP 數(shù)據(jù)的長(zhǎng)度。

大家這時(shí)就奇怪了問:“ UDP 也是基于 IP 層的呀桶略,那 UDP 的數(shù)據(jù)長(zhǎng)度也可以通過這個(gè)公式計(jì)算呀语淘?為何還要有「包長(zhǎng)度」呢?”

這么一問际歼,確實(shí)感覺 UDP 「包長(zhǎng)度」是冗余的惶翻。

因?yàn)闉榱司W(wǎng)絡(luò)設(shè)備硬件設(shè)計(jì)和處理方便,首部長(zhǎng)度需要是 4字節(jié)的整數(shù)倍鹅心。

如果去掉 UDP 「包長(zhǎng)度」字段吕粗,那 UDP 首部長(zhǎng)度就不是 4 字節(jié)的整數(shù)倍了,所以小林覺得這可能是為了補(bǔ)全 UDP 首部長(zhǎng)度是 4 字節(jié)的整數(shù)倍旭愧,才補(bǔ)充了「包長(zhǎng)度」字段颅筋。

TCP 三次握手過程和狀態(tài)變遷

TCP 是面向連接的協(xié)議,所以使用 TCP 前必須先建立連接榕茧,而建立連接是通過三次握手而進(jìn)行的垃沦。

image

TCP 三次握手

一開始,客戶端和服務(wù)端都處于 CLOSED 狀態(tài)用押。先是服務(wù)端主動(dòng)監(jiān)聽某個(gè)端口肢簿,處于 LISTEN 狀態(tài)

image

第一個(gè)報(bào)文—— SYN 報(bào)文

客戶端會(huì)隨機(jī)初始化序號(hào)(client_isn),將此序號(hào)置于 TCP 首部的「序號(hào)」字段中蜻拨,同時(shí)把 SYN 標(biāo)志位置為 1 池充,表示 SYN 報(bào)文。接著把第一個(gè) SYN 報(bào)文發(fā)送給服務(wù)端缎讼,表示向服務(wù)端發(fā)起連接收夸,該報(bào)文不包含應(yīng)用層數(shù)據(jù),之后客戶端處于SYN-SENT 狀態(tài)血崭。

image

第二個(gè)報(bào)文 —— SYN + ACK 報(bào)文

服務(wù)端收到客戶端的 SYN 報(bào)文后卧惜,首先服務(wù)端也隨機(jī)初始化自己的序號(hào)(server_isn),將此序號(hào)填入 TCP 首部的「序號(hào)」字段中夹纫,其次把 TCP 首部的「確認(rèn)應(yīng)答號(hào)」字段填入 client_isn + 1, 接著把 SYN 和 ACK 標(biāo)志位置為1咽瓷。最后把該報(bào)文發(fā)給客戶端,該報(bào)文也不包含應(yīng)用層數(shù)據(jù)舰讹,之后服務(wù)端處于SYN-RCVD 狀態(tài)茅姜。

image

第三個(gè)報(bào)文 —— ACK 報(bào)文

客戶端收到服務(wù)端報(bào)文后,還要向服務(wù)端回應(yīng)最后一個(gè)應(yīng)答報(bào)文月匣,首先該應(yīng)答報(bào)文 TCP 首部 ACK 標(biāo)志位置為 1 ,其次「確認(rèn)應(yīng)答號(hào)」字段填入 server_isn + 1,最后把報(bào)文發(fā)送給服務(wù)端闺骚,這次報(bào)文可以攜帶客戶到服務(wù)器的數(shù)據(jù),之后客戶端處于 ESTABLISHED 狀態(tài)称诗。

服務(wù)器收到客戶端的應(yīng)答報(bào)文后,也進(jìn)入 ESTABLISHED 狀態(tài)头遭。

從上面的過程可以發(fā)現(xiàn)第三次握手是可以攜帶數(shù)據(jù)的粪狼,前兩次握手是不可以攜帶數(shù)據(jù)的,這也是面試常問的題任岸。

一旦完成三次握手,雙方都處于 ESTABLISHED 狀態(tài)狡刘,此致連接就已建立完成享潜,客戶端和服務(wù)端就可以相互發(fā)送數(shù)據(jù)了嗅蔬。

如何在 Linux 系統(tǒng)中查看 TCP 狀態(tài)剑按?

TCP 的連接狀態(tài)查看,在 Linux 可以通過 netstat -napt 命令查看澜术。

image

TCP 連接狀態(tài)查看

為什么是三次握手艺蝴?不是兩次、四次鸟废?

相信大家比較巢赂遥回答的是:“因?yàn)槿挝帐植拍鼙WC雙方具有接收和發(fā)送的能力『醒樱”

這回答是沒問題缩擂,但這回答是片面的,并沒有說出主要的原因添寺。

在前面我們知道了什么是 TCP 連接

用于保證可靠性和流量控制維護(hù)的某些狀態(tài)信息胯盯,這些信息的組合,包括Socket计露、序列號(hào)和窗口大小稱為連接博脑。

所以,重要的是為什么三次握手才可以初始化Socket票罐、序列號(hào)和窗口大小并建立 TCP 連接叉趣。

接下來(lái)以三個(gè)方面分析三次握手的原因:

三次握手才可以阻止歷史重復(fù)連接的初始化(主要原因)

三次握手才可以同步雙方的初始序列號(hào)

三次握手才可以避免資源浪費(fèi)

原因一:避免歷史連接

我們來(lái)看看 RFC 793 指出的 TCP 連接使用三次握手的首要原因

The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.

簡(jiǎn)單來(lái)說,三次握手的首要原因是為了防止舊的重復(fù)連接初始化造成混亂胶坠。

網(wǎng)絡(luò)環(huán)境是錯(cuò)綜復(fù)雜的君账,往往并不是如我們期望的一樣,先發(fā)送的數(shù)據(jù)包沈善,就先到達(dá)目標(biāo)主機(jī)乡数,反而它很騷椭蹄,可能會(huì)由于網(wǎng)絡(luò)擁堵等亂七八糟的原因,會(huì)使得舊的數(shù)據(jù)包净赴,先到達(dá)目標(biāo)主機(jī)绳矩,那么這種情況下 TCP 三次握手是如何避免的呢?

image

三次握手避免歷史連接

客戶端連續(xù)發(fā)送多次 SYN 建立連接的報(bào)文玖翅,在網(wǎng)絡(luò)擁堵等情況下:

一個(gè)「舊 SYN 報(bào)文」比「最新的 SYN 」 報(bào)文早到達(dá)了服務(wù)端翼馆;

那么此時(shí)服務(wù)端就會(huì)回一個(gè) SYN + ACK 報(bào)文給客戶端;

客戶端收到后可以根據(jù)自身的上下文金度,判斷這是一個(gè)歷史連接(序列號(hào)過期或超時(shí))应媚,那么客戶端就會(huì)發(fā)送 RST 報(bào)文給服務(wù)端,表示中止這一次連接猜极。

如果是兩次握手連接中姜,就不能判斷當(dāng)前連接是否是歷史連接,三次握手則可以在客戶端(發(fā)送方)準(zhǔn)備發(fā)送第三次報(bào)文時(shí)跟伏,客戶端因有足夠的上下文來(lái)判斷當(dāng)前連接是否是歷史連接:

如果是歷史連接(序列號(hào)過期或超時(shí))丢胚,則第三次握手發(fā)送的報(bào)文是 RST 報(bào)文,以此中止歷史連接受扳;

如果不是歷史連接携龟,則第三次發(fā)送的報(bào)文是 ACK 報(bào)文,通信雙方就會(huì)成功建立連接勘高;

所以峡蟋, TCP 使用三次握手建立連接的最主要原因是防止歷史連接初始化了連接。

原因二:同步雙方初始序列號(hào)

TCP 協(xié)議的通信雙方相满, 都必須維護(hù)一個(gè)「序列號(hào)」层亿, 序列號(hào)是可靠傳輸?shù)囊粋€(gè)關(guān)鍵因素,它的作用:

接收方可以去除重復(fù)的數(shù)據(jù)立美;

接收方可以根據(jù)數(shù)據(jù)包的序列號(hào)按序接收匿又;

可以標(biāo)識(shí)發(fā)送出去的數(shù)據(jù)包中, 哪些是已經(jīng)被對(duì)方收到的建蹄;

可見碌更,序列號(hào)在 TCP 連接中占據(jù)著非常重要的作用,所以當(dāng)客戶端發(fā)送攜帶「初始序列號(hào)」的 SYN 報(bào)文的時(shí)候洞慎,需要服務(wù)端回一個(gè) ACK 應(yīng)答報(bào)文痛单,表示客戶端的 SYN 報(bào)文已被服務(wù)端成功接收,那當(dāng)服務(wù)端發(fā)送「初始序列號(hào)」給客戶端的時(shí)候劲腿,依然也要得到客戶端的應(yīng)答回應(yīng)旭绒,這樣一來(lái)一回,才能確保雙方的初始序列號(hào)能被可靠的同步。

image

四次握手與三次握手

四次握手其實(shí)也能夠可靠的同步雙方的初始化序號(hào)挥吵,但由于第二步和第三步可以優(yōu)化成一步重父,所以就成了「三次握手」。

而兩次握手只保證了一方的初始序列號(hào)能被對(duì)方成功接收忽匈,沒辦法保證雙方的初始序列號(hào)都能被確認(rèn)接收房午。

原因三:避免資源浪費(fèi)

如果只有「兩次握手」,當(dāng)客戶端的 SYN 請(qǐng)求連接在網(wǎng)絡(luò)中阻塞丹允,客戶端沒有接收到ACK 報(bào)文郭厌,就會(huì)重新發(fā)送 SYN ,由于沒有第三次握手雕蔽,服務(wù)器不清楚客戶端是否收到了自己發(fā)送的建立連接的 ACK 確認(rèn)信號(hào)折柠,所以每收到一個(gè) SYN 就只能先主動(dòng)建立一個(gè)連接,這會(huì)造成什么情況呢批狐?

如果客戶端的 SYN 阻塞了液走,重復(fù)發(fā)送多次 SYN 報(bào)文,那么服務(wù)器在收到請(qǐng)求后就會(huì)建立多個(gè)冗余的無(wú)效鏈接贾陷,造成不必要的資源浪費(fèi)。

image

兩次握手會(huì)造成資源浪費(fèi)

即兩次握手會(huì)造成消息滯留情況下嘱根,服務(wù)器重復(fù)接受無(wú)用的連接請(qǐng)求 SYN 報(bào)文髓废,而造成重復(fù)分配資源。

小結(jié)

TCP 建立連接時(shí)该抒,通過三次握手能防止歷史連接的建立慌洪,能減少雙方不必要的資源開銷,能幫助雙方同步初始化序列號(hào)凑保。序列號(hào)能夠保證數(shù)據(jù)包不重復(fù)冈爹、不丟棄和按序傳輸。

不使用「兩次握手」和「四次握手」的原因:

「兩次握手」:無(wú)法防止歷史連接的建立欧引,會(huì)造成雙方資源的浪費(fèi)频伤,也無(wú)法可靠的同步雙方序列號(hào);

「四次握手」:三次握手就已經(jīng)理論上最少可靠連接建立芝此,所以不需要使用更多的通信次數(shù)憋肖。

為什么客戶端和服務(wù)端的初始序列號(hào) ISN 是不相同的?

因?yàn)榫W(wǎng)絡(luò)中的報(bào)文會(huì)延遲婚苹、會(huì)復(fù)制重發(fā)岸更、也有可能丟失,這樣會(huì)造成的不同連接之間產(chǎn)生互相影響膊升,所以為了避免互相影響怎炊,客戶端和服務(wù)端的初始序列號(hào)是隨機(jī)且不同的。

初始序列號(hào) ISN 是如何隨機(jī)產(chǎn)生的?

起始 ISN 是基于時(shí)鐘的评肆,每 4 毫秒 + 1债查,轉(zhuǎn)一圈要 4.55 個(gè)小時(shí)。

RFC1948 中提出了一個(gè)較好的初始化序列號(hào) ISN 隨機(jī)生成算法糟港。

ISN = M + F (localhost, localport, remotehost, remoteport)

M 是一個(gè)計(jì)時(shí)器攀操,這個(gè)計(jì)時(shí)器每隔 4 毫秒加 1。

F 是一個(gè) Hash 算法秸抚,根據(jù)源 IP速和、目的 IP、源端口剥汤、目的端口生成一個(gè)隨機(jī)數(shù)值颠放。要保證 Hash 算法不能被外部輕易推算得出,用 MD5 算法是一個(gè)比較好的選擇吭敢。

既然 IP 層會(huì)分片碰凶,為什么 TCP 層還需要 MSS 呢?

我們先來(lái)認(rèn)識(shí)下 MTU 和 MSS

image

MTU 與 MSS

MTU:一個(gè)網(wǎng)絡(luò)包的最大長(zhǎng)度鹿驼,以太網(wǎng)中一般為 1500 字節(jié)欲低;

MSS:除去 IP 和 TCP 頭部之后,一個(gè)網(wǎng)絡(luò)包所能容納的 TCP 數(shù)據(jù)的最大長(zhǎng)度畜晰;

如果TCP 的整個(gè)報(bào)文(頭部 + 數(shù)據(jù))交給 IP 層進(jìn)行分片砾莱,會(huì)有什么異常呢?

當(dāng) IP 層有一個(gè)超過 MTU 大小的數(shù)據(jù)(TCP 頭部 + TCP 數(shù)據(jù))要發(fā)送凄鼻,那么 IP 層就要進(jìn)行分片腊瑟,把數(shù)據(jù)分片成若干片,保證每一個(gè)分片都小于 MTU块蚌。把一份 IP 數(shù)據(jù)報(bào)進(jìn)行分片以后闰非,由目標(biāo)主機(jī)的 IP 層來(lái)進(jìn)行重新組裝后,在交給上一層 TCP 傳輸層峭范。

這看起來(lái)井然有序财松,但這存在隱患的,那么當(dāng)如果一個(gè) IP 分片丟失纱控,整個(gè) IP 報(bào)文的所有分片都得重傳游岳。

因?yàn)?IP 層本身沒有超時(shí)重傳機(jī)制,它由傳輸層的 TCP 來(lái)負(fù)責(zé)超時(shí)和重傳其徙。

當(dāng)接收方發(fā)現(xiàn) TCP 報(bào)文(頭部 + 數(shù)據(jù))的某一片丟失后胚迫,則不會(huì)響應(yīng) ACK 給對(duì)方,那么發(fā)送方的 TCP 在超時(shí)后唾那,就會(huì)重發(fā)「整個(gè) TCP 報(bào)文(頭部 + 數(shù)據(jù))」访锻。

因此褪尝,可以得知由 IP 層進(jìn)行分片傳輸,是非常沒有效率的期犬。

所以河哑,為了達(dá)到最佳的傳輸效能 TCP 協(xié)議在建立連接的時(shí)候通常要協(xié)商雙方的 MSS 值,當(dāng) TCP 層發(fā)現(xiàn)數(shù)據(jù)超過 MSS 時(shí)龟虎,則就先會(huì)進(jìn)行分片璃谨,當(dāng)然由它形成的 IP 包的長(zhǎng)度也就不會(huì)大于 MTU ,自然也就不用 IP 分片了鲤妥。

image

握手階段協(xié)商 MSS

經(jīng)過 TCP 層分片后佳吞,如果一個(gè) TCP 分片丟失后,進(jìn)行重發(fā)時(shí)也是以 MSS 為單位棉安,而不用重傳所有的分片底扳,大大增加了重傳的效率。

什么是 SYN 攻擊贡耽?如何避免 SYN 攻擊衷模?

SYN 攻擊

我們都知道 TCP 連接建立是需要三次握手,假設(shè)攻擊者短時(shí)間偽造不同 IP 地址的SYN 報(bào)文蒲赂,服務(wù)端每接收到一個(gè) SYN 報(bào)文阱冶,就進(jìn)入SYN_RCVD 狀態(tài),但服務(wù)端發(fā)送出去的 ACK + SYN 報(bào)文滥嘴,無(wú)法得到未知 IP 主機(jī)的 ACK 應(yīng)答熙揍,久而久之就會(huì)占滿服務(wù)端的 SYN 接收隊(duì)列(未連接隊(duì)列),使得服務(wù)器不能為正常用戶服務(wù)氏涩。

image

SYN 攻擊

避免 SYN 攻擊方式一

其中一種解決方式是通過修改 Linux 內(nèi)核參數(shù),控制隊(duì)列大小和當(dāng)隊(duì)列滿時(shí)應(yīng)做什么處理有梆。

當(dāng)網(wǎng)卡接收數(shù)據(jù)包的速度大于內(nèi)核處理的速度時(shí)是尖,會(huì)有一個(gè)隊(duì)列保存這些數(shù)據(jù)包∧嘁控制該隊(duì)列的最大值如下參數(shù):

net.core.netdev_max_backlog

SYN_RCVD 狀態(tài)連接的最大個(gè)數(shù):

net.ipv4.tcp_max_syn_backlog

超出處理能時(shí)饺汹,對(duì)新的 SYN 直接回 RST,丟棄連接:

net.ipv4.tcp_abort_on_overflow

避免 SYN 攻擊方式二

我們先來(lái)看下Linux 內(nèi)核的 SYN (未完成連接建立)隊(duì)列與 Accpet (已完成連接建立)隊(duì)列是如何工作的痰催?

image

正常流程

正常流程:

當(dāng)服務(wù)端接收到客戶端的 SYN 報(bào)文時(shí)兜辞,會(huì)將其加入到內(nèi)核的「 SYN 隊(duì)列」;

接著發(fā)送 SYN + ACK 給客戶端夸溶,等待客戶端回應(yīng) ACK 報(bào)文逸吵;

服務(wù)端接收到 ACK 報(bào)文后,從「 SYN 隊(duì)列」移除放入到「 Accept 隊(duì)列」缝裁;

應(yīng)用通過調(diào)用 accpet() socket 接口扫皱,從「 Accept 隊(duì)列」取出的連接。

image

應(yīng)用程序過慢

應(yīng)用程序過慢:

如果應(yīng)用程序過慢時(shí),就會(huì)導(dǎo)致「 Accept 隊(duì)列」被占滿韩脑。

image

受到 SYN 攻擊

受到 SYN 攻擊:

如果不斷受到 SYN 攻擊氢妈,就會(huì)導(dǎo)致「 SYN 隊(duì)列」被占滿。

tcp_syncookies 的方式可以應(yīng)對(duì) SYN 攻擊的方法:

net.ipv4.tcp_syncookies=1

image

tcp_syncookies 應(yīng)對(duì) SYN 攻擊

當(dāng) 「 SYN 隊(duì)列」?jié)M之后段多,后續(xù)服務(wù)器收到 SYN 包首量,不進(jìn)入「 SYN 隊(duì)列」;

計(jì)算出一個(gè) cookie 值进苍,再以 SYN + ACK 中的「序列號(hào)」返回客戶端加缘,

服務(wù)端接收到客戶端的應(yīng)答報(bào)文時(shí),服務(wù)器會(huì)檢查這個(gè) ACK 包的合法性琅捏。如果合法生百,直接放入到「 Accept 隊(duì)列」。

最后應(yīng)用通過調(diào)用 accpet() socket 接口柄延,從「 Accept 隊(duì)列」取出的連接蚀浆。

03 TCP 連接斷開

TCP 四次揮手過程和狀態(tài)變遷

天下沒有不散的宴席,對(duì)于 TCP 連接也是這樣搜吧, TCP 斷開連接是通過四次揮手方式市俊。

雙方都可以主動(dòng)斷開連接,斷開連接后主機(jī)中的「資源」將被釋放滤奈。

image

客戶端主動(dòng)關(guān)閉連接 —— TCP 四次揮手

客戶端打算關(guān)閉連接摆昧,此時(shí)會(huì)發(fā)送一個(gè) TCP 首部 FIN 標(biāo)志位被置為 1 的報(bào)文,也即 FIN 報(bào)文蜒程,之后客戶端進(jìn)入 FIN_WAIT_1 狀態(tài)绅你。

服務(wù)端收到該報(bào)文后,就向客戶端發(fā)送 ACK 應(yīng)答報(bào)文昭躺,接著服務(wù)端進(jìn)入CLOSED_WAIT 狀態(tài)忌锯。

客戶端收到服務(wù)端的 ACK 應(yīng)答報(bào)文后,之后進(jìn)入 FIN_WAIT_2 狀態(tài)领炫。

等待服務(wù)端處理完數(shù)據(jù)后偶垮,也向客戶端發(fā)送 FIN 報(bào)文,之后服務(wù)端進(jìn)入LAST_ACK 狀態(tài)帝洪。

客戶端收到服務(wù)端的 FIN 報(bào)文后似舵,回一個(gè) ACK 應(yīng)答報(bào)文,之后進(jìn)入TIME_WAIT 狀態(tài)

服務(wù)器收到了 ACK 應(yīng)答報(bào)文后葱峡,就進(jìn)入了 CLOSE 狀態(tài)砚哗,至此服務(wù)端已經(jīng)完成連接的關(guān)閉。

客戶端在經(jīng)過 2MSL 一段時(shí)間后砰奕,自動(dòng)進(jìn)入 CLOSE 狀態(tài)频祝,至此客戶端也完成連接的關(guān)閉泌参。

你可以看到,每個(gè)方向都需要一個(gè) FIN 和一個(gè) ACK常空,因此通常被稱為四次揮手沽一。

這里一點(diǎn)需要注意是:主動(dòng)關(guān)閉連接的,才有 TIME_WAIT 狀態(tài)漓糙。

為什么揮手需要四次铣缠?

再來(lái)回顧下四次揮手雙方發(fā) FIN 包的過程,就能理解為什么需要四次了昆禽。

關(guān)閉連接時(shí)蝗蛙,客戶端向服務(wù)端發(fā)送 FIN 時(shí),僅僅表示客戶端不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù)醉鳖。

服務(wù)器收到客戶端的 FIN 報(bào)文時(shí)捡硅,先回一個(gè) ACK 應(yīng)答報(bào)文,而服務(wù)端可能還有數(shù)據(jù)需要處理和發(fā)送盗棵,等服務(wù)端不再發(fā)送數(shù)據(jù)時(shí)壮韭,才發(fā)送 FIN 報(bào)文給客戶端來(lái)表示同意現(xiàn)在關(guān)閉連接。

從上面過程可知纹因,服務(wù)端通常需要等待完成數(shù)據(jù)的發(fā)送和處理喷屋,所以服務(wù)端的 ACK 和FIN 一般都會(huì)分開發(fā)送,從而比三次握手導(dǎo)致多了一次瞭恰。

為什么 TIME_WAIT 等待的時(shí)間是 2MSL屯曹?

MSL 是 Maximum Segment Lifetime,報(bào)文最大生存時(shí)間惊畏,它是任何報(bào)文在網(wǎng)絡(luò)上存在的最長(zhǎng)時(shí)間恶耽,超過這個(gè)時(shí)間報(bào)文將被丟棄。因?yàn)?TCP 報(bào)文基于是 IP 協(xié)議的颜启,而 IP 頭中有一個(gè) TTL 字段偷俭,是 IP 數(shù)據(jù)報(bào)可以經(jīng)過的最大路由數(shù),每經(jīng)過一個(gè)處理他的路由器此值就減 1农曲,當(dāng)此值為 0 則數(shù)據(jù)報(bào)將被丟棄,同時(shí)發(fā)送 ICMP 報(bào)文通知源主機(jī)驻债。

MSL 與 TTL 的區(qū)別:MSL 的單位是時(shí)間乳规,而 TTL 是經(jīng)過路由跳數(shù)。所以 MSL 應(yīng)該要大于等于 TTL 消耗為 0 的時(shí)間合呐,以確保報(bào)文已被自然消亡暮的。

TIME_WAIT 等待 2 倍的 MSL,比較合理的解釋是:網(wǎng)絡(luò)中可能存在來(lái)自發(fā)送方的數(shù)據(jù)包淌实,當(dāng)這些發(fā)送方的數(shù)據(jù)包被接收方處理后又會(huì)向?qū)Ψ桨l(fā)送響應(yīng)冻辩,所以一來(lái)一回需要等待 2 倍的時(shí)間猖腕。

比如,如果被動(dòng)關(guān)閉方?jīng)]有收到斷開連接的最后的 ACK 報(bào)文恨闪,就會(huì)觸發(fā)超時(shí)重發(fā) Fin 報(bào)文倘感,另一方接收到 FIN 后,會(huì)重發(fā) ACK 給被動(dòng)關(guān)閉方咙咽, 一來(lái)一去正好 2 個(gè) MSL老玛。

2MSL 的時(shí)間是從客戶端接收到 FIN 后發(fā)送 ACK 開始計(jì)時(shí)的。如果在 TIME-WAIT 時(shí)間內(nèi)钧敞,因?yàn)榭蛻舳说?ACK 沒有傳輸?shù)椒?wù)端蜡豹,客戶端又接收到了服務(wù)端重發(fā)的 FIN 報(bào)文,那么 2MSL 時(shí)間將重新計(jì)時(shí)溉苛。

在 Linux 系統(tǒng)里 2MSL 默認(rèn)是 60 秒镜廉,那么一個(gè) MSL 也就是 30 秒。Linux 系統(tǒng)停留在 TIME_WAIT 的時(shí)間為固定的 60 秒愚战。

其定義在 Linux 內(nèi)核代碼里的名稱為 TCP_TIMEWAIT_LEN:

define TCP_TIMEWAIT_LEN (60HZ) / how long to wait to destroy TIME-WAIT state, about60seconds */

如果要修改 TIME_WAIT 的時(shí)間長(zhǎng)度娇唯,只能修改 Linux 內(nèi)核代碼里 TCP_TIMEWAIT_LEN 的值,并重新編譯 Linux 內(nèi)核凤巨。

為什么需要 TIME_WAIT 狀態(tài)视乐?

主動(dòng)發(fā)起關(guān)閉連接的一方,才會(huì)有 TIME-WAIT 狀態(tài)敢茁。

需要 TIME-WAIT 狀態(tài)佑淀,主要是兩個(gè)原因:

防止具有相同「四元組」的「舊」數(shù)據(jù)包被收到;

保證「被動(dòng)關(guān)閉連接」的一方能被正確的關(guān)閉彰檬,即保證最后的 ACK 能讓被動(dòng)關(guān)閉方接收伸刃,從而幫助其正常關(guān)閉;

原因一:防止舊連接的數(shù)據(jù)包

假設(shè) TIME-WAIT 沒有等待時(shí)間或時(shí)間過短逢倍,被延遲的數(shù)據(jù)包抵達(dá)后會(huì)發(fā)生什么呢捧颅?

image

接收到歷史數(shù)據(jù)的異常

如上圖黃色框框服務(wù)端在關(guān)閉連接之前發(fā)送的 SEQ = 301 報(bào)文,被網(wǎng)絡(luò)延遲了较雕。

這時(shí)有相同端口的 TCP 連接被復(fù)用后碉哑,被延遲的 SEQ = 301 抵達(dá)了客戶端,那么客戶端是有可能正常接收這個(gè)過期的報(bào)文亮蒋,這就會(huì)產(chǎn)生數(shù)據(jù)錯(cuò)亂等嚴(yán)重的問題扣典。

所以,TCP 就設(shè)計(jì)出了這么一個(gè)機(jī)制慎玖,經(jīng)過 2MSL 這個(gè)時(shí)間贮尖,足以讓兩個(gè)方向上的數(shù)據(jù)包都被丟棄,使得原來(lái)連接的數(shù)據(jù)包在網(wǎng)絡(luò)中都自然消失趁怔,再出現(xiàn)的數(shù)據(jù)包一定都是新建立連接所產(chǎn)生的湿硝。

原因二:保證連接正確關(guān)閉

在 RFC 793 指出 TIME-WAIT 另一個(gè)重要的作用是:

TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.

也就是說薪前,TIME-WAIT 作用是等待足夠的時(shí)間以確保最后的 ACK 能讓被動(dòng)關(guān)閉方接收,從而幫助其正常關(guān)閉关斜。

假設(shè) TIME-WAIT 沒有等待時(shí)間或時(shí)間過短示括,斷開連接會(huì)造成什么問題呢?

image

沒有確保正常斷開的異常

如上圖紅色框框客戶端四次揮手的最后一個(gè) ACK 報(bào)文如果在網(wǎng)絡(luò)中被丟失了蚤吹,此時(shí)如果客戶端 TIME-WAIT 過短或沒有例诀,則就直接進(jìn)入了 CLOSE 狀態(tài)了,那么服務(wù)端則會(huì)一直處在 LASE-ACK 狀態(tài)裁着。

當(dāng)客戶端發(fā)起建立連接的 SYN 請(qǐng)求報(bào)文后繁涂,服務(wù)端會(huì)發(fā)送 RST 報(bào)文給客戶端,連接建立的過程就會(huì)被終止二驰。

如果 TIME-WAIT 等待足夠長(zhǎng)的情況就會(huì)遇到兩種情況:

服務(wù)端正常收到四次揮手的最后一個(gè) ACK 報(bào)文扔罪,則服務(wù)端正常關(guān)閉連接。

服務(wù)端沒有收到四次揮手的最后一個(gè) ACK 報(bào)文時(shí)桶雀,則會(huì)重發(fā) FIN 關(guān)閉連接報(bào)文并等待新的 ACK 報(bào)文矿酵。

所以客戶端在 TIME-WAIT 狀態(tài)等待 2MSL 時(shí)間后,就可以保證雙方的連接都可以正常的關(guān)閉矗积。

TIME_WAIT 過多有什么危害全肮?

如果服務(wù)器有處于 TIME-WAIT 狀態(tài)的 TCP,則說明是由服務(wù)器方主動(dòng)發(fā)起的斷開請(qǐng)求棘捣。

過多的 TIME-WAIT 狀態(tài)主要的危害有兩種:

第一是內(nèi)存資源占用辜腺;

第二是對(duì)端口資源的占用,一個(gè) TCP 連接至少消耗一個(gè)本地端口乍恐;

第二個(gè)危害是會(huì)造成嚴(yán)重的后果的评疗,要知道,端口資源也是有限的茵烈,一般可以開啟的端口為 32768~61000百匆,也可以通過如下參數(shù)設(shè)置指定

net.ipv4.ip_local_port_range

如果服務(wù)端 TIME_WAIT 狀態(tài)過多,占滿了所有端口資源呜投,則會(huì)導(dǎo)致無(wú)法創(chuàng)建新連接加匈。

如何優(yōu)化 TIME_WAIT?

這里給出優(yōu)化 TIME-WAIT 的幾個(gè)方式仑荐,都是有利有弊:

打開 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 選項(xiàng)雕拼;

net.ipv4.tcp_max_tw_buckets

程序中使用 SO_LINGER ,應(yīng)用強(qiáng)制使用 RST 關(guān)閉释漆。

方式一:net.ipv4.tcp_tw_reuse 和 tcp_timestamps

如下的 Linux 內(nèi)核參數(shù)開啟后悲没,則可以復(fù)用處于 TIME_WAIT 的 socket 為新的連接所用篮迎。

net.ipv4.tcp_tw_reuse=1

使用這個(gè)選項(xiàng)男图,還有一個(gè)前提示姿,需要打開對(duì) TCP 時(shí)間戳的支持,即

net.ipv4.tcp_timestamps=1(默認(rèn)即為1)

這個(gè)時(shí)間戳的字段是在 TCP 頭部的「選項(xiàng)」里逊笆,用于記錄 TCP 發(fā)送方的當(dāng)前時(shí)間戳和從對(duì)端接收到的最新時(shí)間戳栈戳。

由于引入了時(shí)間戳,我們?cè)谇懊嫣岬降?2MSL 問題就不復(fù)存在了难裆,因?yàn)橹貜?fù)的數(shù)據(jù)包會(huì)因?yàn)闀r(shí)間戳過期被自然丟棄子檀。

溫馨提醒:net.ipv4.tcp_tw_reuse要慎用,因?yàn)槭褂昧怂捅厝灰蜷_時(shí)間戳的支持net.ipv4.tcp_timestamps乃戈,當(dāng)客戶端與服務(wù)端主機(jī)時(shí)間不同步時(shí)褂痰,客戶端的發(fā)送的消息會(huì)被直接拒絕掉。小林在工作中就遇到過症虑。缩歪。。排查了非常的久

方式二:net.ipv4.tcp_max_tw_buckets

這個(gè)值默認(rèn)為 18000谍憔,當(dāng)系統(tǒng)中處于 TIME_WAIT 的連接一旦超過這個(gè)值時(shí)匪蝙,系統(tǒng)就會(huì)將所有的 TIME_WAIT 連接狀態(tài)重置。

這個(gè)方法過于暴力习贫,而且治標(biāo)不治本逛球,帶來(lái)的問題遠(yuǎn)比解決的問題多,不推薦使用苫昌。

方式三:程序中使用 SO_LINGER

我們可以通過設(shè)置 socket 選項(xiàng)颤绕,來(lái)設(shè)置調(diào)用 close 關(guān)閉連接行為。

struct linger so_linger;so_linger.l_onoff =1;so_linger.l_linger =0;setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));

如果l_onoff為非 0蜡歹, 且l_linger值為 0屋厘,那么調(diào)用close后,會(huì)立該發(fā)送一個(gè)RST標(biāo)志給對(duì)端月而,該 TCP 連接將跳過四次揮手汗洒,也就跳過了TIME_WAIT狀態(tài),直接關(guān)閉父款。

但這為跨越TIME_WAIT狀態(tài)提供了一個(gè)可能溢谤,不過是一個(gè)非常危險(xiǎn)的行為,不值得提倡憨攒。

如果已經(jīng)建立了連接世杀,但是客戶端突然出現(xiàn)故障了怎么辦?

TCP 有一個(gè)機(jī)制是备渭活機(jī)制瞻坝。這個(gè)機(jī)制的原理是這樣的:

定義一個(gè)時(shí)間段,在這個(gè)時(shí)間段內(nèi)杏瞻,如果沒有任何連接相關(guān)的活動(dòng)所刀,TCP 毖眉觯活機(jī)制會(huì)開始作用,每隔一個(gè)時(shí)間間隔浮创,發(fā)送一個(gè)探測(cè)報(bào)文忧吟,該探測(cè)報(bào)文包含的數(shù)據(jù)非常少,如果連續(xù)幾個(gè)探測(cè)報(bào)文都沒有得到響應(yīng)斩披,則認(rèn)為當(dāng)前的 TCP 連接已經(jīng)死亡溜族,系統(tǒng)內(nèi)核將錯(cuò)誤信息通知給上層應(yīng)用程序。

在 Linux 內(nèi)核可以有對(duì)應(yīng)的參數(shù)可以設(shè)置笨殉粒活時(shí)間煌抒、保活探測(cè)的次數(shù)厕倍、贝菝担活探測(cè)的時(shí)間間隔,以下都為默認(rèn)值:

net.ipv4.tcp_keepalive_time=7200net.ipv4.tcp_keepalive_intvl=75net.ipv4.tcp_keepalive_probes=9

tcp_keepalive_time=7200:表示卑笄啵活時(shí)間是 7200 秒(2小時(shí))诬像,也就 2 小時(shí)內(nèi)如果沒有任何連接相關(guān)的活動(dòng),則會(huì)啟動(dòng)保活機(jī)制

tcp_keepalive_intvl=75:表示每次檢測(cè)間隔 75 秒庇楞;

tcp_keepalive_probes=9:表示檢測(cè) 9 次無(wú)響應(yīng)烙心,認(rèn)為對(duì)方是不可達(dá)的匙瘪,從而中斷本次的連接荐吵。

也就是說在 Linux 系統(tǒng)中遥倦,最少需要經(jīng)過 2 小時(shí) 11 分 15 秒才可以發(fā)現(xiàn)一個(gè)「死亡」連接艺演。

image

這個(gè)時(shí)間是有點(diǎn)長(zhǎng)的肿男,我們也可以根據(jù)實(shí)際的需求,對(duì)以上的保活相關(guān)的參數(shù)進(jìn)行設(shè)置豪娜。

如果開啟了 TCP 辈驼停活,需要考慮以下幾種情況:

第一種瘤载,對(duì)端程序是正常工作的否灾。當(dāng) TCP 保活的探測(cè)報(bào)文發(fā)送給對(duì)端, 對(duì)端會(huì)正常響應(yīng)鸣奔,這樣 TCP 蹦迹活時(shí)間會(huì)被重置,等待下一個(gè) TCP 笨胬辏活時(shí)間的到來(lái)扣汪。

第二種,對(duì)端程序崩潰并重啟锨匆。當(dāng) TCP 闭副穑活的探測(cè)報(bào)文發(fā)送給對(duì)端后,對(duì)端是可以響應(yīng)的恐锣,但由于沒有該連接的有效信息茅主,會(huì)產(chǎn)生一個(gè) RST 報(bào)文,這樣很快就會(huì)發(fā)現(xiàn) TCP 連接已經(jīng)被重置土榴。

第三種暗膜,是對(duì)端程序崩潰,或?qū)Χ擞捎谄渌驅(qū)е聢?bào)文不可達(dá)鞭衩。當(dāng) TCP 毖眩活的探測(cè)報(bào)文發(fā)送給對(duì)端后,石沉大海论衍,沒有響應(yīng)瑞佩,連續(xù)幾次,達(dá)到迸魈ǎ活探測(cè)次數(shù)后炬丸,TCP 會(huì)報(bào)告該 TCP 連接已經(jīng)死亡

03 Socket 編程

針對(duì) TCP 應(yīng)該如何 Socket 編程蜒蕾?

image

基于 TCP 協(xié)議的客戶端和服務(wù)器工作

服務(wù)端和客戶端初始化 socket稠炬,得到文件描述符;

服務(wù)端調(diào)用 bind咪啡,將綁定在 IP 地址和端口;

服務(wù)端調(diào)用 listen首启,進(jìn)行監(jiān)聽;

服務(wù)端調(diào)用 accept撤摸,等待客戶端連接毅桃;

客戶端調(diào)用 connect褒纲,向服務(wù)器端的地址和端口發(fā)起連接請(qǐng)求;

服務(wù)端 accept 返回用于傳輸?shù)?socket 的文件描述符钥飞;

客戶端調(diào)用 write 寫入數(shù)據(jù)莺掠;服務(wù)端調(diào)用 read 讀取數(shù)據(jù);

客戶端斷開連接時(shí),會(huì)調(diào)用 close,那么服務(wù)端 read 讀取數(shù)據(jù)的時(shí)候童叠,就會(huì)讀取到了 EOF,待處理完數(shù)據(jù)后唇兑,服務(wù)端調(diào)用 close,表示連接關(guān)閉膀估。

這里需要注意的是,服務(wù)端調(diào)用 accept 時(shí)耻讽,連接成功了會(huì)返回一個(gè)已完成連接的 socket察纯,后續(xù)用來(lái)傳輸數(shù)據(jù)。

所以针肥,監(jiān)聽的 socket 和真正用來(lái)傳送數(shù)據(jù)的 socket饼记,是「兩個(gè)」 socket,一個(gè)叫作監(jiān)聽 socket慰枕,一個(gè)叫作已完成連接 socket具则。

成功連接建立之后,雙方開始通過 read 和 write 函數(shù)來(lái)讀寫數(shù)據(jù)具帮,就像往一個(gè)文件流里面寫東西一樣博肋。

listen 時(shí)候參數(shù) backlog 的意義?

Linux內(nèi)核中會(huì)維護(hù)兩個(gè)隊(duì)列:

未完成連接隊(duì)列(SYN 隊(duì)列):接收到一個(gè) SYN 建立連接請(qǐng)求蜂厅,處于 SYN_RCVD 狀態(tài)匪凡;

已完成連接隊(duì)列(Accpet 隊(duì)列):已完成 TCP 三次握手過程,處于 ESTABLISHED 狀態(tài)掘猿;

image

SYN 隊(duì)列 與 Accpet 隊(duì)列

intlisten(intsocketfd,intbacklog)

參數(shù)一 socketfd 為 socketfd 文件描述符

參數(shù)二 backlog病游,這參數(shù)在歷史有一定的變化

在早期 Linux 內(nèi)核 backlog 是 SYN 隊(duì)列大小,也就是未完成的隊(duì)列大小稠通。

在 Linux 內(nèi)核 2.2 之后衬衬,backlog 變成 accept 隊(duì)列,也就是已完成連接建立的隊(duì)列長(zhǎng)度改橘,所以現(xiàn)在通常認(rèn)為 backlog 是 accept 隊(duì)列滋尉。

accept 發(fā)送在三次握手的哪一步?

我們先看看客戶端連接服務(wù)端時(shí)飞主,發(fā)送了什么兼砖?

image

客戶端連接服務(wù)端

客戶端的協(xié)議棧向服務(wù)器端發(fā)送了 SYN 包奸远,并告訴服務(wù)器端當(dāng)前發(fā)送序列號(hào) client_isn,客戶端進(jìn)入 SYNC_SENT 狀態(tài)讽挟;

服務(wù)器端的協(xié)議棧收到這個(gè)包之后懒叛,和客戶端進(jìn)行 ACK 應(yīng)答,應(yīng)答的值為 client_isn+1耽梅,表示對(duì) SYN 包 client_isn 的確認(rèn)薛窥,同時(shí)服務(wù)器也發(fā)送一個(gè) SYN 包,告訴客戶端當(dāng)前我的發(fā)送序列號(hào)為 server_isn眼姐,服務(wù)器端進(jìn)入 SYNC_RCVD 狀態(tài)诅迷;

客戶端協(xié)議棧收到 ACK 之后,使得應(yīng)用程序從 connect 調(diào)用返回众旗,表示客戶端到服務(wù)器端的單向連接建立成功罢杉,客戶端的狀態(tài)為 ESTABLISHED,同時(shí)客戶端協(xié)議棧也會(huì)對(duì)服務(wù)器端的 SYN 包進(jìn)行應(yīng)答贡歧,應(yīng)答數(shù)據(jù)為 server_isn+1滩租;

應(yīng)答包到達(dá)服務(wù)器端后,服務(wù)器端協(xié)議棧使得 accept 阻塞調(diào)用返回利朵,這個(gè)時(shí)候服務(wù)器端到客戶端的單向連接也建立成功律想,服務(wù)器端也進(jìn)入 ESTABLISHED 狀態(tài)。

從上面的描述過程绍弟,我們可以得知客戶端 connect 成功返回是在第二次握手技即,服務(wù)端 accept 成功返回是在三次握手成功之后。

客戶端調(diào)用 close 了樟遣,連接是斷開的流程是什么而叼?

我們看看客戶端主動(dòng)調(diào)用了 close,會(huì)發(fā)生什么豹悬?

image

客戶端調(diào)用 close 過程

客戶端調(diào)用 close澈歉,表明客戶端沒有數(shù)據(jù)需要發(fā)送了,則此時(shí)會(huì)向服務(wù)端發(fā)送 FIN 報(bào)文屿衅,進(jìn)入 FIN_WAIT_1 狀態(tài)埃难;

服務(wù)端接收到了 FIN 報(bào)文,TCP 協(xié)議棧會(huì)為 FIN 包插入一個(gè)文件結(jié)束符 EOF 到接收緩沖區(qū)中涤久,應(yīng)用程序可以通過 read 調(diào)用來(lái)感知這個(gè) FIN 包涡尘。這個(gè) EOF 會(huì)被放在已排隊(duì)等候的其他已接收的數(shù)據(jù)之后,這就意味著服務(wù)端需要處理這種異常情況响迂,因?yàn)?EOF 表示在該連接上再無(wú)額外數(shù)據(jù)到達(dá)考抄。此時(shí),服務(wù)端進(jìn)入 CLOSE_WAIT 狀態(tài)蔗彤;

接著川梅,當(dāng)處理完數(shù)據(jù)后疯兼,自然就會(huì)讀到 EOF,于是也調(diào)用 close 關(guān)閉它的套接字贫途,這會(huì)使得會(huì)發(fā)出一個(gè) FIN 包吧彪,之后處于 LAST_ACK 狀態(tài);

客戶端接收到服務(wù)端的 FIN 包丢早,并發(fā)送 ACK 確認(rèn)包給服務(wù)端姨裸,此時(shí)客戶端將進(jìn)入 TIME_WAIT 狀態(tài);

服務(wù)端收到 ACK 確認(rèn)包后怨酝,就進(jìn)入了最后的 CLOSE 狀態(tài)傀缩;

客戶端進(jìn)過 2MSL 時(shí)間之后,也進(jìn)入 CLOSED 狀態(tài)农猬;

最新整理的Java技術(shù)干貨文檔資料:【Java核心知識(shí)點(diǎn)整理】涵蓋29個(gè)Java核心技術(shù)詳解赡艰,JVM,Redis斤葱,Nginx慷垮,Spring Boot,Spring Cloud苦掘,Kafka换帜,并發(fā)編程楔壤,Tomcat鹤啡,MyBatis,BAT面試題蹲嚣,Java技術(shù)精講視頻等递瑰。轉(zhuǎn)發(fā)+關(guān)注,私信回復(fù)“干貨”即可獲得免費(fèi)領(lǐng)取方式隙畜。

掌握了這些知識(shí)點(diǎn)抖部,面試時(shí)在候選人中又可以?shī)Z目不少,暴擊9999點(diǎn)议惰。機(jī)會(huì)都是留給有準(zhǔn)備的人慎颗,只有充足的準(zhǔn)備,才可能讓自己可以在候選人中脫穎而出言询。

作者:Java技術(shù)劍
鏈接:http://www.reibang.com/p/ac16c211729a
來(lái)源:簡(jiǎn)書
著作權(quán)歸作者所有俯萎。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處运杭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末夫啊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辆憔,更是在濱河造成了極大的恐慌撇眯,老刑警劉巖报嵌,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異熊榛,居然都是意外死亡锚国,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門来候,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)跷叉,“玉大人,你說我怎么就攤上這事营搅≡菩” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵转质,是天一觀的道長(zhǎng)园欣。 經(jīng)常有香客問我,道長(zhǎng)休蟹,這世上最難降的妖魔是什么沸枯? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮赂弓,結(jié)果婚禮上绑榴,老公的妹妹穿的比我還像新娘。我一直安慰自己盈魁,他們只是感情好翔怎,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杨耙,像睡著了一般赤套。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上珊膜,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天容握,我揣著相機(jī)與錄音,去河邊找鬼车柠。 笑死剔氏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竹祷。 我是一名探鬼主播谈跛,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼溶褪!你這毒婦竟也來(lái)了币旧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤猿妈,失蹤者是張志新(化名)和其女友劉穎吹菱,沒想到半個(gè)月后巍虫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鳍刷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年占遥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片输瓜。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瓦胎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尤揣,到底是詐尸還是另有隱情搔啊,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布北戏,位于F島的核電站负芋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嗜愈。R本人自食惡果不足惜旧蛾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蠕嫁。 院中可真熱鬧锨天,春花似錦、人聲如沸剃毒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)迟赃。三九已至陪拘,卻和暖如春厂镇,著一層夾襖步出監(jiān)牢的瞬間纤壁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工捺信, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酌媒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓迄靠,卻偏偏與公主長(zhǎng)得像秒咨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掌挚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349