傳輸層協議

傳輸層協議

  • 再談端口號
    • 端口號范圍劃分
    • 認識知名端口號
    • 兩個問題
    • netstat
    • pidof
  • UDP協議
    • UDP協議端格式
    • UDP的特點
    • 面向數據報
    • UDP的緩沖區
    • UDP使用注意事項
    • 基于UDP的應用層協議
  • TCP協議
    • TCP協議段格式
    • 確認應答(ACK)機制
    • 超時重傳機制
    • 連接管理機制
    • 理解 CLOSE_WAIT 狀態
    • 理解TIME_WAIT狀態
    • 解決TIME_WAIT狀態引起的bind失敗的方法
    • 滑動窗口
    • 流量控制
    • 擁塞控制
    • 延遲應答
    • 捎帶應答
    • 面向字節流
    • 粘包問題
    • TCP異常情況
    • TCP小結
  • TCP/UDP對比
  • TCP 相關實驗
    • 理解 listen 的第二個參數

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

再談端口號

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

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

在這里插入圖片描述

端口號范圍劃分

  • 0 - 1023: 知名端口號, HTTP, FTP, SSH等這些廣為使用的應用層協議, 他們的端口號都是固定的
  • 1024 - 65535: 操作系統動態分配的端口號. 客戶端程序的端口號, 就是由操作系統從這個范圍分配的

認識知名端口號

有些服務器是非常常用的, 為了使用方便, 人們約定一些常用的服務器, 都是用以下這些固定的端口號:

  • ssh服務器, 使用22端口
  • http服務器, 使用80端口
  • https服務器, 使用443
  • telnet服務器, 使用23端口
  • ftp服務器, 使用21端口

兩個問題

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

一個端口號是否可以被多個進程bind?
端口號->進程需要確保唯一關系,所以不可以

netstat

用來查看網絡狀態
常用選項:

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

在這里插入圖片描述

pidof

用來查看服務器的進程id

在這里插入圖片描述

UDP協議

所有協議都包括包括和有效載荷,學習協議的目的就是學習如何解包和分用

UDP協議端格式

在這里插入圖片描述

  • 16位UDP長度, 表示整個數據報(UDP首部+UDP數據)的最大長度;
  • 如果校驗和出錯, 就會直接丟棄

所謂的協議報頭其本質就是一種結構化數據對象
在這里插入圖片描述

UDP的特點

UDP傳輸的過程類似于寄信:

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

面向數據報

應用層交給UDP多長的報文, UDP原樣發送, 既不會拆分, 也不會合并

例如,發送端發送100次,接收端也必須接收100次

UDP的緩沖區

在網絡通信中,發送端應用層其實并不是直接將數據發送至網絡中,而是將數據層層向下傳遞;通信所使用的IO接口,其實也不是發送接收接口,而是拷貝接口

在這里插入圖片描述

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

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

UDP使用注意事項

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

基于UDP的應用層協議

  • NFS: 網絡文件系統
  • TFTP: 簡單文件傳輸協議
  • DHCP: 動態主機配置協議
  • BOOTP: 啟動協議(用于無盤設備啟動)
  • DNS: 域名解析協議

TCP協議

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

TCP協議段格式

在這里插入圖片描述

  • 源/目的端口號: 表示數據是從哪個進程來, 到哪個進程去
  • 32位序號/32位確認號: 后面詳細講
  • 4位首部長度: 表示該TCP報頭有多少個32位bit(有多少個4字節); 所以TCP報頭最大長度是15 * 4 = 60
  • 6位標志位:標識報文的類型,接收方根據不同類型的報文,采取對應的動作
    URG: 緊急指針是否有效
    ACK: 確認序號是否有效
    PSH: 提示接收端應用程序立刻從TCP緩沖區把數據讀走
    RST: 對方要求重新建立連接; 我們把攜帶RST標識的稱為復位報文段
    SYN: 請求建立連接; 我們把攜帶SYN標識的稱為同步報文段
    FIN: 通知對方, 本端要關閉了, 我們稱攜帶FIN標識的為結束報文段
  • 16位窗口大小: 后面再說
  • 16位校驗和: 發送端填充, CRC校驗. 接收端校驗不通過, 則認為數據有問題. 此處的檢驗和不光包含TCP首部, 也包含TCP數據部分
  • 16位緊急指針: 標識哪部分數據是緊急數據
  • 40字節頭部選項: 暫時忽略

