其中TCP處理transport層,主要是用來建立可靠的連接。 而建立連接的基礎,是他豐富的報文內容(md~超級多).我們先來解釋一下。 首先,我們TCP3次握手用的報文就是綠色的"TCP Flags"內容。 通過發送ACK,SYN包實現。具體涉及的Tag詳見:
- Source Port / Destination Port:這個就是客戶端口(源端口)和服務器端口(目的端口). 端口就是用來區別主機中的不同進程,通過結合源IP和目的IP結合,得出唯一的TCP連接。
- Sequence Number(seqNumber): 一般由 客戶端發送,用來表示報文段中第一個數據字節在數據流中的序號,主要用來解決網絡包亂序的問題。
- Acknowledgment Number(ACK): 即就是用來存放客戶端發來的seqNumber的下一個信號(seqNumber+1). 只有當 TCP flags中的ACK為1時才有效. 主要是用來解決不丟包的問題。
- TCP flags: TCP中有6個首部,用來控制TCP連接的狀態.取值為0,1.這6個有:URG,ACK,PSH,RST,SYN,FIN.
- URG 當為1時,用來保證TCP連接不被中斷, 并且將該次TCP內容數據的緊急程度提升(就是告訴電腦,你丫趕快把這個給resolve了)
- ACK 通常是服務器端返回的。 用來表示應答是否有效。 1為有效,0為無效
- PSH 表示,當數據包得到后,立馬給應用程序使用(PUSH到最頂端)
- RST 用來確保TCP連接的安全。 該flag用來表示 一個連接復位的請求。 如果發生錯誤連接,則reset一次,重新連。當然也可以用來拒絕非法數據包。
- SYN 同步的意思,通常是由客戶端發送,用來建立連接的。第一次握手時: SYN:1 , ACK:0. 第二次握手時: SYN:1 ACK:1
- FIN 用來表示是否結束該次TCP連接。 通常當你的數據發送完后,會自動帶上FIN 然后斷開連接
TCP 3次握手
- 第一次握手. 客戶端向服務器發送一個SYN包,并且添加上seqNumber(假設為x),然后進入SYN_SEND狀態,并且等待服務器的確認。
- 第二次握手: 服務器接受SYN包,并且進行確認,如果該請求有效,則將TCP flags中的ACK 標志位置1, 然后將AckNumber置為(seqNumber+1),并且再添加上自己的seqNumber(y), 完成后,返回給客戶端.服務器進入SYN_RECV狀態.(這里服務端是發送SYN+ACK包)
- 第三次握手 客戶端接受ACK+SYN報文后,獲取到服務器發送AckNumber(y), 并且 將新頭部的AckNumber變為(y+1).然后發送給服務器,完成TCP3次連接。此時服務器和客戶端都進入ESTABLISHED狀態.
假如是2次的話, 可能會出現這樣一個情況。
- 當客戶端發送一次請求A后,但是A在網絡延遲了很久, 接著客戶端又發送了一次B,但是此時A已經無效了。 接著服務器相應了B,并返回TCP連接頭,建立連接(這里就2次哈)。 然后,A 歷經千山萬水終于到服務器了, 服務器一看有請求來了,則接受,由于一開始A帶著的TCP格式都是正確的,那么服務器,理所應當的也返回成功連接的flag,但是,此時客戶端已經判斷該次請求無效,廢棄了。 然后服務器,就這么一直掛著(浪費資源),造成的一個問題是,md, 這個鍋是誰的? 所以,為了保險起見,再補充一次連接就可以了。所以3次是最合適的。在Chinese中,以3為起稱為多,如果你用4,5,6,7,8...次的話,這不更浪費嗎?
TCP4次揮手
- 第一次揮手: A機感覺此時如果keep-alive比較浪費資源,則他提出了分手的請求。設置SeqNumber和AckNumber之后,向B機發送FIN包, 表示我這已經沒有數據給你了。然后A機進入FIN_WAIT_1狀態
- 第二次揮手:B機收到了A機的FIN包,已經知道了A機沒有數據再發送了。此時B機會給A機發送一個ACK包,并且將AckNumber 變為 A機傳輸來的SeqNumber+1. 當A機接受到之后,則變為FIN_WAIT_2狀態。表示已經得到B機的許可,可以進行關閉操作。不過此時,B機還是可以向A機發送請求的。
- 第三次揮手 B機向A機發送FIN包,請求關閉,相當于告訴A機,我這里也沒有你要的數據了。然后B機進入CLOSE_WAIT狀態.(這里還需要帶上SeqNumber,大家看圖說話就可以了)
- 第四次揮手 A機接收到B機的FIN包之后,然后同樣,發送一個ACK包給B機。 B機接受到之后,就斷開了。 而A機 會等待2MSL之后,如果沒有回復,確保服務器端確實是關閉了。然后A機也可以關閉連接。A,B都進入了CLOSE狀態.
2MSL=2*MSL. 而MSL其實就是Maximum Segment Lifetime,中文意思就是報文最大生存時間。RFC 793中規定MSL為2分鐘,實際應用中常用的是30秒,1分鐘和2分鐘等。 同樣上面的TIME_WAT狀態其實也就是2MSL狀態。 如果超過改時間,則會將該報文廢棄,然后直接進入CLOSED狀態.
常見瓶頸
TCP網絡應用出問題,十有八九是以下兩種情況:
主動關閉連接方出現大量TIME_WAIT狀態。
被動關閉連接方出現大量CLOSE_WAIT狀態
主動關閉方在關閉連接后,需要發送ACK,假設ACK丟失了,被動關閉一方會重發它的FIN。主動關閉方必須維持一個有效狀態信息(TIMEWAIT狀態下維持),以便能夠重發ACK。如果主動關閉的socket不維持這種狀態而進入CLOSED狀態,那么主動關閉的socket在處于CLOSED狀態時,接收到FIN后將會響應一個RST。被動關閉一方接收到RST后會認為出錯了。這就是為什么socket在關閉后,仍然處于TIME_WAIT狀態的第一個原因,因為它要等待以便重發ACK。第二個原因是確保連接復用時沒有殘存的數據。TCP不允許新連接復用TIME_WAIT狀態下的socket。處于TIME_WAIT狀態的socket在等待兩倍的MSL時間以后,將會轉變為CLOSED狀態,此時通道內不會存在殘存數據。
應用程序無法解決TIME_WAIT問題,我想了一個甩鍋的辦法是讓客戶端斷開連接,因為誰主動斷開誰面臨TIMEWAIT。CLOSE_WAIT需要重點關注。被動關閉方在發送ACK以后會處于CLOSE_WAIT狀態,此時只要調用close方法就會發送FIN包,脫離CLOSE_WAIT狀態。