傳輸層:udp與tcp協議

??????

目錄

再談端口號

端口號范圍劃分?

認識知名端口號(Well-Know Port Number)

兩個問題

netstat

pidof

如何學習下三層協議?

UDP協議?

UDP協議端格式?

UDP的特點?

面向數據報

UDP的緩沖區?

UDP使用注意事項

基于UDP的應用層協議?

TCP協議?

?TCP協議段格式

1.源端口號與目的端口號

2.四位首部長度

3.十六位窗口大小

4.32位序號與32位接收序號?

5.6個標記位?

?確認應答(ACK)機制

?超時重傳機制

連接管理機制?

那么我們為什么需要三次握手呢?

為什么要進行四次揮手?

三次握手的一些補充內容?

四次揮手的一些補充知識

流量控制

滑動窗口

問題一、如果丟包了怎么辦?

問題二、滑動窗口如何移動?

問題三、滑動窗口會在滑動的過程中越界嗎??

延遲應答?

捎帶應答

小總結?

擁塞控制?

面向字節流

粘包問題

TCP異常情況

用UDP實現可靠傳輸(經典面試題)


??傳輸層負責數據能夠從發送端傳輸接收端.

再談端口號

端口號(Port)標識了一個主機上進行通信的不同的應用程序;

????????在TCP/IP協議中, 用 "源IP", "源端口號", "目的IP", "目的端口號", "協議號" 這樣一個五元組來標識一個通信(可以通過netstat -n查看);?

端口號范圍劃分?

0 - 1023: 知名端口號, HTTP, FTP, SSH等這些廣為使用的應用層協議, 他們的端口號都是固定的.

1024 - 65535: 操作系統動態分配的端口號. 客戶端程序的端口號, 就是由操作系統從這個范圍分配的.?

認識知名端口號(Well-Know Port Number)

有些服務器是非常常用的, 為了使用方便, 人們約定一些常用的服務器, 都是用以下這些固定的端口號:
ssh服務器, 使用22端口
ftp服務器, 使用21端口
telnet服務器, 使用23端口
http服務器, 使用80端口
https服務器, 使用443
執行下面的命令, 可以看到知名端口號?

at /etc/services

我們自己寫一個程序使用端口號時, 要避開這些知名端口號.?

兩個問題

1. 一個進程是否可以bind多個端口號?

可以
2. 一個端口號是否可以被多個進程bind??

不可以?

netstat

netstat是一個用來查看網絡狀態的重要工具.
語法:netstat [選項]
功能:查看網絡狀態


常用選項:
n 拒絕顯示別名,能顯示數字的全部轉化成數字
l 僅列出有在 Listen (監聽) 的服務狀態
p 顯示建立相關鏈接的程序名
t (tcp)僅顯示tcp相關選項
u (udp)僅顯示udp相關選項
a (all)顯示所有選項,默認不顯示LISTEN相關?

pidof

在查看服務器的進程id時非常方便.
語法:pidof [進程名]
功能:通過進程名, 查看進程id?

如何學習下三層協議?

UDP協議?

UDP協議端格式?

這個報頭是操作系統給我們加的 ,采用的是定長報頭。

如何將有效載荷交給下一層呢?通過目的端口號,通過這個就可以很容易識別目標進程了。

1.16位UDP長度, 表示整個數據報(UDP首部+UDP數據)的最大長度;
2.如果校驗和出錯, 就會直接丟棄;。(不保證可靠性)

UDP的特點?

UDP傳輸的過程類似于寄信.
1.無連接: 知道對端的IP和端口號就直接進行傳輸, 不需要建立連接;
2.不可靠: 沒有確認機制, 沒有重傳機制; 如果因為網絡故障該段無法發到對方, UDP協議層也不會給應用層返回任何錯誤信息;
3.面向數據報: 不能夠靈活的控制讀寫數據的次數和數量;?

面向數據報

一個udp報文和另一個udp報文是沒有任何關系的。?

對于udp來說不需要進行過多的io處理,因為每次接收到的都是單獨且結構完整的一個報文。

????????應用層交給UDP多長的報文, UDP原樣發送, 既不會拆分, 也不會合并;
????????用UDP傳輸100個字節的數據:
????????如果發送端調用一次sendto, 發送100個字節, 那么接收端也必須調用對應的一次recvfrom, 接收100個字節; 而不能循環調用10次recvfrom, 每次接收10個字節;?

UDP的緩沖區?

1.UDP沒有真正意義上的 發送緩沖區. 調用sendto會直接交給內核, 由內核將數據傳給網絡層協議進行后續的傳輸動作;
2.UDP具有接收緩沖區. 但是這個接收緩沖區不能保證收到的UDP報的順序和發送UDP報的順序一致; 同時如果緩沖區滿了, 再到達的UDP數據就會被丟棄;


UDP的socket既能讀, 也能寫, 這個概念叫做 全雙工?

????????對于發送方和接收方來說,他們的內核中都存在很多的 udp報文,那么我們也需要先描述再組織地去管理這些報文,這些報文被結構體sk_buff描述并管理

UDP使用注意事項

????????我們注意到, UDP協議首部中有一個16位的最大長度. 也就是說一個UDP能傳輸的數據最大長度是64K(包含UDP首部).
????????然而64K在當今的互聯網環境下, 是一個非常小的數字.
????????如果我們需要傳輸的數據超過64K,, 就需要在應用層手動把報文拆分。

基于UDP的應用層協議?

?NFS: 網絡文件系統
TFTP: 簡單文件傳輸協議
DHCP: 動態主機配置協議
BOOTP: 啟動協議(用于無盤設備啟動)
DNS: 域名解析協議
當然, 也包括我們自己寫UDP程序時自定義的應用層協議;

TCP協議?

????????TCP全稱為 "傳輸控制協議(Transmission Control Protocol"). 人如其名, 要對數據的傳輸進行一個詳細的控制;?