如何解包?

  1. TCP協議是定長報頭:20字節;先讀取前20字節
  2. 因為協議報頭是結構化數據,所以提取報頭中的4位首部長度
  3. 計算后續報頭的大小:選項=4位首部長度-20
  4. 報頭處理完畢,剩余的便是有效載荷

如何分用?
協議報頭中包含目的端口號,找到應用層對應的進程,將數據交付給進程

接收端收到一報文后,如何找到曾經綁定的特定端口的進程?網絡協議棧和文件又是什么關系?
系統中有很多場景需要快速定位一個進程,所采取的方式就是將進程和端口放入哈希表中,如此一來通過解包報文便可得到端口,再到表中快速定位進程;找到進程之后,每個進程都有PCB進程管理。通過讀取到的socket(文件描述符),PCB指向文件描述符數據,便可找到對應的文件結構體,網絡協議棧中傳輸層將數據拷貝到文件結構體的讀寫緩存區,依次向上交付,最終到達應用層

在這里插入圖片描述

確認應答(ACK)機制

為什么網絡傳輸中,會存在不可靠問題?
理解起來很簡單,舉個栗子:如果兩個人面對面交談,沒有什么問題,但是如果兩個人相隔很遠,進行交談,對方說話的內容都很難聽到何談交流呢?網絡也是如此
不可靠的場景如:丟包,亂序,重復,校驗錯誤等

如何保證報文的可靠性呢?
我們認為一個報文只要收到了應答就能保證此報文的可靠性
例如:
在這里插入圖片描述

收到了應答,只能確保歷史消息對方已經收到;由于是雙方通信,一定存在最新的消息,沒有被應答

TCP收發消息的工作模式
在這里插入圖片描述

真實的工作模式
在這里插入圖片描述

雙方在通信的過程中,每一方都需要應答;除了正常的數據報,也包括確認數據報

TCP將每個字節的數據都進行了編號. 即為序列號
在這里插入圖片描述

雙方通信過程:

在這里插入圖片描述

因為數據(報文)到達接收方時,順序不一定和發送時一致,如果采取任何措施,便會造成數據亂序的結果

TCP數據段采取序列號來標識數據段,因此應答報文中,對應的報頭中確認報頭;所以確認應答和確認序號確保了接收方已經收到ack序號之前的所有報文(必須是連續的);同時也解釋了為什么報頭中會存在兩組序號(序號,確認序號)

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

至此TCP報頭中還剩余16位窗口大小沒有介紹,在這之前先引入一個新的問題:TCP通信時,數據不能發送太快但也不能太慢,必須有合適的速度;但是作為發送方又該如何得知,發送的數據是適合的呢?只能通過對方的反饋從而得知對方的接收緩沖區的剩余大小,不過雙方都還沒通信,怎么會知道對方緩沖區大小呢?這不就變成了先有雞還是先有蛋的問題了嗎?

因此,在第一次請求時,發送方就會將自己的接收緩沖區大小存放在16位窗口大小中發送給對方,對方亦是如此;并且發送的數據并不會立刻被移除,而是必須在發送緩沖區維持一段時間;由此雙方便交換了接收能力,也能達到流量控制

超時重傳機制

在這里插入圖片描述

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

不過, client未收到server發來的確認應答, 也可能是因為ACK丟失
在這里插入圖片描述

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

發送方是如何判定丟包的呢?
其實真正有沒有丟包,發送方也不清楚,只是如果超時了,就會被判定丟包

發送端發送出去的數據,并不會立刻被移除。而是會在發送緩沖區中保存一段時間,以防丟包,再次發送

