一酸休、傳輸控制協(xié)議TCP簡介
1.1 簡介
TCP(Transmission Control Protocol) 傳輸控制協(xié)議乞巧,是一種 面向連接的口蝠、可靠的器钟、基于字節(jié)流的傳輸層 通信協(xié)議。
TCP是一種面向連接(連接導向)的妙蔗、可靠的基于字節(jié)流的傳輸層通信協(xié)議傲霸。TCP將用戶數(shù)據(jù)打包成報文段,它發(fā)送后啟動一個定時器眉反,另一端收到的數(shù)據(jù)進行確認昙啄、對失序的數(shù)據(jù)重新排序、丟棄重復數(shù)據(jù)禁漓。
TCP把連接作為最基本的對象跟衅,每一條TCP連接都有兩個端點,這種端點我們叫作套接字(socket)播歼,將端口號拼接到IP地址即構(gòu)成了套接字伶跷,例如 192.1.1.6:50030
1.2 特點
- 面向連接的、可靠的秘狞、基于字節(jié)流的 傳輸層 通信協(xié)議
- 將應用層的數(shù)據(jù)流分割成文段并發(fā)送給目標節(jié)點的TCP層
- 數(shù)據(jù)包都有序號叭莫,對方收到則發(fā)送ACK確認,未收到則重傳
- 使用校驗和來檢驗數(shù)據(jù)在傳輸過程中是否有誤
二烁试、TCP報文頭
1雇初、源端口(Source Port)/ 目的端口(Destination Port):他們各占2個字節(jié),標示該段報文來自哪里(源端口)以及要傳給哪個上層協(xié)議或應用程序(目的端口)减响。進行tcp通信時靖诗,一般client是通過系統(tǒng)自動選擇的臨時端口號,而服務器一般是使用知名服務端口號或者自己指定的端口號
(比如DNS協(xié)議對應端口53支示,HTTP協(xié)議對應80)
2刊橘、序號(Sequence Number):占據(jù)四個字節(jié),TCP是面向字節(jié)流的颂鸿,TCP連接中傳送的字節(jié)流中的每個字節(jié)都按順序編號促绵,例如如一段報文的序號字段值是107
,而攜帶的數(shù)據(jù)共有100個字段
嘴纺,如果有下一個報文過來败晴,那么序號就從207(100+107)
開始,整個要傳送的字節(jié)流的起始序號必須要在連接建立時設置栽渴。首部中的序號字段值指的是本報文段所發(fā)送的數(shù)據(jù)的第一個字節(jié)的序號
3尖坤、確認序號(Acknowledgment Number):4個字節(jié),是期望收到對方下一個報文段的第一個數(shù)據(jù)字節(jié)的序號闲擦,若確認號=N糖驴,則表明:到序號N-1為止的所有數(shù)據(jù)都已正確收到僚祷,例如:B收到A發(fā)送過來的報文,其序列號字段是301
贮缕,而數(shù)據(jù)長度是200字節(jié)
,這表明了B正確的收到了A到序號500(301+200-1)
為止的數(shù)據(jù)俺榆,因此B希望收到A的下一個數(shù)據(jù)序號是501
感昼,于是B在發(fā)送給A的確認報文段中,會把ACK確認號設置為501
4罐脊、數(shù)據(jù)偏移(Offset):4個字節(jié)定嗓。指出TCP報文段的數(shù)據(jù)起始處距離報文段的起始處有多遠,這個字段實際上是指出TCP報文段的首部長度萍桌。由于首部中還有長度不確定的選項字段宵溅,因此數(shù)據(jù)偏移字段是必要的。單位是32位字上炎,也就是4字節(jié)恃逻,4位二進制最大表示15,所以數(shù)據(jù)偏移也就是TCP首部最大60字節(jié)
5藕施、保留(Reserved):6個字節(jié)寇损。保留域
6、TCP Flags:控制位裳食,由八個標志位組成矛市,每個標志位表示控制的功能,我們主要來介紹TCP Flags中常用的六個簿寂,
URG(緊急指針標志):當
URG=1
時胳泉,表明緊急指針字段有效爹脾。它告訴系統(tǒng)此報文段中有緊急數(shù)據(jù),應盡快傳送(相當于高優(yōu)先級的數(shù)據(jù))找田,而不要按原來的排隊順序來傳送。例如径密,已經(jīng)發(fā)送了很長的一個程序在主機上運行午阵。但后來發(fā)現(xiàn)了一些問題,需要取消該程序的運行享扔。因此用戶從鍵盤發(fā)出中斷命令底桂。如果不使用緊急數(shù)據(jù),那么這兩個字符將存儲在接收TCP的緩存末尾惧眠。只有在所有的數(shù)據(jù)被處理完畢后這兩個字符才被交付接收方的應用進程籽懦。這樣做就浪費了許多時間ACK(確認序號標志):當
ACK=1
時確認號字段有效。當ACK=0
時氛魁,確認號無效暮顺。TCP規(guī)定厅篓,在連接建立后所有的傳送的報文段都必須把ACK置1PSH(push標志):當兩個應用進程進行交互式的通信時,有時在一端的應用進程希望在鍵入一個命令后立即就能收到對方的響應捶码。在這種情況下羽氮,TCP就可以使用推送操作。這時惫恼,發(fā)送方TCP把PSH置1档押,并立即創(chuàng)建一個報文段發(fā)送出去。接收方TCP收到PSH=1的報文段祈纯,就盡快地交付接收應用進程令宿,而不再等到整個緩存都填滿了后向上交付
RST(重置連接標志):TCP連接中出現(xiàn)嚴重差錯(如由于主機崩潰或其他原因),必須釋放連接腕窥,然后再重新建立運輸連接粒没,可以用來拒絕一個非法的報文段或拒絕打開一個連接
SYN(同步序號,用于建立連接過程):在連接建立時用來同步序號簇爆。當
SYN=1而ACK=0
時癞松,表明這是一個連接請求報文段。對方若同意建立連接冕碟,則應在相應的報文段中使用SYN=1和ACK=1
拦惋。因此,SYN置為1就表示這是一個連接請求或連接接受保溫安寺。FIN(finish標志厕妖,用于釋放連接):當
FIN=1
時,表明此報文段的發(fā)送方的數(shù)據(jù)已發(fā)送完畢挑庶,并要求釋放運輸連接
7言秸、窗口(Window):是TCP流量控制的一個手段
。這里說的窗口迎捺,指的是接收通告窗口(Receiver Window举畸,RWND)。它告訴對方本端的TCP接收緩沖區(qū)還能容納多少字節(jié)的數(shù)據(jù)凳枝,這樣就可以控制發(fā)送數(shù)據(jù)的速度
8抄沮、檢驗和(Checksum):檢驗范圍包括首部和數(shù)據(jù)兩部分,由發(fā)送端填充岖瑰,接收端對TCP報文段執(zhí)行CRC算法以檢驗TCP報文段在傳輸過程中是否損壞叛买。這也是TCP可靠傳輸?shù)囊粋€重要保障
9、緊急指針(Urgent Pointer):緊急指針僅在URG=1時才有意義蹋订,它指出本報文段中的緊急數(shù)據(jù)的字節(jié)數(shù)(緊急數(shù)據(jù)結(jié)束后就是普通數(shù)據(jù))率挣。因此,緊急指針指出了緊急數(shù)據(jù)的末尾在報文段中的位置露戒。當所有緊急數(shù)據(jù)都處理完時椒功,TCP就告訴應用程序恢復到正常操作捶箱。值得注意的是,即使窗口為零時也可發(fā)送緊急數(shù)據(jù)动漾。
10丁屎、TCP可選項(TCP Options):長度可變,最長可達40字節(jié)谦炬。當沒有使用“選項”時悦屏,TCP的首部長度是20字節(jié)。
三键思、TCP的三次握手
所謂三次握手(Three-Way Handshake)即建立TCP連接,就是指建立一個TCP連接時甫贯,需要客戶端和服務端總共發(fā)送3個包以確認連接的建立吼鳞。在socket編程中,這一過程由客戶端執(zhí)行connect來觸發(fā)叫搁,整個流程如下圖所示:
在TCP/IP協(xié)議中赔桌,TCP協(xié)議提供可靠的連接服務,采用三次握手建立一個連接渴逻。
第一次握手: 建立連接時疾党,客戶端發(fā)送SYN包(syn=j)到服務器,并進入SYN_SEND狀態(tài)惨奕,等待服務器確認雪位,SYN:同步序列編號(Synchronize Sequence Numbers)。
第二次握手: 服務器收到 SYN 包梨撞,必須確認客戶的 SYN(ack=j+1)雹洗,同時自己也發(fā)送一個SYN包(syn=k),即SYN+ACK包卧波,此時服務器進入SYN_RECV狀態(tài)时肿;
第三次握手: 客戶端收到服務器的SYN + ACK包,向服務器發(fā)送確認包ACK(ack=k+1)港粱,此包發(fā)送完畢螃成,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態(tài),完成三次握手查坪。
3.1 為什么需要三次握手才能建立連接
- 為了初始化Sequence Number 的初始值寸宏,實現(xiàn)可靠數(shù)據(jù)傳輸, TCP 協(xié)議的通信雙方咪惠, 都必須維護一個序列號击吱, 以標識發(fā)送出去的數(shù)據(jù)包中, 哪些是已經(jīng)被對方收到的遥昧。 三次握手的過程即是通信雙方相互告知序列號起始值覆醇, 并確認對方已經(jīng)收到了序列號起始值的必經(jīng)步驟
- 如果只是兩次握手朵纷, 至多只有連接發(fā)起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認
3.2 首次握手的隱患——SYN超時
一永脓、問題起因分析:
- 服務器收到客戶端的SYN袍辞,回復SYN和ACK的時候未收到ACK確認
- 服務器不斷重試直至超時,Linux默認等待63秒才斷開連接常摧;(重復5次【不包括第一次】搅吁,從1秒開始,每次重試都翻倍:1+2+4+8+16+32=63秒)
二落午、針對SYN Flood的防護措施:
- SYN隊列滿后谎懦,通過tcp_syncookies參數(shù)會發(fā)SYN cookie【源端口+目標端口+時間戳組成】
- 若為正常連接則Client會回發(fā)SYN Cookie,直接建立連接溃斋;
3.3 苯缋梗活機制:
當我們建立連接后,Client出現(xiàn)故障怎么辦梗劫?
- 向?qū)Ψ桨l(fā)送毕淼椋活探測報文,如果未收到相應則繼續(xù)發(fā)送梳侨;
- 嘗試次數(shù)達到彬韧活探測數(shù)仍未收到相應則中斷連接;
四走哺、TCP的四次揮手
所謂四次揮手(Four-Way Wavehand)即終止TCP連接蚯嫌,就是指斷開一個TCP連接時,需要客戶端和服務端總共發(fā)送4個包以確認連接的斷開割坠。在socket編程中齐帚,這一過程由客戶端或服務端任一方執(zhí)行close來觸發(fā),整個流程如下圖所示:
由于TCP連接時全雙工的彼哼,因此对妄,每個方向都必須要單獨進行關(guān)閉,這一原則是當一方完成數(shù)據(jù)發(fā)送任務后敢朱,發(fā)送一個FIN來終止這一方向的連接剪菱,收到一個FIN只是意味著這一方向上沒有數(shù)據(jù)流動了,即不會再收到數(shù)據(jù)了拴签,但是在這個TCP連接上仍然能夠發(fā)送數(shù)據(jù)孝常,直到這一方向也發(fā)送了FIN。首先進行關(guān)閉的一方將執(zhí)行主動關(guān)閉蚓哩,而另一方則執(zhí)行被動關(guān)閉构灸。
- 第一次揮手: Client發(fā)送一個FIN,用來關(guān)閉Client到Server的數(shù)據(jù)傳送岸梨,Client進入FIN_WAIT_1狀態(tài)
- 第二次揮手: Server收到FIN后喜颁,發(fā)送一個ACK給Client稠氮,確認序號為收到序號+1(與SYN相同,一個FIN占用一個序號)半开,Server進入CLOSE_WAIT狀態(tài)
- 第三次揮手: Server發(fā)送一個FIN隔披,用來關(guān)閉Server到Client的數(shù)據(jù)傳送,Server進入LAST_ACK狀態(tài)
- 第四次揮手: Client收到FIN后寂拆,Client進入TIME_WAIT狀態(tài)奢米,接著發(fā)送一個ACK給Server,確認序號為收到序號+1纠永,Server進入CLOSED狀態(tài)鬓长,完成四次揮手
一、為什么會有TIME_WAIT狀態(tài)
客戶端連接在收到服務器的結(jié)束報文段之后尝江,不會直接進入CLOSED狀態(tài)痢士,而是轉(zhuǎn)移到TIME_WAIT狀態(tài)。在這個狀態(tài)茂装,客戶端連接要等待一段長為2MSL,即兩倍的報文段最大生存時間善延,才能完全關(guān)閉少态,其原因主要有兩點:
- 確保有足夠的時間放對方收到ACK包
- 避免新舊連接混淆
二、為什么需要四次握手才能斷開連接
因為TCP連接是全雙工的網(wǎng)絡協(xié)議易遣,允許同時通信的雙方同時進行數(shù)據(jù)的收發(fā)彼妻,同樣也允許收發(fā)兩個方向的連接被獨立關(guān)閉,以避免client數(shù)據(jù)發(fā)送完畢豆茫,向server發(fā)送FIN關(guān)閉連接侨歉,而server還有發(fā)送到client的數(shù)據(jù)沒有發(fā)送完畢的情況。所以關(guān)閉TCP連接需要進行四次握手揩魂,每次關(guān)閉一個方向上的連接需要FIN和ACK兩次握手幽邓,發(fā)送發(fā)和接收方都需要FIN報文和ACK報文
三、服務器出現(xiàn)大量CLOSE_WAIT狀態(tài)的原因
是由于對方關(guān)閉socket連接火脉,我方忙于讀或?qū)懬6妫瑳]有及時關(guān)閉連接
當客戶端因為某種原因先于服務端發(fā)出了FIN信號,就會導致服務端被動關(guān)閉倦挂,若服務端不主動關(guān)閉socket發(fā)FIN給Client畸颅,此時服務端Socket會處于CLOSE_WAIT狀態(tài)(而不是LAST_ACK狀態(tài))。通常來說方援,一個CLOSE_WAIT會維持至少2個小時的時間(系統(tǒng)默認超時時間的是7200秒没炒,也就是2小時)。如果服務端程序因某個原因?qū)е孪到y(tǒng)造成一堆CLOSE_WAIT消耗資源犯戏,那么通常是等不到釋放那一刻送火,系統(tǒng)就已崩潰
解決:
1拳话、檢查代碼,特別是釋放資源的代碼
2漾脂、檢查配置假颇,特別是處理請求的線程配置
Linux的檢查代碼:netstat -n | awk '/^tcp/{++S[$NF]}END{for(a in S) print a,S[a]}'
五、總結(jié)
到這里TCP的三次握手四次揮手就講完了骨稿,好久都沒有寫技術(shù)文章了笨鸡,寫了一下,感覺還挺好的坦冠,上面是博主的認識形耗,有寫的不好的地方,大家可以在評論區(qū)討論或者提問辙浑,博主看到了會第一時間回復大家激涤,最近也準備開始面試了,先好好準備一下判呕,希望今年可以找到心滿意足的工作倦踢,也希望今年面試的小伙伴們都有一個好的office,大家一起加油侠草,我是牧小農(nóng)辱挥,我喂自己帶鹽,大家加油边涕。