? ? ? ? 其中的傳輸和控制我們都能輕易理解,但是這里的控制是怎么回事呢?

? ? ? ? 其實就是說,例如我們應用層把數據write到對應內核中的緩沖區中,但是至于這個發送緩沖區里面的內容怎么發,發多少,出錯了怎么辦這些都是由tcp協議自主決定的。

? ? ? ? (這張圖其實是和我們之前將io幾乎一樣?。)

?TCP協議段格式

1.源端口號與目的端口號

?這兩個是一對的概念,目的端口號用于找到上層指定進程,將數據交付

2.四位首部長度

? ? ??? 我們將報頭與有效載荷分離是通過 固定長度+自描述字段 進行分離的

????????這里的固定長度是指標準報頭中的20字節的長度,這部分是一定存在的

????????而選項內容則是不一定會有,因此標準報頭中有一個4位首部長度,其取值為[0,15],但是比較特殊的是,它有自己的大小單位,每個單位長度為4字節,因此如果選項中沒有額外內容,那么這個4位首部長度的值就是5 。

3.十六位窗口大小

?

因為這個十六位窗口大小與tcp可靠性有關,因此要講這里的十六位窗口大小,就先要提三個預備知識了

?1.客戶端和服務器基于tcp協議進行通信,互發消息的時候,發送的是完整的tcp報文,攜帶的一定是完整的tcp報頭。

2.為了保證可靠性,tcp協議中有一個確認應答機制,它是保證可靠性最重要的一個點,即若客戶端向服務器發送消息,服務器如果收到,那么就會返回一個確認應答,反之也一樣。

3.tcp是不害怕丟包等問題的,因為tcp具有重傳機制保證可靠性。如果我們的報文發送速度太快,丟失了報文我們也不需要擔心,tcp會進行重傳。但是這種方案并不優秀。如果我們可以根據收消息一方的接收緩沖區大小來控制發送方發送消息的速度,那么這種可能大量丟失報文的情況就不太會出現了。因此tcp還有流量控制的方案。

綜合上述三點,我們的接收方接收到報文之后,進行確認應答返回一個完整的報文,其中的十六位窗口大小正是它自身的接收緩沖區中剩余空間的大小。

4.32位序號與32位接收序號?

?在介紹這兩個字段之前我們也需要補充一些知識

首先我們知道,如果我們發送的報文得到了應答,那么說明我們最近發送的消息被對方收到了

而如果沒有被應答,我們就無法保證其可靠性。

客戶端與服務器雙方最新發送的一條消息是沒有應答的 ,所以我們無法保證發出的消息是百分百可靠的。

但是雖然我們不能保證整個通信過程的可靠,但是我們可以保證局部的可靠,即最新一條消息之前的消息是可以保證可靠的。

?為了提高通信效率, 通信的時候雙方都可能會捎帶應答,即發送需要發送消息的同時也將應答帶上,因為應答本身是不需要數據的的。

? ? ? ? 而處理應答的方式就是:如果發送方一段時間沒有收到所發報文的應答,那么它就會認為數據丟失了。

? ? ? ? 但是串型一發一應答的方式效率還是太低,因此一般采用并型發送,同時發送許多報文,不過這種方式無法保證發送報文的順序,因此我們就需要序號來標記每個報文。這樣接收方接收到報文后,根據序號進行重新排序就能保證報文的有序了。

?

????????那么序號又是什么呢?

????????我們知道用戶層會把數據拷貝到tcp的發送緩沖區,我們可以將這個緩沖區看成一個數組,那么每個字節都會有自己的編號,數組的下標就天然可以作為一種序號。?

? ? ? ? 而每個tcp報文的序號實際上就是要發送的數據塊的最后一個字符的下標。

? ? ? ? 而接收序號填充的則是收到報文的序號+1。

? ? ? ? 有人會疑惑,為什么要多此一舉多弄一個確認序號呢?發送方發送會攜帶一個序號,確認應答的時候把這個序號返回不久可以了。

? ? ? ? 主要有兩點原因。其一是上面我們說的,捎帶應答的情況,報文本身需要一個序號,應答也需要一個序號,因此必須將序號與接收序號分離。其二是因為這里的客戶端和服務器雙方通信是平等的,不像我們前面學習的http協議,服務器只能被動接收客戶端的消息,然后返回。

? ? ? ? 那為什么確認序號要+1呢?

? ? ? ? 其一是表示該序號之前的數據,我已經全部收到了。

?????????其二就是下一次發送,填寫數據從確認序號開始填寫。

?

(實際上序號是會由隨機值+數組下標進行確認,這樣可以極大避免重復,避免新服務取到老的數據,畢竟我們無法保證time wait之后網絡中的數據就真的全部消散了,這點可以看到后面連接之后再回來看)?

5.6個標記位?

通常來說,客戶端與服務器的比重是n:1,那么這些客戶端的狀態必然是不可能一模一樣的。

它們有的可能正在建立連接,有的正在通信,而有的需要斷開連接。但是不管做什么,它們都需要發送tcp報文給服務器。

因此 服務器需要辨別不同類型的報文,來進行不同的處理。此時6個標記位就可以用于標記報文的類型。

1.ACK: 確認號是否有效?

2.?SYN: 請求建立連接; 我們把攜帶SYN標識的稱為同步報文段

3.FIN: 通知對方, 本端要關閉了

這三個標記位較為好理解,分別對應建立連接,通信中,斷開連接三種情況。

4.PSH: 提示接收端應用程序立刻從TCP緩沖區把數據讀走

????????我們以服務器的接收緩沖區為例,客戶端一方向其寫數據,服務器一方從其讀數據。

????????這樣的一種機制,非常類似我們之前學習過的生產者消費者模型。流量控制本質就是對于?發送過程的一個同步的過程。