那么, 如果超時的時間如何確定?

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

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

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

連接管理機制

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

三次握手
在這里插入圖片描述

客戶端狀態轉化:

  • [CLOSED -> SYN_SENT] 客戶端調用connect, 發送同步報文段
  • [SYN_SENT -> ESTABLISHED] connect調用成功, 則進入ESTABLISHED狀態, 開始讀寫數據

服務端狀態轉化:

  • [CLOSED -> LISTEN] 服務器端調用listen后進入LISTEN狀態, 等待客戶端連接
  • [LISTEN -> SYN_RCVD] 一旦監聽到連接請求(同步報文段), 就將該連接放入內核等待隊列中, 并向客戶端發送SYN確認報文
  • [SYN_RCVD -> ESTABLISHED] 服務端一旦收到客戶端的確認報文, 就進入ESTABLISHED狀態, 可以進行讀寫數據

為什么要三次握手呢?既然是三次握手就表示一次,兩次都不可以,為什么呢?
一次握手:sever需要維護好已經建立好的鏈接,如果只握手一次,可能會遭到SYN洪水;兩次握手也是如此
四次握手其實也可以,但是沒有必要
三次握手是用最小成本驗證全雙工通信是否通暢,也可以有效防止單機對服務器進行攻擊

三次握手不一定會成功,最擔心的是最后一個ACK丟失,不過存在對應的解決措施;鏈接也是要被管理的(OS),先描述,再組織

四次揮手
在這里插入圖片描述

客戶端狀態轉化:

  • [ESTABLISHED -> FIN_WAIT_1] 客戶端主動調用close時, 向服務器發送結束報文段, 同時進入FIN_WAIT_1
  • [FIN_WAIT_1 -> FIN_WAIT_2] 客戶端收到服務器對結束報文段的確認, 則進入FIN_WAIT_2, 開始等待服務器的結束報文段
  • [FIN_WAIT_2 -> TIME_WAIT] 客戶端收到服務器發來的結束報文段, 進入TIME_WAIT, 并發出LAST_ACK
  • [TIME_WAIT -> CLOSED] 客戶端要等待一個2MSL(Max Segment Life, 報文最大生存時間)的時間, 才會進入CLOSED狀態

服務端狀態轉化:

  • [ESTABLISHED -> CLOSE_WAIT] 當客戶端主動關閉連接(調用close), 服務器會收到結束報文段, 服務器返回確認報文段并進入CLOSE_WAIT
  • [CLOSE_WAIT -> LAST_ACK] 進入CLOSE_WAIT后說明服務器準備關閉連接(需要處理完之前的數據); 當服務器真正調用close關閉連接時, 會向客戶端發送FIN, 此時服務器進入LAST_ACK狀態, 等待最后一個ACK到來(這個ACK是客戶端確認收到了FIN)
  • [LAST_ACK -> CLOSED] 服務器收到了對FIN的ACK, 徹底關閉連接

客戶端在FIN_WAIT_2狀態時,所謂的不發數據(用戶),指的是不發送用戶數據,并不代表底層沒有管理報文的交互;斷開是雙方的事情,需要征得雙方同意

主動斷開鏈接的一方,最終狀態是TIME_WAIT狀態
被動斷開鏈接的一方,兩次揮手完成,會進入CLOSED_WAIT狀態

理解 CLOSE_WAIT 狀態

如果在服務器中將關閉文件描述符的代碼注釋掉,觀察運行結果
客服端斷開之前
在這里插入圖片描述
客戶端斷開之后
在這里插入圖片描述

此時服務器進入了 CLOSE_WAIT 狀態, 結合我們四次揮手的流程圖, 可以認為四次揮手沒有正確完成,因為關閉文件描述符的操作沒有實現

對于服務器上出現大量的 CLOSE_WAIT 狀態, 原因就是服務器沒有正確的關閉 socket, 導致四次揮手沒有正確完成. 這是一個 BUG. 只需要加上對應的 close 即可解決問題

理解TIME_WAIT狀態

