最近在重溫計算機網絡TCP/IP協議簇,TCP的三次握手四次揮手能夠說是面試中有關計算機網絡部分常常被拿出來問的部分,因此這裏就作一個整理。javascript
傳輸控制協議(TCP,Transmission Control Protocol)是一種面向鏈接的、可靠的、基於字節流的傳輸層通訊協議,由IETF的RFC 793定義。
TCP 是面向鏈接的傳輸層協議,面向鏈接是指發送數據以前必須在兩端創建鏈接。創建鏈接的方法是「三次握手」。前端
TCP 提供可靠傳輸服務,實行「順序控制」或「重發控制」機制。java
TCP提供全雙工通訊,容許通訊雙方的應用程序在任什麼時候候都能發送數據。TCP鏈接的兩端都設有緩存,用來臨時存放雙向通訊的數據。面試
TCP還具有「流控制(流量控制)」、「擁塞控制」、提升網絡利用率等衆多功能。當網絡出現擁塞的時候,TCP可以減少向網絡注入數據的速率和數量,緩解擁塞。算法
TCP僅支持單播傳輸,每條TCP傳輸鏈接只能有兩個端點,只能進行點對點的數據傳輸,不支持多播和廣播傳輸方式。編程
許多應用層協議都是基於TCP協議:segmentfault
FTP
:文件傳輸協議;SSH
:安全登陸、文件傳送(SCP)和端口重定向;Telnet
:不安全的文本傳送;SMTP
:簡單郵件傳輸協議Simple Mail Transfer Protocol (E-mail);HTTP
:超文本傳送協議 (WWW);TCP/IP協議的每一個分層中,都會對所發送的數據附加一個首部,在這個首部中包含了該層必要的信息,如發送的目標地址以及協議相關信息。一般,爲協議提供的信息爲包首部,所要發送的內容爲數據。在下一層的角度看,從上一層收到的包所有都被認爲是本層的數據。緩存
雖然TCP以字節流方式進行傳輸,但TCP傳輸的數據單元倒是報文段。TCP報文段分爲TCP首部和數據部分,TCP報文段首部的前20個字節是固定的,後面有4*n字節根據須要動態添加的選項,最大長度爲40字節。安全
因此要理解TCP協議,首先要了解TCP報文段的首部。服務器
TCP報文段首部中各部分的含義:
序號
: 佔4個字節,範圍是[0,2^32],TCP是面向字節流的,每一個字節都是按順序編號。例如一個報文段,序號字段是201,攜帶數據長度是100,那麼第一個數據的序號就是201,最後一個就是300。當達到最大範圍,又從0開始。確認號
: 佔4個字節,是指望收到對方下一個報文段的第一個字節的序號。若確認號=N,則表示序號N前全部的數據已經正確收到了。標誌位:共6個,即URG、ACK
、PSH、RST、SYN
、FIN
等,具體含義
ACK
(確認): 僅當ACK=1時,確認號纔有效,鏈接創建後,全部的報文段ACK都爲1。SYN
(同步): 在創建鏈接時用來同步序號。當SYN=1,ACK=0,則代表是一個鏈接請求報文段。SYN=1,ACK=1則表示對方贊成鏈接。TCP創建鏈接用到。FIN
(終止): 用來釋放一個鏈接窗口。當FIN=1時,代表此報文段的發送方再也不發送數據,請求釋放單向鏈接。TCP斷開鏈接用到。須要注意的是:
故事一:三次牽手
男:我喜歡你。
女:我知道了,我也喜歡你。
男:我知道了,那咱們在一塊兒吧。
故事二:四次分手
男:我不喜歡你了,我要和你分手。
女:我知道到了,等我先打完這把王者榮耀再說。
女:我打完了,我也不喜歡你了,我要和你分手。
男:我知道了,贊成分手。
這真是兩個有意思的故事,那麼接下去轉入正題。
ACK=1
:確認序號有效;SYN=1
:發起一個新鏈接;FIN=1
:釋放一個鏈接;seq = x
:本報文段發送的數據的第一個字節的序號ACK=1, ack = y
:期待收到對方下一個報文段的第一個字節的序號。
舉個例子:
注意,不是三次牽手 : )
所謂三次握手是指創建一個 TCP 鏈接時須要客戶端和服務器端總共發送三個包以確認鏈接的創建。在socket編程中,這一過程由客戶端執行connect來觸發。
客戶端將標誌位SYN
置爲1,隨機產生一個值seq=x
,並將該數據包發送給服務器端,客戶端進入SYN-SENT
狀態,等待服務器端確認。
服務器端收到數據包後由標誌位SYN=1
知道客戶端請求創建鏈接,服務器端將標誌位SYN
和ACK
都置爲1,ack=x+1
,隨機產生一個值seq=y
,並將該數據包發送給客戶端以確認鏈接請求,服務器端進入SYN-RCVD
狀態。
客戶端收到確認後,檢查ack
是否爲x+1
,ACK
是否爲1
,若是正確則將標誌位ACK
置爲1
,ack=y+1
,並將該數據包發送給服務器端,服務器端檢查ack
是否爲y+1
,ACK
是否爲1
,若是正確則鏈接創建成功,客戶端和服務器端進入ESTABLISHED
狀態,完成三次握手,隨後客戶端與服務器端之間能夠開始傳輸數據了。
鏈接創建總結:
SYN = 1, seq = x; SYN = 1, ACK = 1, seq = y; ack = x + 1; ACK = 1, seq = x + 1, ack = y + 1;
注意,不是四次分手 : )
四次揮手即終止TCP鏈接,就是指斷開一個TCP鏈接時,須要客戶端和服務端總共發送4個包以確認鏈接的斷開。在socket編程中,這一過程由客戶端或服務端任一方執行close來觸發。
中斷鏈接端能夠是客戶端,也能夠是服務器端。
客戶端進程發出鏈接釋放報文,而且中止發送數據。釋放數據報文首部,FIN=1
,其序列號爲seq=u
等於前面已經傳送過來的數據的最後一個字節的序號加1,並將該數據包發送給服務器端,用來關閉客戶端到服務器端的數據傳送,客戶端進入FIN-WAIT-1
狀態。意思是說:「我客戶端沒有數據要發給你了,可是若是你服務器端還有數據沒有發送完成,則沒必要急着關閉鏈接,能夠繼續發送數據」。
服務器端收到FIN=1
後,發出確認報文,ACK=1
,ack=u+1
,而且帶上本身的序列號seq=v
,告訴客戶端:「你的請求我收到了,可是我還沒準備好,請繼續你等個人消息」。這個時候客戶端就進入FIN-WAIT-2
狀態,繼續等待服務器端的FIN
報文。客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2
狀態,等待服務器發送鏈接釋放報文(在這以前還須要接受服務器發送的最後的數據)。
當服務器端肯定數據已發送完成,就向客戶端發送鏈接釋放報文。置FIN=1
,ACK=1
,ack=u+1
,因爲在半關閉狀態,服務器極可能又發送了一些數據,假定此時的序列號爲seq=w,告訴客戶端:「好了,我這邊數據發完了,準備好關閉鏈接了,請求關閉鏈接」。服務器端進入LAST-ACK
狀態。
客戶端收到服務器端的鏈接釋放報文後,必須發出確認ACK=1
,ack=w+1
,而本身的序列號是seq=u+1
,此時,客戶端就進入TIME-WAIT
狀態,若是Server端沒有收到ACK
則能夠重傳。注意此時TCP鏈接尚未釋放,必須通過2MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED
狀態。服務器只要收到了客戶端發出的確認,當即進入CLOSED
狀態。一樣,撤銷TCB後,就結束了此次的TCP鏈接。能夠看到,服務器結束TCP鏈接的時間要比客戶端早一些。
鏈接斷開總結:
FIN = 1, seq = u; ACK = 1, seq = v; ack = u + 1; FIN = 1, ACK = 1, seq = w, ack = u + 1; ACK = 1, seq = u + 1, ack = w + 1
這裏列出每一個狀態所包含的含義以供參考:
LISTEN
- 偵聽來自遠方TCP端口的鏈接請求;
SYN-SENT
-在發送鏈接請求後等待匹配的鏈接請求;
SYN-RECEIVED
- 在收到和發送一個鏈接請求後等待對鏈接請求的確認;
ESTABLISHED
- 表明一個打開的鏈接,數據能夠傳送給用戶;
FIN-WAIT-1
- 等待遠程TCP的鏈接中斷請求,或先前的鏈接中斷請求的確認;
FIN-WAIT-2
- 從遠程TCP等待鏈接中斷請求;
CLOSE-WAIT
- 等待從本地用戶發來的鏈接中斷請求;
CLOSING
-等待遠程TCP對鏈接中斷的確認;
LAST-ACK
- 等待原來發向遠程TCP的鏈接中斷請求的確認;
TIME-WAIT
-等待足夠的時間以確保遠程TCP接收到鏈接中斷請求的確認;
CLOSED
- 沒有任何鏈接狀態;
爲了防止已失效的鏈接請求報文段忽然又傳送到了服務端,於是產生錯誤,同時確認雙方的接收與發送能力是否正常。
因爲TCP鏈接是全雙工的,所以,每一個方向都必需要單獨進行關閉,這一原則是當一方完成數據發送任務後,發送一個FIN來終止這一方向的鏈接,收到一個FIN只是意味着這一方向上沒有數據流動了,即不會再收到數據了,可是在這個TCP鏈接上仍然可以發送數據,直到這一方向也發送了FIN。首先進行關閉的一方將執行主動關閉,而另外一方則執行被動關閉。
MSL是Maximum Segment Lifetime的英文縮寫,可譯爲「最長報文段壽命」,它是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。
爲了保證客戶端發送的最後一個ACK報文段可以到達服務器。由於這個ACK有可能丟失,從而致使處在LAST-ACK狀態的服務器收不到對FIN-ACK的確認報文。服務器會超時重傳這個FIN-ACK,接着客戶端再重傳一次確認,從新啓動時間等待計時器。最後客戶端和服務器都能正常的關閉。假設客戶端不等待2MSL,而是在發送完ACK以後直接釋放關閉,一但這個ACK丟失的話,服務器就沒法正常的進入關閉鏈接狀態。
兩個理由:
服務器端的資源分配是在二次握手時分配的,而客戶端的資源是在完成三次握手時分配的,因此服務器容易受到SYN洪泛攻擊。SYN攻擊就是Client在短期內僞造大量不存在的IP地址,並向Server不斷地發送SYN包,Server則回覆確認包,並等待Client確認,因爲源地址不存在,所以Server須要不斷重發直至超時,這些僞造的SYN包將長時間佔用未鏈接隊列,致使正常的SYN請求由於隊列滿而被丟棄,從而引發網絡擁塞甚至系統癱瘓。SYN 攻擊是一種典型的 DoS/DDoS 攻擊。
檢測 SYN 攻擊很是的方便,當你在服務器上看到大量的半鏈接狀態時,特別是源IP地址是隨機的,基本上能夠判定這是一次SYN攻擊。在 Linux/Unix 上可使用系統自帶的 netstats 命令來檢測 SYN 攻擊。
netstat -n -p TCP | grep SYN_RECV
常見的防護 SYN 攻擊的方法有以下幾種:
三次握手的一個重要功能是客戶端和服務端交換ISN(Initial Sequence Number), 以便讓對方知道接下來接收數據的時候如何按序列號組裝數據。
若是ISN是固定的,攻擊者很容易猜出後續的確認號,所以 ISN 是動態生成的。
服務器第一次收到客戶端的 SYN 以後,就會處於 SYN-RCVD
狀態,此時雙方尚未徹底創建其鏈接,服務器會把此種狀態下請求鏈接放在一個隊列裏,咱們把這種隊列稱之爲半鏈接隊列。
全鏈接隊列指已經完成三次握手,創建起鏈接的就會放在全鏈接隊列中。若是隊列滿了就有可能會出現丟包現象。
服務器發送完SYN-ACK包,若是未收到客戶確認包,服務器進行首次重傳,等待一段時間仍未收到客戶確認包,進行第二次重傳,若是重傳次數超 過系統規定的最大重傳次數,系統將該鏈接信息從半鏈接隊列中刪除。注意,每次重傳等待的時間不必定相同,通常會是指數增加,例如間隔時間爲 1s, 2s, 4s, 8s, ….
第三次握手的時候能夠攜帶數據的。第一次、第二次握手不能夠攜帶數據。
對於第三次握手來講,此時客戶端已經處於 established 狀態,也就是說,對於客戶端來講,他已經創建起鏈接了,而且也已經知道服務器的接收、發送能力是正常的了,因此能攜帶數據頁沒啥毛病。
最近一段時間應該會比較系統性地從新整理一些TCP/IP協議簇有關的知識,目前正在打算開一個計算機基礎相關的專題,可能主要會涉及操做系統、計算機網絡、算法設計和數據結構有關的內容。
俗話說的好,基礎不牢,地動山搖,咱們勵志要成一隻有夢想的優質前端攻城獅!
參考:
《計算機網絡 第7版 謝希仁》
終於把TCP/IP 協議講的明明白白了,不再怕被問三次握手了
「真香警告」重學 TCP/IP 協議 與三次握手
https://blog.csdn.net/freekin...
TCP和UDP比較
關於三次握手與四次揮手面試官想考咱們什麼?— 不看後悔系列
若是你還看不懂這篇TCP/IP協議的話,就能夠來打我了
推薦閱讀:
【專題:JavaScript進階之路】
ES6 Promise
JavaScript之深刻理解閉包
ES6 尾調用和尾遞歸
JavaScript之函數柯理化
淺談 MVC 和 MVVM 模型
我是Cloudy,年輕的前端攻城獅一枚,愛專研,愛技術,愛分享。
我的筆記,整理不易,感謝關注、閱讀、點贊和收藏。
文章有任何問題歡迎你們指出,也歡迎你們一塊兒交流前端各類問題!