? ? ? ? ?如果客戶端將服務器的接收緩沖區寫滿了,而服務器端又遲遲不讀,那么客戶端方發的報文就會發送PSH,提醒服務器盡快把TCP的接收緩沖區內的數據讀走。

? ? ? ? 那如果服務器的接收緩沖區有空間了,客戶端如何知道呢?有兩種策略共同作用

? ? ? ? 1.客戶端向服務器發送報文詢問是否接收緩沖區有剩余空間,服務器進行響應

? ? ? ? 2.當服務器的接收緩沖區內的數據被讀走,存在剩余空間的時候,服務器主動向客戶端發送報文,告知客戶端。

5.?RST: 對方要求重新建立連接; 我們把攜帶RST標識的稱為復位報文段

我們首先需要認識到,雖然TCP保證可靠性,但是TCP是允許建立連接失敗的。?

因此我們引出下面這種情況來舉例子(連接異常還有許多情況)

1.客戶端與服務器進行三次握手建立連接。

????????由于第三次握手時,客戶端發送的報文是沒有應答的,那么對于客戶端來說當其發送了這條報文,它就認為連接已經建立了。而對于服務器來說,它必須接收到這條報文,它才認為連接成功建立。?

2.此時若服務器并沒有收到第三次握手的報文,服務器端認為此次連接建立失敗,而客戶端在發送完報文之后就認為連接建立成功了,就已經開始發送數據了。

3.那么當客戶端自顧自開始通信,服務器接收到報文后,服務器就會識別到這樣的報文是非法的,就會給客戶端返回攜帶填寫RSR標志位的報文,讓客戶端重新連接。

(當然,服務器要維護管理“連接”,也需要成本,管理策略也是先描述再組織)

除了這種,還有類似瀏覽器中連接被重置的情況

6.?URG: 緊急指針是否有效

這個標記位是與16位緊急指針相結合發揮作用的。

十六位緊急指針會指向數據中的一個位置,其大小為1字節,因此并不需要起始位置和長度。?

(將其設置為1個字節是因為tcp總體要保證數據的有序性,1字節的較小的數據,對于整體影響較小)

在上層,這個緊急數據被稱為帶外數據,我們調用send接口的時候將flags修改就能發送帶外數據了。

????????那么我們在什么情況才會使用這種帶外數據呢?

????????下面進行舉例。

????????例如我們客戶端向服務器 發起請求,但是發現這個服務器不給我們響應,或者響應非常慢。可是我們ping的時候發現這個服務器的機器是好的。此時我們就想知道是為什么。

? ? ? ? 我們就可以利用緊急數據來詢問服務器現在的狀態。不過這需要我們的服務器支持讀取緊急數據,并且相應軟件功能中支持提供狀態編號。

? ? ? ? 此時我們向服務器發送帶外數據,被服務器優先處理,并且響應可以同樣使用帶外數據返回結果,這樣客戶端就能快速獲得服務器的狀態了。?

?確認應答(ACK)機制

?

TCP將每個字節的數據都進行了編號. 即為序列號?

????????每一個ACK都帶有對應的確認序列號, 意思是告訴發送者, 我已經收到了哪些數據; 下一次你從哪里開始發.?

? ? ? ? 確認應答機制我們前面已經提過,這里不再重復,但是需要一提的是,這個序號和發送緩沖區的數組下標是兩套不同的機制,序號為了避免重復,起始序號可能會取一個隨機值,并且結合數據的位置,然后形成序號。我們前面那樣說是為了好理解一些

?超時重傳機制

?

主機A發送數據給B之后, 可能因為網絡擁堵等原因, 數據無法到達主機B;
如果主機A在一個特定時間間隔內沒有收到B發來的確認應答, 就會進行重發;?

?但是, 主機A未收到B發來的確認應答, 也可能是因為ACK丟失了

????????因此主機B會收到很多重復數據. 那么TCP協議需要能夠識別出那些包是重復的包, 并且把重復的丟棄掉.
????????這時候我們可以利用前面提到的序列號 , 就可以很容易做到去重的效果.
????????那么, 如果超時的時間如何確定??

?????????最理想的情況下, 找到一個最小的時間, 保證 "確認應答一定能在這個時間內返回".
????????但是這個時間的長短, 隨著網絡環境的不同, 是有差異的.
????????如果超時時間設的太長, 會影響整體的重傳效率;
????????如果超時時間設的太短, 有可能會頻繁發送重復的包;

TCP為了保證無論在任何環境下都能比較高性能的通信, 因此會動態計算這個最大超時時間.?

????????Linux中(BSD Unix和Windows也是如此), 超時以500ms為一個單位進行控制, 每次判定超時重發的超時時間都是500ms的整數倍.
????????如果重發一次之后, 仍然得不到應答, 等待 2*500ms 后再進行重傳.
????????如果仍然得不到應答, 等待 4*500ms 進行重傳. 依次類推, 以指數形式遞增.
????????累計到一定的重傳次數, TCP認為網絡或者對端主機出現異常, 強制關閉連接.?

連接管理機制?

在正常情況下, TCP要經過三次握手建立連接, 四次揮手斷開連接?

????????connect只負責 發起三次握手,至于三次握手的細節,上層的接口并不過多參與。

????????而accept并不參與三次握手,而是把建立好的鏈接直接拿上來,如果拿不到鏈接,它就會一直阻塞住。

? ? ? ? 客戶端TIME_WAIT之后會再等待一段時間,然后CLOSED。

? ? ? ? 如圖我們可以看到,客戶端把第三次的ACK發送,狀態就會更改為established,而服務器只有接收到這個ACK,其狀態才會變為established。和我們前面提的是一樣的。

? ? ? ? 所以客戶端和服務器在三次握手期間,對應的鏈接狀態是會改變的。這個狀態是由雙方的操作系統自主維護的。我們可以理解為客戶端有對應的INT字段,上面這些大寫的用下劃線隔開的都是宏。