首先啟動server,然后啟動client,然后用Ctrl-C使server終止,這時馬上再運行server, 結果是
在這里插入圖片描述

這是因為,雖然server的應用程序終止了,但TCP協議層的連接并沒有完全斷開,因此不能再次監 聽同樣的server端口

  • TCP協議規定,主動關閉連接的一方要處于TIME_ WAIT狀態,等待兩個MSL(maximum segment lifetime)的時間后才能回到CLOSED狀態
  • 我們使用Ctrl-C終止了server, 所以server是主動關閉連接的一方, 在TIME_WAIT期間仍然不能再次監聽同樣的server端口
  • MSL在RFC1122中規定為兩分鐘,但是各操作系統的實現不同, 在Centos7上默認配置的值是60s

為什么是TIME_WAIT的時間是2MSL?

  • MSL是TCP報文的最大生存時間, 因此TIME_WAIT持續存在2MSL的話
  • 就能保證在兩個傳輸方向上的尚未被接收或遲到的報文段都已經消失(否則服務器立刻重啟, 可能會收到來自上一個進程的遲到的數據, 但是這種數據很可能是錯誤的)
  • 同時也是在理論上保證最后一個報文可靠到達(假設最后一個ACK丟失, 那么服務器會再重發一個FIN. 這時雖然客戶端的進程不在了, 但是TCP連接還在, 仍然可以重發LAST_ACK)

四次揮手的動作已經完成,但是主動斷開鏈接的一方要維持一段時間的TIME_WAIT,為什么呢?
需要保證最后一個ACK盡可能地被對方收到;雙方在斷開鏈接時,網絡中可能還有滯留的報文,保證滯留報文進行消散

服務器有時可以立即重啟,但是上面這種情況無法進行立即重啟,如果在服務器運行繁忙時,此時會非常棘手

解決TIME_WAIT狀態引起的bind失敗的方法

在server的TCP連接沒有完全斷開之前不允許重新監聽, 某些情況下可能是不合理的

  • 服務器需要處理非常大量的客戶端的連接(每個連接的生存時間可能很短, 但是每秒都有很大數量的客戶端來請求)
  • 這個時候如果由服務器端主動關閉連接(比如某些客戶端不活躍, 就需要被服務器端主動清理掉), 就會產生大量TIME_WAIT連接
  • 由于我們的請求量很大, 就可能導致TIME_WAIT的連接數很多, 每個連接都會占用一個通信五元組(源ip, 源端口, 目的ip, 目的端口, 協議). 其中服務器的ip和端口和協議是固定的. 如果新來的客戶端連接的ip和端口號和TIME_WAIT占用的鏈接重復了就會出現問題

使用setsockopt()設置socket描述符的 選項SO_REUSEADDR為1, 表示允許創建端口號相同但IP地址不同的多個socket描述符

int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);

在這里插入圖片描述

再次運行服務器

在這里插入圖片描述

滑動窗口

剛才我們討論了確認應答策略, 對每一個發送的數據段, 都要給一個ACK確認應答. 收到ACK后再發送下一個數據段;這樣做有一個比較大的缺點, 就是性能較差. 尤其是數據往返的時間較長的時候;并且如果我們發送數據,沒有收到應答之前,必須將已發送的數據暫時保存起來,為了支持超時重傳,所保存的地方其實就是發送緩沖區,發送緩沖區和滑動窗口緊密關系
不嚴格的情況下,發送緩沖區可以分為三部分:
在這里插入圖片描述

其中已發送&&!收到應答的部分稱作滑動窗口

如何看待滑動窗口呢?
可以將其理解為一個字符數組

在這里插入圖片描述

窗口移動,其本質就是下標在進行更新
目前的理解是滑動窗口的大小和對方的接收能力有關,無論未來怎么滑動,都要保證對方能夠進行正常的接收
win_start=ack_seq;win_end=win_start+tcp_win
ack_seq報文的確認序號 tcp_win對方的接收緩沖區剩余空間

