面試反客為主 TCP
本文轉載自微信公眾號「sowhat1412」,作者SoWhat1412。轉載本文請聯系sowhat1412公眾號。
3 傳輸層 TCP/UDP
承接上文 HTTP,數據經過應用層就到傳輸層,但數據到傳輸層之前需要先獲得服務端的 IP 地址,這就涉及到 DNS 域名解析。
3.1 DNS
3.1.1 DNS 講解
主機的真正地址是 IP ,問題是 IP 地址不方便人們記憶,就像你拿手機給張三打電話,難道你能瞬間說出張三電話號碼么,手機里做一個名字跟電話的映射即可,想通話時直接從通訊錄找到張三就可以找到對應的手機號,在網絡請求時候也是需要映射的,而域名服務器 Domain Name System 就是干這個事的,深入講 DNS 前先了解下域名。
我們在瀏覽器地址欄中輸入的每一個地址都是一個域名,比如 www.baidu.com。域名是由.和不同級別域的域名組成。通常我們在書寫時會省略根域名,即域名結尾的.,如www.baidu.com.。由于域名是老外發明的所以從左到右范圍逐步變大且以.分割。
DNS分層
由上到下域名之間相互包含跟嵌套,根域名服務器是關鍵,必須是眾所周知的,找到了它,下面的各級域名服務器才能找到,否則域名解析就無從談起了。我們看下請求 www.baidu.com 的 DNS 解析流程:
- 首先訪問根域名服務器,根域名是不做域名解析,只是給你指路用的,現在你獲取 com 頂級域名服務器的地址。
- 請求 com 頂級域名服務器,返回 baidu.com 域名服務器的地址。
- 然后請求 baidu.com 域名服務器,返回 www.baidu.com 的地址。
這樣進行 DNS 的流程是OK的,但問題是全球數十億的PC電腦,如果每個電腦請求上網都按照上面流程走一波,那上面的 DNS 核心解析系統瞬間爆炸!解決辦法就是用緩存,很多大公司跟運營商都會搭建自己的 DNS 服務器來代替用戶請求核心 DNS 系統,如果查到的話可以緩存查詢記錄,再次收到請求的號如果有緩存結果或者緩存未過期,則直接返回原來的緩存結果,知名的 Google 8.8.8.8 DNS 解析服務器,就是 Google 自建的非權威域名服務器 。除了非權威域名服務器,我們經常看到的有瀏覽器緩存,操作系統緩存,比如 /etc/hosts文件等。
3.1.2 DNS 樣例
DNS域名解析
- 用戶輸入網址先看下瀏覽器的DNS緩存是否過期,沒過期直接拿來用。過期了看看本地操作系統緩存 /etc/hosts 文件等。
- 請求本地配置的 非權威域名服務器 DNS resolver。
- DNS resolver 將網址轉發到根域名請求,返回 com 域名地址。
- DNS resolver 將網址轉發到 com 域名的服務器請求,返回跟 baidu.com 相關的 權威DNS解析器。
- DNS resolver 將網址轉發權威 DNS 解析器繼續請求,然后返回真正的目標域名IP。
- DNS resolver 最終將目標 IP 返回給用戶繼續接下來的訪問請求。
3.2 TCP
3.2.1 TCP 頭部講解
TCP 是一個是面向連接的、可靠的、基于字節流的、工作在傳輸層的數據傳輸服務。用 TCP 傳輸數據能確保接收端接收的網絡包是無損壞、無間隔、非冗余、有序。這里需注意 TCP 是一對一連接的。
TCP頭部 + HTTP
- 發送端口:是一個大于 1023 的 16 位數字,由基于TCP應用程序的用戶進程隨機選擇。
- 目的端口:指明接收者所用的端口號,一般由應用程序來指定。
- 序列號:建立連接時客戶端生成隨機數作為初始值,通過 SYN 包傳給接收端主機,每發送一次數據,就累加一次。序列號達到最大值會出現序列號回繞,再次從0 開始。核心作用就是 接收方去重數據 + 按序接收。
- 確認號:用來解決不丟包的問題,指定下一次希望收到的數據的序列號,發送端收到這個確認應答以后可以認為在這個序號以前的數據都已經被正常接收。
- 數據偏移:表示 TCP 報文段的首部長度,4 位二進制最大表示15,由于TCP首部包含個可變長度選項,需要指定這個 TCP 報文段到底有多長。它指出 TCP 報文段的數據起始處距離 TCP 報文段的起始處有多遠。該字段的單位是4字節,所以TCP首部最大15*4 = 60字節。
- 保留:保留6位,未使用,應置零。
下面的7~12是控制位,用來表示說明報文段的性質
7.URG:表示本報文段中發送的數據是否包含緊急數據。只有當 URG=1 時后面的緊急指針字段urgent pointer才有效。
8.ACK:表示是否前面確認號字段是否有效。只有當 ACK=1 時,前面的確認號字段才有效。TCP 規定連接建立后 ACK=1,帶 ACK 標志的TCP報文段稱為確認報文段。
9.PSH:提示接收端需立即從 TCP 接收緩沖區中讀走數據,為接收后續數據騰出空間。為1表示對方應當立即把數據提交給上層應用,如果應用程序不將接收到的數據讀走,就會一直停留在 TCP 接收緩沖區中。
10.RST:收到一個 RST=1 的報文說明與主機的連接出現了嚴重錯誤,必須釋放連接,然后再重新建立連接。或者說明上次發送給主機的數據有問題,主機拒絕響應,帶 RST 標志的 TCP 報文段稱為復位報文段。
11.SYN:建立連接時用來同步序號。SYN=1 說明這是一個請求建立連接或同意建立連接的報文。只有在前兩次握手中 SYN 才置為1,帶 SYN 標志的 TCP 報文段稱為同步報文段。
- 當 SYN=1 且 ACK=0 時表示這是一個請求建立連接的報文段。
- 當 SYN=1 且 ACK=1 時表示對方同意建立連接。
12FIN:通知對方本端要關閉連接了,標記數據是否發送完畢。如果 FIN=1 告訴對方釋放連接,帶FIN標志的TCP報文段稱為結束報文段。
13.窗口大小:表示現在允許對方發送的數據量,告訴對方從本報文段的確認號開始允許對方發送的數據量,達到此值,需要ACK確認后才能再繼續傳送后面數據。
14.校驗和:提供額外的可靠性。
15.緊急指針:標記緊急數據在數據字段中的位。
16.選項部分:選項部分的最大長度可根據TCP首部長度進行推算。TCP首部長度用4位表示,選項部分最長為:(2^4-1)*4-20=40 字節。
TCP 只規定了一種選項,即TCP報文段最大長度 MSS,通常是1460字節,整個TCP報文段的長度 = 數據字段的長度 + TCP 首部的長度 。
17.填充:這里需注意,為了網絡設備硬件設計和處理方便, 數據傳輸過程中首部長度必須是 4 字節的整數倍。
3.2.2 TCP 三次握手
TCP三次握手
- 一開始客戶端跟服務器都處于 closed 狀態,然后服務端主動監聽某個客戶端端口,此時服務端處于listen 狀態。
- 客戶端隨機初始化序列號 seq = client_isn,同時將 SYN = 1 表示這是SYN 報文,接著把該 SYN 報文發給服務器,注意此時報文不包含引用層數據,客戶端處于 syn-sent狀態。
- 服務端收到客戶端的 SYN報文后也隨機初始化個序號 seq = server_isn,并且將確認序號 ack = client_isn + 1,接著把 SYN = 1跟 ACK = 1,然后該報文發送給客戶端,服務器處于 syn-rcvd狀態。
- 客戶端收到服務器的報文后,將 ACK = 1,確認應答號 ack = server_isn + 1,然后把報文發送給服務器,本次報文可發送數據,同時客戶端處于established 狀態。
- 服務器收到客戶端的應答報文后,也進入 established 狀態。
- 客戶端和服務端建立好了連接,可以相互發送數據。
這里你可能發現了客戶端跟服務器的初始化序列號是各自隨機的,原因是網絡中的報文會重發、會延遲、也有可能丟失,為避免相互影響干脆各用各的為好。同時通過流程發現前兩次握手是不帶數據的,第三次可攜帶數據。
3.2.3 TCP 數據傳輸大致流程
前面在HTTP時候就說過了,數據到TCP層跟IP層都會拆分發送,有人可能會問:既然IP會分幀,那為什么TCP層還分層呢?原因是如果TCP不分層,只用IP層分幀數據發送,如果有一幀出現丟失則會導致整個IP報文分幀全部重傳。本質在于IP層沒有重傳機制而TCP層可以實現數據的超時重傳、丟失重傳。
信息傳輸大致流程
3.2.4 TCP 狀態查詢
服務器一般用 netstat 查看 tcp,udp 的端口和進程等相關情況。netstat -tunlp | grep 端口號
- -t (tcp) 僅顯示tcp相關選項
- -u (udp) 僅顯示udp相關選項
- -n 拒絕顯示別名,能顯示數字的全部轉化為數字
- -l 僅列出在Listen(監聽)的服務狀態
- -p 顯示建立相關鏈接的程序名
netstat樣例
3.2.5 TCP 為啥三次握手
TCP是不區分客戶端和服務端,連接的建立是雙向的過程。所以客戶端要給服務器通訊的話兩次握手是必須的。
- 第一次握手客戶端發個連接請求給服務端,服務端收到后知道自己可以跟客戶端連接了,
- 但是此時客戶端不知道啊,所以必須的執行第二次握手,反饋下信息給客戶端。
- 第一次握手請求連接如果因為網絡導致延遲,直到連接釋放后信息才到達服務端,那此時服務端也會給客戶端進行第二次握手回復,關鍵是客戶端已經不要這個連接了,此時服務端會一直在等待接收客戶端信息,造成資源浪費。
- 如果用了三次握手則客戶端會發送 RST 報文告知服務端請終止本次舊連接。
如果還不太理解,我們用個生活常識說明下。晚上你在小區里散步,不遠處看見一位漂亮妹子迎面而來,因為路燈有點暗不能100%確認,所以要通過招手的方式來確定對方是否認識自己。
1. 你首先向妹子 招手 syn。
2. 妹子看到你向自己招手后,向你點頭 微笑 ack。
3. 她也需要確認一下你有沒有可能你是在看別人呢,妹子也向你 招手 syn。
4. 你看到妹子 微笑ack 后確認了妹子成功辨認出了自己,進入 established 狀態。
5. 妹子給你 招手 syn 了,你也 微笑 ack 回復,妹子收到后也進入 established 狀態。
因為妹子連續進行了兩個動作,先是點頭微笑,然后再次招手,所以可以將這兩個動作合成一個動作,招手的同時點頭和微笑。于是這四個動作就簡化成了三個動作。
你與妹子的相識
3.2.6 TCP 三次握手的意義
1.避免歷史連接
客戶端建立連接時發送多次 SYN 報文,由于網絡擁堵可能舊的 SYN 報文比新的 SYN 報文先到服務器,服務器不管新舊,收到就回復 SYN + ACK 給客戶端,三次握手情況下客戶端可以根據序列號或超時時間判斷回復的連接是否是歷史連接,如果是歷史連接直接發送 RST 報文給服務端來終止連接。
2.同步雙方初始序列號
TCP 協議的通信雙方都在維護各自的序列號,且必須要讓對方知道。只有通過三次握手才可以實現。
3.避免服務端資源浪費
二次握手情況下,如果客戶端的 SYN 阻塞導致重復發送多次 SYN 報文,那么服務器在收到請求后就會建立多個冗余的無效鏈接,造成不必要的資源浪費。而三次握手發現無效鏈接可在第三次給服務器端發送終止指令。
3.2.7 TCP 連接中客戶端忽然掛掉咋辦?
TCP還設有一個保活計時器,服務器每收到一次客戶端的請求后都會重新復位這個計時器,時間通常是設置為2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以后每隔75秒發送一次。若一連發送10個探測報文仍然沒反應,服務器就認為客戶端出了故障,接著就關閉連接。
3.2.8 TCP 如何避免 SYN 攻擊
TCP 連接時會經過三次握手,在第一次握手后,服務端收到 SYN 報文 就會發出 ACK + SYN 報文 同時進入 SYN_RCVD 狀態,如果有黑客偽造 n 個不同 IP 發出請求,會導致服務器的 SYN_RCVD 隊列 爆滿,最終無法對外提供服務。
解決方法:
- 設置 SYN_RCVD 最大值:服務端超過處理能力時,直接將新的 SYN 請求 RST 丟棄。
- 可縮短 SYN Timeout 時間:通過縮短從接收到 SYN 報文到確定這個報文無效并丟棄該連接的時間,可以降低服務器負荷。
- 設置SYN Cookie:給每個請求連接的 IP 地址分配一個 Cookie,如果短時間內收到同一個 IP 的重復 SYN 報文,則以后從這個 IP 地址來的包會被丟棄。
3.2.9 TCP 四次揮手
客戶端跟服務端都可以發出端口請求,TCP 斷開連接是通過四次揮手方式。
TCP四次揮手
- 客戶端停止發送數據并且發出釋放連接的報文,報文中FIN = 1,FIN報文段即使不攜帶數據,也要消耗一個序號,此時序列號seq = u ,u = 前面已經傳輸過來數據最后一個字節序號加1,客戶端進入FIN-WAIT-1狀態。
- 服務器收到連接釋放報文,發出確認報文,ACK=1,應答確認好 ack=u+1,并且帶上自己的序列號seq=v,此時服務端就進入了CLOSE-WAIT 狀態。TCP 服務器通知高層的應用進程進入半閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個 CLOSE-WAIT狀態持續的時間。
- 客戶端收到服務器的確認請求后,此時客戶端就進入FIN-WAIT-2 狀態,等待服務器發送連接釋放報文,在這之前還需要接受服務器發送的最后的數據。
- 服務器將最后的數據發送完畢后,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由于在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號為 seq=w,此時服務器就進入了LAST-ACK 狀態,等待客戶端的確認。
- 客戶端收到服務器的連接釋放報文后必須發出確認,ACK=1,ack=w+1,seq=u+1,此時客戶端就進入了TIME-WAIT狀態。注意此時 TCP 連接還沒有釋放,必須經過最長報文段壽命 2MSL 的時間后,當客戶端撤銷相應的 TCB 后,才進入 CLOSED 狀態。
- 服務器只要收到了客戶端發出的確認,立即進入 CLOSED 狀態。同樣撤銷 TCB后,就結束了這次的 TCP 連接,可以看到服務器結束TCP連接的時間要比客戶端早一些。
我們還以你跟妹子碰面交流為例,你倆彼此確認后交流幾分鐘后,你打算結束這個談話,畢竟交流太久沒老婆發現就涼了。
你跟妹子揮手離別
3.2.10 TCP 為什么四次揮手
其實分析下整個關閉的流程就知道為什么必須是四次揮手而不是三次揮手了。
- 關閉連接時,客戶端向服務端發送 FIN 時,僅僅表示客戶端不再發送數據出去了但是還是能接收數據。
- 服務器收到客戶端的 FIN 報文時,先回一個 ACK應答報文,意思是不再接受數據了,但服務端可能還有數據需往外發送,等服務端不再發送數據時才發送 FIN 報文給客戶端來表示同意現在關閉連接。這里注意服務端的 ACK 跟 FIN 是分開發的。
- 客戶端收到服務端的 ACK 后,再給服務端發送 ACK,最終客戶端跟服務器都進入close 狀態。
3.2.11 TCP 揮手為什么需要 TIME_WAIT 狀態
MSL 定義:
Maximum Segment Lifetime 報文最大生存時間,意思是網絡傳輸的報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。而數據之所以可以被拋棄是因為TCP層的下面的IP層有個TTL來記錄報文傳輸過程中經過的最大路由次數。
TIME_WAIT 定義:
TIME_WAIT = 2* MSL,原因是 發送方數據到接受方后,接收方會返回響應,這樣一來一回正好2 倍的MSL。
Time_wait 是從客戶端接收到 FIN 后發送 ACK 開始計時的,如果在 TIME-WAIT 時間內,客戶端的 ACK 沒有傳輸到服務端,客戶端又接收到了服務端重發的 FIN 報文,那 2MSL 將重新計時。
TIME_WAIT 存在意義:
防止舊連接的數據包被重新消費:上一次連接時候如果有網絡震蕩導致服務端數據在網絡游蕩,如果因為 time_wait 時間太短,新的連接可能會重新接受到游蕩的消息。有了延遲時間可以避免消耗游蕩的數據。
確保連接正確關閉:TIME-WAIT 作用是等待足夠的時間以確保最后的 ACK 能讓被動關閉方接收,從而幫助其正常關閉。
TIME_WAIT 發生場景:
在高并發短連接的 TCP 服務器上,當服務器處理完請求后立刻主動正常關閉連接。這個場景下會出現大量 socket 處于 TIME_WAIT 狀態。如果客戶端的并發量持續很高,因為端口有限,內存有限,會導致此時部分客戶端顯示連接不上。
在Linux內核中 TIME_WAIT = 60秒。
避免 TIME_WAIT 過多:
- 取消短連接,改用長連接方式,
- 設定閾值,一旦超過閾值系統會將所有time_wait 連接重置。
- 修改客戶端程序代碼。
3.2.12 TCP 如何保證數據傳輸可靠
- 校驗和:發送跟接受數據都會進行檢驗的,如果不一致,那么傳輸有誤。
- 確認應答序列號:TCP進行傳輸時數據都進行了編號,每次接收方返回ACK都有確認序列號。
- 超時重傳:如果發送方發送數據一段時間后沒有收到ACK,那么就重發數據。并且自帶去重功能。
- 連接管理:三次握手和四次揮手的過程。
- 流量控制:TCP協議報頭包含16位的窗口大小,接收方會在返回ACK時同時把自己的即時窗口填入,發送方就根據報文中窗口的大小控制發送速度。
- 擁塞控制:剛開始發送數據的時候,擁塞窗口是1,以后每次收到ACK,則擁塞窗口+1,然后將擁塞窗口和收到的窗口取較小值作為實際發送的窗口,如果發生超時重傳,擁塞窗口重置為1。這樣做的目的就是為了保證傳輸過程的高效性和可靠性。
3.3 UDP
UDP 為應用程序提供了一種無需建立連接就可以發送封裝的 IP 數據包的方法,它的協議很簡單,頭部只有八個字節:
UDP頭部
- 兩個十六位的端口號:分別為源端口和目標端口。
- 包長度:該字段 = UDP首部長度 + 數據長度。
- 校驗和:整個數據報文的檢驗和,該字段用于發現頭部信息和數據中的錯誤。
3.3.1 UDP 特點
UDP有不提供數據包分組、組裝和不能對數據包進行排序的缺點,當報文發送之后,是無法得知其是否安全完整到達的。
1.面向無連接
UDP 不會進行三次握手建立連接,想建立連接就建立連接,并且也只是數據報文的搬運工,不會對數據報文進行任何拆分和拼接操作。
在發送端,應用層將數據傳遞給傳輸層的 UDP 協議,UDP 只會給數據增加一個 UDP 頭標識下是 UDP 協議,然后就傳遞給網絡層了。
在接收端,網絡層將數據傳遞給傳輸層,UDP 只去除 IP 報文頭就傳遞給應用層,不會任何拼接操作
2.有單播、多播、廣播的功能
UDP 不止支持一對一的傳輸方式,同樣支持一對多,多對多,多對一的方式,也就是說 UDP 提供了單播,多播,廣播的功能。
3.UDP面向報文
發送方的UDP對應用程序交下來的報文,在添加首部后就向下交付IP層。UDP對應用層交下來的報文,既不合并,也不拆分,而是保留這些報文的邊界。因此應用程序必須選擇合適大小的報文
4.不可靠性
不可靠性體現在無連接上,通信都不需要建立連接,想發就發,這樣的情況肯定不可靠。
收到什么數據就傳遞什么數據,并且也不會備份數據,發送數據也不會關心對方是否已經正確接收到數據了。
沒有擁塞控制,一直會以恒定的速度發送數據。網絡不好可能導致丟包,在某些實時性要求高的場景,比如視頻電話就需要使用 UDP。
5.頭部開銷小
UDP 的頭部開銷小,只有八字節,相比 TCP 的至少二十字節要少得多,在傳輸數據報文時是很高效的
3.3.2 TCP 跟 UDP 對比
3.3.3 TCP UDP 共有端口
你可能經常被問到,TCP 和 UDP 為何可以共用同一端口?這是因為從網絡層的角度來看,它是不知道端口這個概念的,TCP/UDP 都是包裹在 IP 協議內的,IP 協議只需要知道 IP 對應的硬件地址就可以把遠端的網絡包發送到目的主機上。
端口這個概念是由操作系統劃分的。因為內核不可能把所有網絡數據都發送給所有的進程,所以為了區分哪些數據該劃分給哪些進程,便在傳輸層的協議中定義了端口。而TCP和UDP協議中的端口號占位都是16位,所以操作系統能綁定的端口也就只有65535個。
如果你查看 C 語言有關 Socket 編程中的 socket 跟 bind 函數你會發現,系統是以 協議 + ip + 端口來綁定端口的,所以不同協議相同的ip和端口也是可以綁定成功的。
4 TCP 進階
4.1 TCP 重傳機制
為保證數據安全到達接受端,TCP引入了超時重傳、快速重傳、SACK、D-SACK。
4.1.1 超時重傳
以時間為基準,在發送數據時設置個定時器,如果期限內沒收到接受者的ACK就會重新發送數據,一般數據包丟失或確認應答丟失會導致超時重傳,這里先普及兩個跟時間相關的參數跟一些規則。
- RTT:Round-Trip Time 往返時間,指的是數據從發送到接受的耗時時間。
- RTO :Retransmission Timeout 超時重傳時間。
- 動態:RTT 收到網絡波動是動態變化的,同理RTO也是動態變化的。
- RTO翻倍:每遇到一次超時重傳,系統都會將下一次RTO翻倍。
RTO跟RTT
RTT跟RTO之間的關系十分微妙。
- RTO 較小時可能導致數據本來就沒丟失只是還沒被響應, 又重發會增加網絡擁塞,導致更多的超時重發。
- RTO較大時候可能導致數據已經丟了好久才重發數據。
所以離線情況下 RTO 稍微大于 RTT是最好的。具體規則有興趣的可自行百度。
4.1.2 快速重傳
TCP有累計確認機制,當接收端收到比期望序號大的報文段時,便會重復發送最近一次確認的報文段的確認信號,我們稱之為冗余ACK(duplicate ACK)。
如圖所示,報文段1成功接收并被確認ACK 2,接收端的期待序號為2,當報文段2丟失,報文段3失序到來,與接收端的期望不匹配,接收端重復發送冗余ACK 2。
快速重傳機制
發送端如果在超時重傳定時器溢出之前,接收到連續的三個重復冗余ACK(其實是收到4個同樣的ACK,第一個是正常的,后三個才是冗余的),發送端便知曉哪個報文段在傳輸過程中丟失了,于是重發該報文段,不需要等待超時重傳定時器溢出,最后客戶端收到 2,因為345已經回復過了,返回ACK6。
為啥是3次呢? 你要明白發送端即使按序發送,接收端也是會出現亂序的。亂序也會造成冗余ACK發送,那冗余ACK是亂序導致還是丟包導致呢?經過權衡把3次冗余ACK作為判定丟失的準則其本身就是估計值。
數據接收情況
A為發送端,B為接收端,A的待發報文段序號為 【N-1,N,N+1,N+2】,假設報文段N-1成功到達。
- 在沒丟失的情況下,有40%的可能出現3次冗余ACK,在亂序的情況下必定是2次冗余ACK。
- 在丟失的情況下,必定出現3次冗余ACK。
基于這樣的概率,選定3次冗余ACK作為閾值也算是合理的。實際抓包時大多數的快速重傳都會在大于3次冗余ACK后發生。
快速重傳解決了超時問題,可是重傳時是重傳之前的一個,還是重傳所有它是定不了的。
4.1.3 SACK
既然快速重傳搞不定,就用 Selective Acknowledgment 選擇性確認,原理也很簡單,服務端給客戶端回復的時候多加個字段SACK,SACK的內容就是告知發送端服務端收到了哪些。這樣服務端可以根據收到的信息選擇性發送丟失的包。
4.1.4 D-SACK
DSACK是在SACK的基礎上做了一些擴展,主要用于對收到的重復報文進行了處理。DSACK同樣使用了與SACK一樣的報文格式。核心關注點是發送的時候出問題了還是回復的時候出問題了。
如果發送端發送數據A延時而觸發了快速重傳機制,快速重傳機制發送過來的信息新A,然后老A又到了,接收端會回復SACK 意思是網絡震蕩導致的。
如果服務端的ACK 客戶端沒收到,客戶端重發的時候,服務端會回復SACK,意思就是你的數據發送重復了。
4.2 TCP 滑動窗口
如果沒有滑動窗口的機制:傳輸N份文件,就需要等待N次應答時間。
總的傳輸時間 = N份傳輸時間 + N份應答傳輸時間。
保證可靠性的前提下TCP 引入了窗口概念,滑動窗口可以讓我們進一步提高傳輸效率。在窗口內的數據無需等待確認應答就可以繼續發送數據。窗口的本質是OS開辟的一個緩存空間,然后進行批量傳輸,只要接收方沒確認應答那么緩存中會一直存在。
總的傳輸時間 = N分數據傳輸時間疊加成一份時間,N份應答傳輸時間,重疊成一份時間
窗口大小為4000字節
窗口大小一般是接收方來決定的,接收方會告知發送方自己有多少緩存可接受數據,如果超過這個數據量接收方就無法接收了。
4.2.1 發送滑動窗口
滑動窗口
在一的狀態下發送方收到一個請求序列號2001的確認應答ACK,則2001前數據被標記為傳輸完畢,系統會進行窗口滑動變為二的樣子。
- 窗口左邊是已經發送并且受到服務器的ACK的數據,這些數據可以從緩存刪除。
- 窗口內的數據其實也分為兩類,一類是發送還沒接收到ACK的,一類是還未發送的。在收到整個窗口的確認應答ACK之前,如果數據有丟失,發送端仍然需要重傳。所以發送端需要有緩存保留可能被重傳的數據,直到收到服務端ACK。
- 收到服務端ACK后,發送端會將窗口滑動到確認應答中的序列號的位置。這樣可以順序地將多個段同時發送提高通信性能。這種機制也別稱為滑動窗口控制。
- 窗口模式下發送方也會根據接收方的能力來進行發送數據來進行流量控制。
4.2.2 窗口數據丟失
這里的數據丟失其實跟前面說到的重傳機制類似,主要分為兩種
接收端收到信息但是返回ACK失敗了:如果丟失ACK,不需要做任何處理,如3001這個ACK丟了,但是4001ACK卻已經發送給主機A,說明2001~3000這個數據也順利到達,3001ACK丟了無所謂,只要當前序號開始,就說明之前的數據已經正確傳輸到達主機B
收到數據但ACK丟失
發送端發送數據中途數據丟失了:如下圖1001-2000數據包丟了,而2001-3000,3001-4000都順利到達,此時接收方反饋的ACK確認序號始終是1001,發送方如果發現接收方連續發送ACK都是1001,接收方就明白1001-2000這個數據丟包,就會重新傳送,當接收方重新收到丟失的1001-2000數據后,直接返回ACK4001,因為2001-4000已經接受過放到緩存區了,接下來ACK直接從4001開始。
發送時丟失
4.3 擁塞控制
前面說到的流量控制只是單純的對于發送方跟接受方而已,但是我們要知道網絡一般都是公用的,別的服務器也可以能將網絡搞阻塞,因為阻塞導致重發,然后重發導致更阻塞,最后陷入惡性循環。
為了控制發送方的數據量避免數據阻塞整個網絡,發送方維護著一個叫擁塞窗口的東西,前面說到過發送窗口跟接受窗口,現在由于有了擁塞窗口,此時 發送窗口swnd = min(擁塞窗口cwnd,接受窗口rwnd)。擁塞窗口的大小是動態變化的,當網絡沒阻塞就會變大,網絡中有阻塞就會變小。判斷阻塞的依據就是如果發送方在指定時間內沒收到數據那就是阻塞了。
擁塞控制主要通過慢開始,快重傳,快恢復和避免擁塞來實現的。
4.3.1 慢開始
TCP建立連接后系統有個慢啟動的過程,意思就是一點一點的提高發送數據包的數量,慢啟動的原則就是當發送方每收到一個 ACK,擁塞窗口 cwnd 的大小就會加 1。有一個叫慢啟動門限 slow start threshold 狀態變量來充當最大值。
cwnd < ssthresh 時,使用慢啟動算法。
cwnd >= ssthresh 時,使用擁塞避免算法。
4.3.2 擁塞避免算法
一般情況下 slow start threshold = 65535字節,系統進入擁塞避免算法后,每當收到一個 ACK 時,擁塞窗口就增加 1/擁塞窗口。擁塞避免算法存在的意義就是將慢開始的那種指數增長變化為線程增長。
4.3.3 快重傳
進入擁塞避免算法后的數據隨著不斷增長最終會導致網絡阻塞,最終引發丟包。然后會采用前面說到的超時重傳跟快速重傳。
- 超時重傳:ssthresh = cwnd/2 同時 cwnd 重置為 1,然后重新開始慢啟動,回到了起點。
- 快速重傳:cwnd = cwnd/2 同時 ssthresh = cwnd 然后進入快速恢復算法。
4.3.4 快恢復
快恢復與快重傳配合使用,當發送方接收到連續三個重復確認請求,為了避免網絡擁塞,執行快速重傳(cwnd = cwnd/2 同時 ssthresh = cwnd ),執行快速恢復算法。
- cwnd = ssthresh + 3
- 重傳丟失的數據包
- 收到重復ACK則 cwnd 累加 1。
- 收到新ACK后設置 cwnd = ssthresh,進入擁塞避免算法。
5 參考
科技哥網絡:https://t.1yb.co/gJRx
小林網絡:https://t.1yb.co/fQG3
TCP/IP講解:https://developer.51cto.com/art/201906/597961.htm
快速重傳:https://blog.csdn.net/whgtheone/article/details/80983882