TCP通信是基于連接的。

建立和斷開

三次握手和四次揮手。

這里三次握手實際上也可以看成四次握手,只是因為服務器捎帶應答了而已。

在建立連接的時候,正常情況下服務器肯定是會接受的。因此可以捎帶應答。

而斷開連接的時候,可能客戶端并沒有要發的數據了,已經斷開連接,可是服務器還有內容沒發,還不能斷開連接。所以可以捎帶應答的情況其實是具有偶然性的。?

那么我們為什么需要三次握手呢?

1.可靠地驗證全雙工。

????????三次握手,客戶端和服務器都至少進行過一次收和發,因此可以用于驗證全雙工。

????????有人說兩次握手客戶端和服務器也都至少進行過一次收和發,但是為什么不行呢?

????????因為只有兩次握手并不能可靠地驗證全雙工,沒有客戶端再返回的ACK,服務器無法證明自己發出的報文是否被客戶端接收。

2.奇數次握手可以保證一般情況下握手失敗的連接成本是嫁接在客戶端的

????????維護連接是有成本的,每一個連接都要消耗服務器的資源。?

????????如果一個客戶端向服務器發送大量的SYN,因為不需要應答,所以服務器只能接著這些SYN并建立連接。有人說這樣客戶端也是需要建立連接的。但要是我客戶端和你玉石俱焚呢?我就偏要一直發SYN。并且由于沒有應答,即使客戶端本身無法繼續建立連接,客戶端仍然可以繼續發送SYN,直到服務器沒辦法繼續建立連接。

? ? ? ? 這樣就很容易出現服務器上連接資源被占用滿的情況。這種我們就稱為SYN洪水。

? ? ? ? (雖然說三次握手遇到大量的SYN請求的沖擊也會收到影響,但是最起碼三次握手并沒有比較大的硬傷)

?

????????而進行兩次握手還有的一個比較大的問題就是這樣會優先使服務器做出建立連接的動作。

????????在服務器發送確認應答的時候服務器就會建立連接了,而客戶端需要接收到這個應答才會建立連接。

? ? ? ? 如此,如果服務器的應答丟失,服務器這邊是會正常消耗資源建立連接,可是客戶端是不會建立連接的。如果有5%的應答因為各種原因出現異常,那么此時這部分連接是不會有客戶端方發送信息的。這些資源就被白白浪費了。由于服務器與客戶端是1對N的關系,所以服務器方的影響會更大。因此,若是對某些資源讓步的話,優先要讓客戶端讓步。

為什么要進行四次揮手?

斷開連接:沒有數據要給對方發送了

發送數據是雙方都可能發。所以必須斷開兩次 。

????????而且實際上關閉連接的那方是可以收到另一方的數據的。所以如果客戶端已經沒有要發的數據了,關閉了連接,但是服務器還有要發的數據,那么服務器就可以先把要發的數據發了,然后再關閉連接

三次握手的一些補充內容?

?1.連接建立成功與否和上層accept是沒有關系的,三次握手是雙方操作系統自行完成的

2.listen的第二個參數(backlog)

?這個參數的值+1表示底層已經建立好的連接隊列的最大長度。客戶端與服務器三次握手建立的連接被稱為全連接,全連接建立好后會被放入全連接隊列。accept接收連接即從這個隊列里取。若全連接隊列已滿,服務器會丟棄后續的 ACK 包,導致客戶端重傳,此連接會被放入半連接隊列,并將服務器端連接狀態置為SYN_RECV

在全連接隊列和半連接隊列的長度有限的情況下,即使有非常多的syn請求被惡意發往服務器,此時服務器的資源仍然?不會被浪費太多。但是正常用戶卻很難再建立連接,這叫做SYN洪水。

3.服務器端不會長時間維持SYN_RECV狀態。被連接的一方處于SYN_RECV狀態被稱為半連接,半連接也由一個有一定規定長度的隊列管理。但是這種半連接的節點不會被長時間維護。若服務器未在規定時間內收到客戶端的 ACK,會重發 SYN-ACK(默認多次重試),超時后清除半連接記錄。如下圖所示。

4.服務器端與客戶端建立連接不一致的問題。從上面幾點我們可以看到,可能客戶端方連接處于ESTABLISHED狀態時,服務器方連接因為連接數目已經達到上限,還處于SYN_RECV狀態。甚至客戶端方連接處于ESTABLISHED狀態時,服務器方連接信息已經被清理。此時如果客戶端方發送數據給服務器,服務器會認為客戶端再次發起了連接,從而又重新處于SYN_RECV狀態。

5.全連接隊列為什么不能太長,為什么不能沒有

全連接隊列如果太長,會導致在上層忙碌無法繼續accept新連接的情況下,全連接隊列依然 會耗費資源維護那些不進行通信的連接,在上層忙碌的情況下還增加了系統的負擔。

如果沒有全連接隊列,那么當上層有資源進的時候,accept無法立刻接收連接,上層資源無法被充分利用。

四次揮手的一些補充知識

?理解TIME_WAIT狀態

???主動斷開連接的一方在四次揮手完成之后會進入time_wait狀態,等待若干時長之后會自動釋放。

?????在工程上,我們前面已經遇到過了,如果我們將服務器斷開,然后立即重啟,系統會提示我們端口號已經被占用 ,我們就無法立即重啟這個服務。此時我們需要在上層調用setsockopt接口,使得我們可以立即重啟服務。?

? ? ? ? 而客戶端雖然也會進入time_wait狀態,但是由于其端口號是由系統隨機綁定的,因此一般情況下不會影響使用。

那么TIME_WAIT等待多長時間?為什么要等待??

????????會等待兩個MSL時間,?MSL是TCP報文的最大生存時間(不是指報文從一端發送到另一端的時間,這個時間僅僅有幾毫秒),在RFC1122中規定為兩分鐘,但是各操作系統的實現不同, 在Centos7上默認配置的值是60s;,