窗口一定會向右滑動嗎?會向左滑動嗎?
可能向右滑動,取決于右邊界的變化;一定不會向左滑動,因為收到應答的數據不可能變成沒有收到應答

窗口一定會一直不變嗎?會變大嗎?會變小嗎?為什么?
窗口一定會變化的;當發送的數據越來越多,對方一次將大量的數據從接收緩沖區中取走,此時窗口就會變大;當對方一直不取走接收緩沖區中數據,窗口便會變小

收到應答的時候,如果不是最左側發送的報文的確認,而是中間的,或者最右側的怎么辦?需要滑動嗎?
兩種可能

  1. 數據并沒有丟,而是應答丟了,根據確認序號的定義,最左側的下標會向右移動;同時也說明了確認序號也是為了支持滑動窗口的規則而設定的
  2. 數據真的丟了,接收方接收的ACK序號會重復,且是該丟失的報文的確認序號;當連續接收三次時,會觸發重傳機制

滑動窗口必須要滑動嗎?會不會不動?或者變為0?
不一定滑動,可能不動,也可能變為零
只取決于 win_start=ack_seq;win_end=win_start+tcp_win兩者的差值

如果一直向后滑動,空間不夠怎么辦?
發送緩沖區是環狀結構的,所以空間不存在不夠

流量控制

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

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

接收端如何把窗口大小告訴發送端呢?
在三次握手時就已經交換了窗口大小

擁塞控制

雖然TCP有了滑動窗口這個大殺器, 能夠高效可靠的發送大量的數據. 但是如果在剛開始階段就發送大量的數據, 仍然可能引發問題
在之前我們考慮的只是通信雙方,并沒有考慮到兩者之間的網絡

因為網絡上有很多的計算機, 可能當前的網絡狀態就已經比較擁堵. 在不清楚當前網絡狀態下, 貿然發送大量的數據,是很有可能引起雪上加霜的

TCP引入 慢啟動 機制, 先發少量的數據, 探探路, 摸清當前的網絡擁堵狀態, 再決定按照多大的速度傳輸數據

在這里插入圖片描述

  • 此處引入一個概念程為擁塞窗口
  • 發送開始的時候, 定義擁塞窗口大小為1
  • 每次收到一個ACK應答, 擁塞窗口加1
  • 每次發送數據包的時候, 將擁塞窗口和接收端主機反饋的窗口大小做比較, 取較小的值作為實際發送的窗口:滑動窗口=min(擁塞窗口,自己的接收能力)

像上面這樣的擁塞窗口增長速度, 是指數級別的. “慢啟動” 只是指初使時慢, 但是增長速度非常快

  • 為了不增長的那么快, 因此不能使擁塞窗口單純的加倍
  • 此處引入一個叫做慢啟動的閾值
  • 當擁塞窗口超過這個閾值的時候, 不再按照指數方式增長, 而是按照線性方式增長

在這里插入圖片描述

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

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

延遲應答

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

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

一定要記得, 窗口越大, 網絡吞吐量就越大, 傳輸效率就越高. 我們的目標是在保證網絡不擁塞的情況下盡量提高傳輸效率

那么所有的包都可以延遲應答么? 肯定也不是

  • 數量限制: 每隔N個包就應答一次
  • 時間限制: 超過最大延遲時間就應答一次

具體的數量和超時時間, 依操作系統不同也有差異; 一般N取2, 超時時間取200ms;

捎帶應答

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

簡單理解就是在確認應答時也發送數據

面向字節流

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

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

由于緩沖區的存在, TCP程序的讀和寫不需要一一匹配, 例如:

  • 寫100個字節數據時, 可以調用一次write寫100個字節, 也可以調用100次write, 每次寫一個字節
  • 讀100個字節數據時, 也完全不需要考慮寫的時候是怎么寫的, 既可以一次read 100個字節, 也可以一次read一個字節, 重復100次

粘包問題

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

那么如何避免粘包問題呢? 歸根結底就是一句話, 明確兩個包之間的邊界

  • 對于定長的包, 保證每次都按固定大小讀取即可; 例如上面的Request結構, 是固定大小的, 那么就從緩沖區從頭開始按sizeof(Request)依次讀取即可
  • 對于變長的包, 可以在包頭的位置, 約定一個包總長度的字段, 從而就知道了包的結束位置
  • 對于變長的包, 還可以在包和包之間使用明確的分隔符(應用層協議, 是程序猿自己來定的, 只要保證分隔符不和正文沖突即可)

對于UDP協議來說, 是否也存在 “粘包問題” 呢?

  • 對于UDP, 如果還沒有上層交付數據, UDP的報文長度仍然在. 同時, UDP是一個一個把數據交付給應用層. 就有很明確的數據邊界
  • 站在應用層的站在應用層的角度, 使用UDP的時候, 要么收到完整的UDP報文, 要么不收. 不會出現"半個"的情況

TCP異常情況

進程終止: 進程終止會釋放文件描述符, 仍然可以發送FIN. 和正常關閉沒有什么區別
機器重啟: 和進程終止的情況相同

機器掉電/網線斷開: 接收端認為連接還在, 一旦接收端有寫入操作, 接收端發現連接已經不在了, 就會進行reset. 即使沒有寫入操作, TCP自己也內置了一個保活定時器, 會定期詢問對方是否還在. 如果對方不在, 也會把連接釋放

另外, 應用層的某些協議, 也有一些這樣的檢測機制. 例如HTTP長連接中, 也會定期檢測對方的狀態. 例如QQ, 在QQ斷線之后, 也會定期嘗試重新連接

TCP小結

為什么TCP這么復雜? 因為要保證可靠性, 同時又盡可能的提高性能.
可靠性

  • 校驗和
  • 序列號(按序到達)
  • 確認應答
  • 超時重發
  • 連接管理
  • 流量控制
  • 擁塞控制

提高性能

  • 滑動窗口
  • 快速重傳
  • 延遲應答
  • 捎帶應答

TCP/UDP對比

我們說了TCP是可靠連接, 那么是不是TCP一定就優于UDP呢? TCP和UDP之間的優點和缺點, 不能簡單, 絕對的進行比較

  • TCP用于可靠傳輸的情況, 應用于文件傳輸, 重要狀態更新等場景
  • UDP用于對高速傳輸和實時性要求較高的通信領域, 例如, 早期的QQ, 視頻傳輸等. 另外UDP可以用于廣播

歸根結底, TCP和UDP都是程序員的工具, 什么時機用, 具體怎么用, 還是要根據具體的需求場景去判定

TCP 相關實驗

理解 listen 的第二個參數

舉個栗子
在大型商場中,每個飯館外面都有閑置的座椅,每當到飯點時,都會有顧客在外面排隊,仔細觀察會發現,這些座椅并不會很多,因為沒有必要;讓顧客排隊的目的就是在里面有資源空閑時,立刻投入使用,提高入座率;在TCP協議中同樣如此

TCP協議,要為上層維護一個鏈接隊列,當服務器有空閑的資源時,隊列中的客戶端立刻進行連接;隊列的長度受listen的第二個參數影響

對于服務器, listen 的第二個參數設置為 2, 并且不調用 accept
當對服務器進行三次連接之后,查詢結果如下

在這里插入圖片描述

當進行第四次連接時,查詢結果

在這里插入圖片描述

當第四次連接時,客戶端認為自己已經完成握手,但是服務端并沒有完成握手,并沒有最后一次確認,這種情況也稱半連接

在這里插入圖片描述

Linux內核協議棧為一個tcp連接管理使用兩個隊列:

  1. 半鏈接隊列(用來保存處于SYN_SENT和SYN_RECV狀態的請求)
  2. 全連接隊列(accpetd隊列)(用來保存處于established狀態,但是應用層沒有調用accept取走的請求)

全連接隊列的長度會受到 listen 第二個參數的影響;全連接隊列滿了的時候, 就無法繼續讓當前連接的狀態進入 established 狀態
;這個隊列的長度通過上述實驗可知, 是 listen 的第二個參數 + 1;同時也說明了accpet與三次握手沒有關系

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

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