?????????1.??因此TIME_WAIT持續存在2MSL的話就能保證在兩個傳輸方向上的尚未被接收或遲到的報文段都已經消失,一般來說即將這些報文收到后丟棄,因為如果超時了,服務器早已經重發過報文了(否則服務器立刻重啟, 可能會收到來自上一個進程的遲到的數據, 但是這種數據很可能是錯誤的);
????????2.同時也是在理論上保證最后一個報文可靠到達(假設最后一個ACK丟失, 那么服務器會再重發一個FIN. 這時雖然客戶端的進程不在了, 但是TCP連接還在, 仍然可以重發LAST_ACK),雖然超時之后服務器自動就會關閉,但是使得服務器正常關閉更顯其容錯性;

流量控制

????????接收端處理數據的速度是有限的. 如果發送端發的太快, 導致接收端的緩沖區被打滿, 這個時候如果發送端繼續發送,就會造成丟包, 繼而引起丟包重傳等等一系列連鎖反應.
????????因此TCP支持根據接收端的處理能力, 來決定發送端的發送速度. 這個機制就叫做流量控制(Flow Control);?

1.接收端將自己可以接收的緩沖區大小放入 TCP 首部中的 "窗口大小" 字段, 通過ACK端通知發送端;
2.窗口大小字段越大, 說明網絡的吞吐量越高;
3.接收端一旦發現自己的緩沖區快滿了, 就會將窗口大小設置成一個更小的值通知給發送端;
4.發送端接受到這個窗口之后, 就會減慢自己的發送速度;
5.如果接收端緩沖區滿了, 就會將窗口置為0; 這時發送方不再發送數據, 但是需要定期發送一個窗口探測數據段, 使接收端把窗口大小告訴發送端.

那么通信雙方第一次正式通信的時候,是怎么保證發送的數據量是合理的?

答:不要將三次握手僅僅理解為三次握手,雙方是交換了報文的,此時就已經協商了雙方的接受能力。

第一次與第二次握手不能攜帶數據,而第三次握手時是可以攜帶數據的(捎帶應答),這說明協商報文接受能力在前兩次握手的時候?已經完成了

16位窗口字段, 就是存放了窗口大小信息;
????????那么問題來了, 16位數字最大表示65535, 那么TCP窗口最大就是65535字節么?
? ? ? ? TCP窗口默認就是這么大,但是 TCP首部40字節選項中還包含了一個窗口擴大因子M, 實際窗口大小是 窗口字段的值左移 M 位(移幾位就乘以2的幾次);

????????如圖,接收端主機滿的情況下,發送端就不會再繼續發送帶數據的報文,而是會每隔一段時間發送一個窗口探測,進行詢問,接收端也會給予答復。

????????同時,當接收端有空閑資源的時候,接收端也會主動向發送端發送窗口更新通知。

????????這樣雙方都有主動的權利,一方出現異常,另一方也能及時通知到,提高了容錯性。并且如果雙方都無法把自己的信息發給對方,那就說明連接出現異常,一段時間后這個異常連接就會被關閉。?

? ? ? ? 那么流量控制是屬于可靠性,還是屬于效率呢?

? ? ? ? 直接上肯定是屬于可靠性的,因為它可以避免正常的丟包。不過側面來說,它避免了正常丟包,避免了大量的重傳,也就提高了效率

滑動窗口

1.已經發出去,但是暫時沒有收到應答的報文,它會被tcp暫時保存起來

2.像這樣的報文,可能會在發送方存在多個

那么這些報文會被保存在哪里呢?

實際上我們發送的數據,它們原本就在發送緩沖區中,發送只不過是拷貝一次到下一層去。

因此我們可以將發送緩沖區大致分為三個部分。(但是其實待發送區后還有一塊空間)

第一部分是已經發送并且被對方確認的消息,這部分空間可以被覆蓋,那么我們就可以理解為這部分數據已經被移除,因為系統已經不再維護它了

第二部分是可以進行發送,或者已經發送但是未被確認的數據的區域。

第三部分則是待發送的區域。?

1.滑動窗口在哪里? 是我們發送緩沖區的一部分

?2.滑動窗口的最大大小? 目前我們認為是對方接收窗口的大小,即不超過對方的接收緩沖區的剩余空間大小。

3.如何理解區域劃分?通過指針/下標來區分即可

問題一、如果丟包了怎么辦?

a.應答丟失

我們這時候要聯想到確認序號的定義,確認序號x是指序號之前的報文我們全都收到了。

(這保證了滑動窗口線性連續地向右更新,不出現跳躍)

????????以下圖為例,即使2001,3001?,4001都丟了,只要5001的應答成功發送過去,對方就知道5001前的報文都已經被成功收到了。因此其實是允許少量ack丟失的。

? ? ? ? 如果是5001的ack丟失,那么對方接收不到就只能等待超時重傳了,但是這種情況對資源的浪費并不大。

?

b.攜帶數據的報文丟失?

????????如果某個報文丟失,由于確認序號的定義,后續的確認應答都將會是最后一個正常發送的報文的確認序號,如圖為1001.?

? ? ? ? 而發送端如果收到三個一樣的確認應答時,會立即對未成功發送的報文進行重發,這被稱為快重傳。重發的報文成功發送至接收端的之后,接收端的確認應答會更新為最新一個正常發送的報文的確認應答,圖中更新為7001.

? ? ? ? 有人說已經有了快重傳,為什么還需要有超時重傳?

? ? ? ? 因為超時重傳雖然好,但是它是有條件的,即必須收到三個重復的確認應答。這種快重傳對大量數據通信的時候可以很好地提高效率。但是如果發送的報文數目較少,那么就只能等待超時重傳了,即超時重傳可以理解為是兜底的