相關文章

修改el-select和el-input樣式;修改element-plus的下拉框el-select樣式

修改el-select樣式 .select_box{// 默認placeholder:deep .el-input__inner::placeholder {font-size: 14px;font-weight: 500;color: #3E534F;}// 默認框狀態樣式更改:deep .el-input__wrapper {height: 42px;background-color: rgba(0,0,0,0)!important;box-shadow: 0 0 0 …

OptaPlanner筆記6 N皇后

N 個皇后 問題描述 將n個皇后放在n大小的棋盤上,沒有兩個皇后可以互相攻擊。 最常見的 n 個皇后謎題是八個皇后謎題,n 8: 約束: 使用 n 列和 n 行的棋盤。在棋盤上放置n個皇后。沒有兩個女王可以互相攻擊。女王可以攻擊同一水…

如何做好一名網絡工程師?具體實踐?

預防問題 – 資格與認證 在安裝線纜或升級網絡時測試線纜是預防問題的有效方式。對已安裝布線進行測試的方法有兩種。 資格測試確定布線是否有資格執行某些操作 — 換言之,支持特定網絡速度或應用。盡管“通過”認證測試也表明按標準支持某一網絡速度或應用的能力…

Redux - Redux在React函數式組件中的基本使用

文章目錄 一,簡介二,安裝三,三大核心概念Store、Action、Reducer3.1 Store3.2 Reducer3.3 Action 四,開始函數式組件中使用4.1,引入store4.1,store.getState()方法4.3,store.dispatch()方法4.4&…

cookie和session的區別及原理

Cookie概念 在瀏覽某些 網站 時,這些網站會把 一些數據存在 客戶端 , 用于使用網站 等跟蹤用戶,實現用戶自定義 功能. 是否設置過期時間: 如果不設置 過期時間,則表示這個 Cookie生命周期為 瀏覽器會話期間 , 只要關閉瀏覽器,cookie就消失了. 這個生命期為瀏覽會話期的cookie,就…

其他行業跳槽轉入計算機領域簡單看法

其他行業跳槽轉入計算機領域簡單看法 本人選擇從以下幾個方向談談自己的想法和觀點。 如何規劃才能實現轉碼 自我評估和目標設定:首先,你需要評估自己的技能和興趣,確定你希望在計算機領域從事的具體職位或領域。這有助于你更好地規劃學習路…

深入了解 Rancher Desktop 設置

Rancher Desktop 設置的全面概述 Rancher Desktop 擁有方便、強大的功能,是最佳的開發者工具之一,也是在本地構建和部署 Kubernetes 的最快捷方式。 本文將介紹 Rancher Desktop 的功能和特性,以及 Rancher Desktop 作為容器管理平臺和本地…

人工智能原理(2)

目錄 一、知識與知識表示 1、知識 2、知識表示 3、知識表示方法 二、謂詞邏輯表示法 1、命題邏輯 2、謂詞邏輯 三、產生式表達法 1、知識的表示方法 2、產生式系統組成 3、推理方式 4、產生式表示法特點 四、語義網絡 1、概念及結構 2、語義網絡的基本語義聯系 …

zookeeper案例

目錄 案例一:服務器動態上下線 服務端: (1)先獲取zookeeper連接 (2)注冊服務器到zookeeper集群: (3)業務邏輯(睡眠): 服務端代碼…

Java+Excel+POI+testNG基于數據驅動做一個簡單的接口測試【杭州多測師_王sir】

一、創建一個apicases.xlsx放入到eclipse的resource里面&#xff0c;然后refresh刷新一下 二、在pom.xml文件中加入poi和testng的mvn repository、然后在eclipse的對應目錄下放入features和plugins&#xff0c;重啟eclipse就可以看到testNG了 <!--poi excel解析 --><d…

運維監控學習筆記3

DELL的IPMI頁面的登錄&#xff1a; 風扇的狀態&#xff1a; 電源溫度&#xff1a;超過70度就告警&#xff1a; 日志信息&#xff1a; 可以看到更換過磁盤。 iDRAC的設置 虛擬控制臺&#xff1a;啟動遠程控制臺&#xff1a; 可以進行遠程控制。 機房工程師幫我們接遠程控制&…

【云原生】kubernetes中容器的資源限制

目錄 1 metrics-server 2 指定內存請求和限制 3 指定 CPU 請求和限制 資源限制 在k8s中對于容器資源限制主要分為以下兩類: 內存資源限制: 內存請求&#xff08;request&#xff09;和內存限制&#xff08;limit&#xff09;分配給一個容器。 我們保障容器擁有它請求數量的…

【云原生】K8S集群

目錄 一、調度約束1.1 POT的創建過程1.1調度過程 二、指定節點調度2.1 通過標簽選擇節點 三、親和性3.1requiredDuringSchedulingIgnoredDuringExecution&#xff1a;硬策略3.1 preferredDuringSchedulingIgnoredDuringExecution&#xff1a;軟策略3.3Pod親和性與反親和性3.4使…

(2)原神角色數據分析-2

功能一&#xff1a; 得到某個屬性的全部角色&#xff0c;將其封裝在class中 """各元素角色信息&#xff1a;一對多""" from pandas import DataFrame, Series import pandas as pd import numpy as npclass FindType:# 自動執行&#xff0c;將…

山東布谷科技直播平臺搭建游戲開發技術分享:數據存儲的重要意義

在市場上的熱門的直播平臺中&#xff0c;有很多小程序為用戶提供各種各樣的功能&#xff0c;這其中就有很多游戲小程序&#xff0c;當今社會獨生子女眾多&#xff0c;很多作為獨生子女的用戶都會去選擇一個能夠社交互動的APP來填補內心的空虛&#xff0c;而直播平臺的實時互動的…

SAP 選擇屏幕組件名描述翻譯時字符長度不夠問題處理

問題&#xff1a;有時候我們在開發report程序的時候&#xff0c;要求程序顯示支持中英文&#xff0c;如果程序是在中文環境下開發的時候&#xff0c;需要進行翻譯處理&#xff0c;但是我們發現選擇屏幕上的組件的描述支持的默認長度是30位&#xff0c;如果超過該如何處理呢 解…

《路由與交換技術》讀書筆記

小小感悟 工作近3年&#xff0c;基本沒去看路由交換相關書籍&#xff0c;趁著搬家后&#xff0c;周末閑暇時間&#xff0c;快速看了一遍《路由與交換技術》&#xff0c;溫習了一遍&#xff0c;很有收獲&#xff0c;以后還是要多花時間看看其他類型的書。 讀書筆記 1.1 移動通…

構建一個LLM應用所需的所有信息

一、說明 您是否對大型語言模型&#xff08;LLM&#xff09;的潛力感興趣&#xff0c;并渴望創建您的第一個基于LLM的應用程序&#xff1f;或者&#xff0c;也許您是一位經驗豐富的開發人員&#xff0c;希望簡化工作流程&#xff1f;看看DemoGPT就是您的最佳選擇。該工具旨在簡…

【軟件測試】Linux環境下Docker搭建+Docker搭建MySQL服務(詳細)

目錄&#xff1a;導讀 前言 一、Python編程入門到精通二、接口自動化項目實戰三、Web自動化項目實戰四、App自動化項目實戰五、一線大廠簡歷六、測試開發DevOps體系七、常用自動化測試工具八、JMeter性能測試九、總結&#xff08;尾部小驚喜&#xff09; 前言 Linux之docker搭…

CDN(內容分發網絡)

CDN的全稱是 Content Delivery Network, 即內容分發網絡。CDN是構建在現有網絡基礎之上的智能虛擬網絡&#xff0c;依靠部署在各地的邊緣服務器&#xff0c;通過中心平臺的負載均衡、內容分發、調度等功能模塊&#xff0c;使用戶就近獲取所需內容&#xff0c;降低網絡擁塞&a…