?

問題二、滑動窗口如何移動?

滑動窗口是不能向左移動的,但是會向右移動。移動的時候大小是會動態變化的。

我們這里提供一鐘理解方式,幫助理解。

即我們把滑動窗口的兩段用start和end作標記。

每次start根據確認序號進行更新。

而end則是通過確認序號和對方返回的窗口大小共同作用,進行更新。這樣對方的窗口大小動態變化,我們滑動窗口的大小也會動態變化。這也就和流量控制聯系起來了。?

(不過有時候待發送區的有效數據較少,那么end更新到待發送區結尾即可)

?

問題三、滑動窗口會在滑動的過程中越界嗎??

并不會,tcp處理滑動窗口的時候采用了環狀算法,思想和我們數組中取模來模擬環形隊列類似。?

延遲應答?

如果接收數據的主機立刻返回ACK應答, 這時候返回的窗口可能比較小.

1.假設接收端緩沖區為1M. 一次收到了500K的數據; 如果立刻應答, 返回的窗口就是500K;

2.但實際上可能處理端處理的速度很快, 10ms之內就把500K數據從緩沖區消費掉了;
3.在這種情況下, 接收端處理還遠沒有達到自己的極限, 即使窗口再放大一些, 也能處理過來;
4.如果接收端稍微等一會再應答, 那么這個時候返回的窗口大小就是1M;?

?????????一定要記得, 窗口越大, 網絡吞吐量就越大, 傳輸效率就越高. 我們的目標是在保證網絡不擁塞的情況下盡量提高傳輸效率,但是同樣,也不是所有的包都能延遲應答的,例如實時性要求很強的場景下,這時候包就不適合延遲應答。

延遲應答一般有以下策略

1.數量限制: 每隔N個包就應答一次;
2.時間限制: 超過最大延遲時間就應答一次;
具體的數量和超時時間, 依操作系統不同也有差異; 一般N取2, 超時時間取200ms;

捎帶應答

????????在延遲應答的基礎上, 我們發現, 很多情況下, 客戶端服務器在應用層也是 "一發一收" 的. 意味著客戶端給服務器說了 "How are you", 服務器也會給客戶端回一個 "Fine, thank you";
????????那么這個時候ACK就可以搭順風車, 和服務器回應的 "Fine, thank you" 一起回給客戶端?

? ? ? ? 捎帶應答我們在前面就已經提過了,這里不再贅述。

小總結?

????????tcp在可靠性和提高性能的方面都作出了很大努力。

????????三次握手除了建立連接也協商了起始序列號和雙方緩沖區大小。

????????但是我們也發現,幾乎所有的策略,都是在兩段的機器上起作用的。

?????????但是數據包大部分時候都是在網上的,因此我們還應該考慮網絡狀態,應該對網絡信道有所評估。(但也只是評估,tcp并不能直接影響網絡。客戶端和服務器對于網絡出現的問題是無能為力的)因此我們引出擁塞控制

擁塞控制?

????????有人可能會問,網絡資源那么多,會因為我們一個主機發送數據的多少受影響嗎?

????????我們一個主機當然不能影響,但是tcp協議這么規定,實際上是讓所有的主機對網絡擁塞形成共識,當所有主機都意識到網絡擁塞并減少報文發送,那么此時網絡資源就會有很大的空余,等網絡緩過來,我們再慢慢恢復通信。

? ? ? ? 而且也并不是所有主機都會同時意識到網絡擁塞,例如某些主機已經意識到網絡擁塞并且減少了報文發送,另一些主機剛開始發送報文,發送了兩個,雖然都丟了,但是由于其它主機減少了報文發送使網絡恢復,后續報文又都正常發送,此時該主機就不會意識到網絡擁塞了。

?

那么tcp到底會怎么做呢?

?????????雖然TCP有了滑動窗口這個大殺器, 能夠高效可靠的發送大量的數據. 但是如果在剛開始階段就發送大量的數據, 仍然可能引發問題.
????????因為網絡上有很多的計算機, 可能當前的網絡狀態就已經比較擁堵. 在不清楚當前網絡狀態下, 貿然發送大量的數據,是很有可能引起雪上加霜的.
????????TCP引入 慢啟動 機制, 先發少量的數據, 探探路, 摸清當前的網絡擁堵狀態, 再決定按照多大的速度傳輸數據;

1.此處引入一個概念程為擁塞窗口
2.發送開始的時候, 定義擁塞窗口大小為1;
3.每次收到一個ACK應答, 擁塞窗口加1;(這樣就會形成一個指數級的增長)
4.每次發送數據包的時候, 將擁塞窗口和接收端主機反饋的窗口大小做比較, 取較小的值作為實際發送的窗口;?

像上面這樣的擁塞窗口增長速度, 是指數級別的. "慢啟動" 只是指初使時慢, 但是增長速度非常快.
1.為了不增長的那么快, 因此不能使擁塞窗口單純的加倍.

2.此處引入一個叫做慢啟動的閾值
3.當擁塞窗口超過這個閾值的時候, 不再按照指數方式增長, 而是按照線性方式增長?

?????????當TCP開始啟動的時候, 慢啟動閾值等于窗口最大值;
????????在每次超時重發的時候, 慢啟動閾值會變成原來的一半, 同時擁塞窗口置回1;

????????少量的丟包, 我們僅僅是觸發超時重傳; 大量的丟包, 我們就認為網絡擁塞;
????????當TCP通信開始后, 網絡吞吐量會逐漸上升; 隨著網絡發生擁堵, 吞吐量會立刻下降;
????????擁塞控制, 歸根結底是TCP協議想盡可能快的把數據傳輸給對方, 但是又要避免給網絡造成太大壓力的折中方案.

面向字節流

?創建一個TCP的socket, 同時在內核中創建一個 發送緩沖區 和一個 接收緩沖區;

1.調用write時, 數據會先寫入發送緩沖區中;
2.如果發送的字節數太長, 會被拆分成多個TCP的數據包發出;
3.如果發送的字節數太短, 就會先在緩沖區里等待, 等到緩沖區長度差不多了, 或者其他合適的時機發送出去;
4.接收數據的時候, 數據也是從網卡驅動程序到達內核的接收緩沖區;
5.然后應用程序可以調用read從接收緩沖區拿數據;
6.另一方面, TCP的一個連接, 既有發送緩沖區, 也有接收緩沖區, 那么對于這一個連接, 既可以讀數據, 也可以寫數據. 這個概念叫做 全雙工?

由于緩沖區的存在, TCP程序的讀和寫不需要一一匹配, 例如:
寫100個字節數據時, 可以調用一次write寫100個字節, 也可以調用100次write, 每次寫一個字節;
讀100個字節數據時, 也完全不需要考慮寫的時候是怎么寫的, 既可以一次從緩沖區read 100個字節, 也可以每次從緩沖區read一個字節, 重復100次;

????????例如用戶層認為它發送了四個請求,但是TCP并不這樣看,它只認為這就是一串字節數據。交給接收端之后,接收端也是這么認為的。tcp協議不關心上層協議,也不關心上層報文格式,只有字節概念。因此向我們之前寫的網絡版本計算器,我們在應用層要不斷地讀,然后自行分析收到的數據。

粘包問題

這是一個應用層的問題。?

?????????首先要明確, 粘包問題中的 "包" , 是指的應用層的數據包.
????????在TCP的協議頭中, 沒有如同UDP一樣的 "報文長度" 這樣的字段, 但是有一個序號這樣的字段.
站在傳輸層的角度, TCP是一個一個報文過來的. 按照序號排好序放在緩沖區中.
????????站在應用層的角度, 看到的只是一串連續的字節數據.
????????那么應用程序看到了這么一連串的字節數據, 就不知道從哪個部分開始到哪個部分, 是一個完整的應用層數據包.

那么如何避免粘包問題呢? 歸根結底就是一句話, 明確兩個包之間的邊界(這就是為什么我們前面寫網絡計算器的時候encode和decode)

即在應用層定義協議

如何在應用層解決粘包問題?下面簡單列舉幾個策略

1.采用定長報文? 例如我們規定一個報文為100字節,那么每次將數據讀上來之后按照100字節分隔即可

2.使用特殊字符分隔? 例如我們某次通信發送的數據中不帶有換行符,我們就可以以換行符作為分隔。

3.自描述字段+定長報頭? 例如我們規定報頭是八個字節,前四個字節用于描述有效載荷的長度

4.自描述字段+特殊字符 例如我們http協議里用換行符作為特殊字符接收請求行和請求報頭,然后有一個content?length作為自描述字段來描述有效載荷,同時也有一個空行用于分隔。

思考: 對于UDP協議來說, 是否也存在 "粘包問題" 呢?
????????對于UDP, UDP是一個一個把數據交付給應用層. 就有很明確的數據邊界.
????????站在應用層的角度, 使用UDP的時候, 要么收到完整的UDP報文, 要么不收. 不會出現"半
個"的情況.

TCP異常情況

?1.進程終止: 進程終止會釋放文件描述符, 但是仍然可以發送FIN. 和正常關閉沒有什么區別.

連接 是和文件直接相關的,文件的生命周期又隨進程

2.機器重啟: 和進程終止的情況相同.機器要重啟就需要關閉所有進程。

3.機器掉電/網線斷開: 接收端和發送端無法進行四次揮手,此時接收端認為連接還在, 一旦接收端有寫入操作, 接收端發現連接已經不在了, 就會進行reset. 即使沒有寫入操作, TCP自己也內置了一個保活定時器 。

用UDP實現可靠傳輸(經典面試題)

?參考TCP的可靠性機制, 在應用層實現類似的邏輯;
例如:
引入序列號, 保證數據順序;
引入確認應答, 確保對端收到了數據;
引入超時重傳, 如果隔一段時間沒有應答, 就重發數據;
......

????????最最重要的是要問清楚應用場景,既然要用udp實現可靠傳輸,那么肯定只需要一部分的可靠性保證,否則直接使用tcp不就好了

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/83765.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/83765.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/83765.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java 實現excel文件轉pdf | 無水印 | 無限制

文章目錄 目錄 文章目錄 前言 1.項目遠程倉庫配置 2.pom文件引入相關依賴 3.代碼破解 二、Excel轉PDF 1.代碼實現 2.Aspose.License.xml 授權文件 總結 前言 java處理excel轉pdf一直沒找到什么好用的免費jar包工具,自己手寫的難度,恐怕高級程序員花費一年的事件,也…

Keil調試模式下,排查程序崩潰簡述

在Keil調試模式下,若程序崩潰,可以通過以下步驟來定位崩潰的位置: 一、查看調用棧(Call Stack) 打開調用棧窗口: 在Keil的調試模式下,點擊菜單欄的“View” -> “Call Stack Window”&…

深度解析Mysql中MVCC的工作機制

MVCC,多版本并發控制 定義:維護一個數據的多個版本,使讀寫操作沒有沖突,依賴于:隱藏字段,undo log日志,readView MVCC會為每條版本記錄保存三個隱藏字段 DB_TRX_ID: 記錄最近插入或修改該記錄的事務IDDB_R…

uniapp+vue3實現CK通信協議(基于jjc-tcpTools)

1. TCP 服務封裝 (tcpService.js) export class TcpService {constructor() {this.connections uni.requireNativePlugin(jjc-tcpTools)this.clients new Map() // 存儲客戶端連接this.servers new Map() // 存儲服務端實例}// 創建 TCP 服務端 (字符串模式)createStringSe…

學習設計模式《十二》——命令模式

一、基礎概念 命令模式的本質是【封裝請求】命令模式的關鍵是把請求封裝成為命令對象,然后就可以對這個命令對象進行一系列的處理(如:參數化配置、可撤銷操作、宏命令、隊列請求、日志請求等)。 命令模式的定義:將一個…

Webpack的基本使用 - babel

Mode配置 Mode配置選項可以告知Webpack使用相應模式的內置優化 默認值是production(什么都不設置的情況下) 可選值有:none | development | production; 這幾個選項有什么區別呢? 認識source-map 我們的代碼通常運行在瀏覽器…

「基于連續小波變換(CWT)和卷積神經網絡(CNN)的心律失常分類算法——ECG信號處理-第十五課」2025年6月6日

一、引言 心律失常是心血管疾病的重要表現形式,其準確分類對臨床診斷具有關鍵意義。傳統的心律失常分類方法主要依賴于人工特征提取和經典機器學習算法,但這些方法往往受限于特征選擇的主觀性和模型的泛化能力。 隨著深度學習技術的發展,基于…

C++.OpenGL (11/64)材質(Materials)

材質(Materials) 真實感材質系統 #mermaid-svg-NjBjrmlcpHupHCFQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NjBjrmlcpHupHCFQ .error-icon{fill:#552222;}#mermaid-svg-NjBjrmlcpHupHCFQ .error-text{fill:…

P1345 [USACO5.4] 奶牛的電信Telecowmunication

P1345 [USACO5.4] 奶牛的電信Telecowmunication 突然發現 USACO 好喜歡玩諧音梗。 題意就是給定一個無向圖,問你要刪多少點才能使 s , t s,t s,t 不連通。 注意是刪點而不是刪邊,所以不能直接使用最小割來求。所以考慮變換一下題目模型。 經典 tric…

EXCEL如何快速批量給兩字姓名中間加空格

EXCEL如何快速批量給姓名中間加空格 優點:不會導致排版混亂 缺點:無法輸出在原有單元格上,若需要保留原始數據,可將公式結果復制后“選擇性粘貼為值” 使用場景:在EXCEL中想要快速批量給兩字姓名中間加入空格使姓名對…

使用vtk8.2.0加載dicom圖像

1 上一篇文章我們已經編譯好了VTK的dll,下面我們就來加載他。 2 在Pro里面加載dll #------------------------------------------------- # # Project created by QtCreator 2024-02-04T14:39:07 # #-------------------------------------------------QT …

使用vsftpd搭建FTP服務器(TLS/SSL顯式加密)

安裝vsftpd服務 使用vsftpd RPM安裝包安裝即可,如果可以訪問YUM鏡像源,通過dnf或者yum工具更加方便。 yum -y install vsftpd 啟動vsftpd、查看服務狀態 systemctl enable vsftpd systemctl start vsftpd systemctl status vsftpd 備份配置文件并進…

鴻蒙OSUniApp集成WebGL:打造跨平臺3D視覺盛宴#三方框架 #Uniapp

UniApp集成WebGL:打造跨平臺3D視覺盛宴 在移動應用開發日新月異的今天,3D視覺效果已經成為提升用戶體驗的重要手段。本文將深入探討如何在UniApp中集成WebGL技術,實現炫酷的3D特效,并特別關注鴻蒙系統(HarmonyOS)的適配與優化。 …

前端文件下載常用方式詳解

在前端開發中,實現文件下載是常見的需求。根據不同的場景,我們可以選擇不同的方法來實現文件流的下載。本文介紹三種常用的文件下載方式: 使用 axios 發送 JSON 請求下載文件流使用 axios 發送 FormData 請求下載文件流使用原生 form 表單提…

MacOS解決局域網“沒有到達主機的路由 no route to host“

可能原因:MacOS 15新增了"本地網絡"訪問權限,在 APP 第一次嘗試訪問本地網絡的時候會請求權限,可能順手選擇了關閉。 解決辦法:給想要訪問本地網絡的 APP (例如 terminal、Navicat、Ftp)添加訪問…

中英文實習證明模板:一鍵生成標準化實習證明,助力實習生職場發展

中英文實習證明模板:一鍵生成標準化實習證明,助力實習生職場發展 【下載地址】中英文實習證明模板 這份中英文實習證明模板專為實習生設計,內容簡潔專業,適用于多種場景。模板采用中英文對照格式,方便國際交流與使用。…

RocketMQ運行架構和消息模型

運?架構 nameServer 命名服務 NameServer 是 RocketMQ 的 輕量級注冊中心,負責管理集群的路由信息(Broker 地址、Topic 隊列分布等),其核心作用是解耦 Broker 與客戶端,實現動態服務發現。broker 核?服務 RocketMQ最…

C++學習-入門到精通【11】輸入/輸出流的深入剖析

C學習-入門到精通【11】輸入/輸出流的深入剖析 目錄 C學習-入門到精通【11】輸入/輸出流的深入剖析一、流1.傳統流和標準流2.iostream庫的頭文件3.輸入/輸出流的類的對象 二、輸出流1.char* 變量的輸出2.使用成員函數put進行字符輸出 三、輸入流1.get和getline成員函數2.istrea…

OpenCV 圖像像素的邏輯操作

一、知識點 1、圖像像素的邏輯操作,指的是位操作bitwise,與、或、非、異或等。 2、位操作簡介: 位1 位2 與and 或or 異或xor0 0 0 0 00 1 0 1 11 0 0 …

【AAOS】【源碼分析】用戶管理(二)-- 整體架構

整體介紹 Android多用戶功能作為 Android Automotive 的重要組成部分,為不同駕駛員和乘客提供了一個更加定制化、隱私保護的使用環境。Android 多用戶的存在,它可以讓多個用戶使用同一臺設備,同時保持彼此的數據、應用和設置分隔開來。 各用戶類型的權限 能力SystemAdminS…