文章目錄
- 1.引入
- 2.協議段格式
- 4位首部長度
- 16位窗口大小
- 32位序號
- 思考三個問題【demo】
- 標記位
- URG: 緊急指針是否有效==提升某報文被處理優先級【0表示不設置1表示設置】
- ACK: 確認號是否有效
- PSH: 提示接收端應用程序立刻從TCP緩沖區把數據讀走
- RST: 對方要求重新建立連接; 我們把攜帶RST標識的稱為復位報文段
- SYN: 請求建立連接; 我們把攜帶SYN標識的稱為同步報文段
- FIN: 通知對方, 本端要關閉了, 我們稱攜帶FIN標識的為結束報文段
- 總結標記位
- 總結報頭屬性
1.引入
之前講的網絡版計算機/http協議 都是基于tcp協議 哪里體現出來?我們用了tcp套接字編程及三次握手等。
TCP協議
TCP全稱為 “傳輸控制協議(Transmission Control Protocol”). 人如其名, 要對數據的傳輸進行一個詳細的控制;
用戶級緩沖區
自己定義的string/char buffer
write/read/recv/send --》不是發送函數 而是拷貝函數
這些接口本質不是把數據發送到網絡中 而是把數據從用戶級緩沖區拷貝到發送緩沖區里。發送緩沖區里的數據什么時候發送,發送多少,出錯了怎么辦 — 由OS的TCP協議自主決定
似曾相識?
講文件時,我們提到 把自己的charBuf的數據調用write接口”寫“到文件里 實際上write是把用戶級緩沖區的數據拷貝到文件緩沖區(每個文件都有自己的文件緩沖區)刷新策略由OS控制!把磁盤換成網卡,是不是就理解了?!文件讀寫屬于IO,網絡也一樣!在用戶層做:報頭和有效載荷分離,序列反序列,把這些數據調用write接口拷貝到發送緩沖區,之后就由OS來管理了!
read讀文件/接收緩沖區 阻塞
接收緩沖區沒有數據 阻塞等待 將read進程掛起 當接收緩沖區有數據(OS內某些條件就緒了) 再調度。
一個文件可以同時讀寫?
一個文件描述符配備兩個緩沖區(發送/接收)
發送/接收緩沖區實際上是內存空間
OS為了管理內存 把他們劃分為4kb內存塊 哪些內存塊被占用/被系統鎖住(不能換入換出)/ 需要刷新?管理!–每一個內存塊都有一個struct page{};這里的發送接收緩沖區實際上是很多個struct page管理的內存塊。對內存的管理變成對結構體數組的增刪查改!打開網絡–打開一個文件–文件描述符–struct file{}–本應指向磁盤 現在讓他指向網絡設備 再加上兩個緩沖區–網絡IO!
傳輸控制協議的控制體現在哪里?
數據從用戶級緩沖區到發送緩沖區后 什么時候發到網絡 發多少 出錯怎么辦 由TCP協議自主決定 – 控制。udp沒有發送緩沖區 自然無法做到控制!udp一直發 接收端收不下的多于內容就丟棄了 但是tcp有”控制“,它可以讓接收端盡快清空接收緩沖以便于接收新數據。
2.協議段格式
源/目的端口號: 表示數據是從哪個進程來, 到哪個進程去;
32位序號/32位確認號: 后面詳細講;
4位TCP報頭長度: 表示該TCP頭部有多少個32位bit(有多少個4字節); 所以TCP頭部最大長度是15 * 4 = 60
16位校驗和: 發送端填充, CRC校驗. 接收端校驗不通過, 則認為數據有問題. 此處的檢驗和不光包含TCP首部, 也包含TCP數據部分.
16位緊急指針: 標識哪部分數據是緊急數據
不同的名字
4位首部長度
計算時有基本單位 4B
[0000,1111] --> [0,15] —> [0,60]
如果沒有選項 只有標準報頭 那么 4位首部長度應該存5
報頭和有效載荷如何分離?
固定長度(標準報頭長度20B)+ 自定義字段(4位首部長度x)
收到一個報文 截取20B獲取標準報頭header 提取header的x x-20即為選項長度 再截取x-20即為選項 剩下的報文即為有效載荷!
16位窗口大小
前面學了應用層 現在在學傳輸層 對于整個協議棧 只考慮應用層和傳輸層 我們要有以下的認識
- 客戶端在應用層構建了一個http請求 交付給傳輸層 傳輸層會加一個tcp報頭。
- 服務端在傳輸層接收到會分離報頭和有效載荷。
- client和server基于tcp協議進行通信互發消息的時候,發送的是完整的tcp報文,即,一定攜帶完整的tcp報頭! 理解:http請求與響應,發送數據等都是一個完整的報文,即,只要想通過網絡通信那么就要封裝和分離報頭。
- 一個tcp鏈接在應用層看 實際上就是一個fd配著兩個緩沖區(發送緩沖區/接收緩沖區)。
講可靠性 先了解什么是不可靠
數據重復/亂序/丟包
丟包:接收端的接收緩沖區已經滿了 發送方并不知道還在發 丟包!怎么解決?當接收緩沖區已經滿,想辦法讓發送方知道并發慢一點。—流量控制。
怎么做到流量控制?
tcp可靠性中有:丟包重傳/超時重傳。那么不做流量控制,發送方就一直發,接收方沒收到發送方重傳,這樣可以嗎?可以!但是不合理!這樣會消耗資源!一個正常的數據發過來由于接收方緩沖區滿而讓發送方重傳,不合理!消耗資源!降低效率!這種情況,重傳機制并不合理!比較好的做法是讓發送方發慢一點!
TCP憑什么保證可靠性?
一個基本特點:應答機制!接收方收到數據就會應答,表示自己收到了!上面已經提到 此處說的發送消息或者確認應答 只要經過網絡 那么他就要帶著報文!
發送方發送速度的依據?
由接收方接收緩沖區的剩余空間大小決定!發送方怎么得知?接收方的應答是一個帶著tcp報頭的報文,報頭中【16位窗口大小】的字段就是【接收方接收緩沖區的剩余空間大小】。
【16位窗口大小】是發送方自己的接收緩沖區剩余空間大小!
CS雙方通過攜帶這一字段 告知對方你給我發數據時要根據這個悠著點來!
32位序號
世界上不存在百分百可靠的網絡協議!
- 發送方發了一條信息 收到了應答 可以確定:我最近發送的信息對方收到了!
- 沒有收到應答 不能保證對方收到了!永遠存在一條最新的消息 是沒有應答的!
局部上存不存在百分百可靠的網絡協議?
最新一條信息之前的信息都能保證是可靠的!除了最新的消息 之前的每一條信息都有應答!實際上,沒有必要對應答做應答 — 死循環!
tcp最基本最原始的通信過程
發送方發了一條信息 接收方應答 就可以保證發送方發的消息被接收方成功收到了!發送方有必要對應答做應答嗎?沒必要!發送方不必告知接收方我收到你的應答了!發送方需要應答的目的是需要知道接收方到底收到沒有,接收方不需要應答是反正我已經收到了,我不需要關心發送方知不知道我收到了!即只要保證發送方發的消息接收方收到了就行!
保證這兩個方向上的可靠,即,我發送后知道你有沒有收到,你發送后知道我有沒有收到。
關于應答
發送方發送數據后 如果一段時間沒有收到應答 認為接收方沒有收到數據 重傳!
接收方發的應答丟失了怎么辦?同上!這也引來一個問題:發送方發送的數據發送給網絡后還要在發送緩沖區里待一段時間,如果沒有收到應答還得重傳!如果收到應答,如果這段數據之后不用了就可以移出發送緩沖區!
優化 – 捎帶應答【應答+要發的消息】
發送方一次發很多消息
發一條需要接收到應答才發下一條 … 這樣是十分低效的!一次發一批!
發10條收10條應答 ---- 更真實的tcp!
tcp發的時候按順序發 收的時候呢?
先發的不一定先收(經過網絡受等多方面影響)。---- 數據包亂序! – 亂序是不可靠的一種場景!數據以亂序發給接收方這個是不可控的!怎么辦?
32位序號的應用場景
發送方發送數據時 對各個報文編號 【32位序號】一個作用就是保證數據按序到達!即接收方收到后排序接收!
序號是什么?
發送方發送的數據:從用戶層拷貝到發送緩沖區,然后OS把發送緩沖區里的完整數據一個個發給網絡。發送緩沖區:(實際上是一個char 字符數組buf – 面向字節流,由于是數組,每個字節天然都有一個編號即數組下標),每一塊數據在buf里占連續的空間,每個數據塊的最后一個字符的下標就是【序號】!
一批發送一批響應 , 響應是對哪一個數據的響應?
【確認序號】!== 收到的報文序號 + 1 。意義表示:確認序號之前的數據我已經全部收到了!下次發送請從確認序號指定的數字開始發送
1001,2001發送時丟失了,但是發送方收到了3001,說明接收方在3001之前的數據都接受到了! – 允許存在應答丟失情況!、
如果接收方壓根沒有收到2000,但是收到了1000,3000,然后應答1001,3001,發送方收到3001是不是仍然認為接收方接收到3001前的數據,這種場景不是錯的嗎?
這種情況,接收方收到1000,3000,沒收到2000,應答時不會應答3001,只會應答1001【后面還會細講此場景】
tcp協議段中有了32位序號后為什么還要有32位確認序號
- 接收方應答時可能同時發送數據那么就需要指明自己發送數據的序號。
- 發送方在發送數據的同時可能也在接收數據!雙方地位對等。
思考三個問題【demo】
- 之前模擬瀏覽器服務端 我們只在應用層編碼 至于數據怎么傳的 底層實際上做了很多工作 在應用層我們不學 之后我們慢慢學
- 16位窗口大小:發送方自己的接收緩沖區剩余空間大小。但是歷史上第一次發送方向接收方發信息,他怎么知道接收方的窗口大小?即他怎么知道他要發多少數據才合適?
- 多批量傳輸時 報文和應答丟失的情況。
標記位
6個標記位
建立鏈接三次握手 開始進行數據通信 斷開連接四次揮手 。在tcp通信時,這些行為都會發生tcp報文,不同的行為引發不同的動作,意味著tcp收到的報文是有各種”類型“的,不同的類型決定了接收方要做不同的動作。接收方如何得知報頭的類型?通過6個標記位!標記位的意義:區分tcp報文的類型!
簡述
您已經列舉了TCP協議中的六個重要標志位(flags),每個標志位都在TCP通信中扮演著不同的角色。以下是這些標志位的簡要介紹:
URG (Urgent):
作用:表示TCP報文段的緊急指針(Urgent Pointer)是否有效。
解釋:當URG標志位被設置時,TCP的緊急指針會指出緊急數據在報文段中的位置。這允許發送方標記一些數據為“緊急”,這樣接收方就可以優先處理這些數據,而不是按照正常的順序來處理。
ACK (Acknowledgment):
作用:表示確認號(Acknowledgment Number)是否有效。
解釋:ACK標志位用于確認接收到的數據。當TCP發送一個報文段時,它會期望對方返回一個包含ACK標志的報文段,以確認數據的接收。ACK確認號表示接收方下一個期望接收的字節的序列號。
PSH (Push):
作用:提示接收端的應用程序立刻從TCP緩沖區把數據讀走。
解釋:PSH標志位用于通知接收端的應用程序盡快從TCP的接收緩沖區中讀取數據。在某些情況下,即使接收緩沖區中的數據量未達到應用程序的讀取閾值,PSH標志也可以確保數據被立即讀取。
RST (Reset):
作用:對方要求重新建立連接;我們把攜帶RST標識的稱為復位報文段。
解釋:RST標志位用于異常關閉連接。當TCP檢測到嚴重錯誤(如主機崩潰)或者收到一個非法的報文段時,它可能會發送一個RST報文段來關閉連接。RST報文段不攜帶數據,并且會釋放所有關聯的資源。
SYN (Synchronize):
作用:請求建立連接;我們把攜帶SYN標識的稱為同步報文段。
解釋:SYN標志位用于建立一個新的連接。在三次握手過程中,第一個報文段(SYN報文段)會包含SYN標志位,并設置初始的序列號。接收方會回復一個SYN-ACK報文段(包含SYN和ACK標志位),表示它同意建立連接,并確認收到的序列號。
FIN (Finish):
作用:通知對方,本端要關閉了;我們稱攜帶FIN標識的為結束報文段。
解釋:FIN標志位用于正常關閉連接。當一方決定關閉連接時,它會發送一個FIN報文段給另一方。接收方會回復一個ACK報文段來確認這個FIN報文段,并在完成所有數據的發送后,發送一個自己的FIN報文段來關閉連接。
這些標志位共同構成了TCP協議的核心部分,確保了數據在網絡中的可靠傳輸。
總結
ACK/SYN/FIN:以非數據傳送為目的 有一些控制傳輸的含義在其中。OS不會直接暴露這些標記位給用戶 但是或多或少會給用戶提供接口讓用戶設置 如connect–》syn;close–》fin:給對方發一個含有fin標記位的報文
詳述
URG: 緊急指針是否有效==提升某報文被處理優先級【0表示不設置1表示設置】
一般情況,tcp數據報文(一個大數據分成很多小數據發)按123456順序發,接收時不一定是123456,但是收到后他要根據32位序號排序成123456。在tcp協議下,某些報文是不允許插隊的,即接收方只能按照特定順序依次處理報文。但是如果接收方收到了帶有urg的報文,TCP協議段的16位緊急指針會指出緊急數據在報文段中的位置,接收方收到后會優先處理該數據包。
緊急指針:報文中有效載荷中的緊急數據的偏移量。
tcp中一個報文中一般只允許出現一個字節的緊急數據。
如何使用urg?
在支持帶外數據的套接字上發送帶外數據(例如SOCKSTREAM類型);底層協議也必須支持帶外數據。
此標志請求接收正常數據流中不會接收的帶外數據。一些協議將加急數據放在正常數據隊列的頭部,因此這個標志不能用于這樣的協議。
什么場景用?
c向s發起服務請求,s響應很慢或者幾乎不響應,但是s并沒壞,他可能因為某種計算很復雜而一直計算,此時c并不知道持續向s發送請求,但是沒有響應,于是c向s發送了帶外數據(緊急數據)詢問s你到底在干嘛?s中有例程專門處理緊急數據,相應一個帶有服務器狀態的報文給c讓c知道s在干嘛。(s要支持讀取緊急數據,且s在軟件設計上設計了某一狀態用某一種編號代替)
ACK: 確認號是否有效
三次握手成功后 幾乎所有的應答報文的ack都是置為1的,表示我是一個應答報文,至于是不是捎帶應答,看看是否存在有效載荷。
PSH: 提示接收端應用程序立刻從TCP緩沖區把數據讀走
情景:
數據從網絡里發到接收方 OS把網絡數據存至接收緩沖區 用戶層決定讀不讀接收緩沖區里的內容。如果用戶一直不讀 接收緩沖區里的空間就會越來越少。OS把網絡數據放到接收緩沖區,用戶從接收緩沖區讀==》PCMode!流量控制–》發送過程【發送者的發送緩沖區到接收者的接收緩沖區】的同步!接收方接收緩沖區的數據一直不被取走,發送方發送緩沖區的數據有一天會被拷貝滿,這時在發送方,用戶層就不能再向發送緩沖區寫數據—》寫阻塞!發送方阻塞等待!等到什么時候?有沒有判定依據?1. 定期詢問對方2. 當接收方接收緩沖區有空間了,給發送方發通知。 接收方一直不讀呢?發送方就會發送一個帶有psh標記位的報文!接收方就會得知發送方已經著急了!提示接收端應用程序立刻從TCP緩沖區把數據讀走!!同樣,一個合格的服務器也應該及時的讀取數據!宏觀上提升網絡通信效率!也有可能,一個捎帶psh即帶有數據+psh標記位的報文!讓服務器盡快接收數據。
RST: 對方要求重新建立連接; 我們把攜帶RST標識的稱為復位報文段
tcp協議保證可靠性:正常通信下 tcp提供對于數據的很多種可靠策略 但是也有一些情況無法避免:客戶端不想通信了 也不揮手 直接拔了網線。所以tcp的可靠性的前提是不會出現一些不可抗拒的異常問題! 即tcp不能保證三次握手每次都成功!即便三次握手不成功但是三次握手每一步都做了 tcp也會開始通信。tcp允許鏈接建立失敗!服務器內部可能存在多個建立好的鏈接(他在跟多個客戶端通信)。服務端存在很多鏈接,管理–》先描述再組織!鏈接—結構體;屬性—成員變量;很多鏈接—鏈表;
維護鏈接有成本
創建內核數據結構對象 初始化 填充字段==》空間時間
鏈接太多 管理不過來 異常!
客戶端認為鏈接建立好了 服務端認為沒建立好 服務端發送帶有rst標記位的報文要求重新建立連接;
畫圖線是斜的:數據經過網絡—有延遲!
問題:首先要知道,三次握手重在三次,至于是否建立好了連接不知道。
服務端接收回應接收三次后認為三次握手已完成。而客戶端最后一次發送并沒有響應他會認為只要他第三次發出去了就握手了–就建立鏈接了!不考慮重傳,正常情況下,客戶端不知道服務端是否收到!tcp在賭第三次的ack能被服務端收到!既然第三次ack一經發出,客戶端就認為建立好了連接,那么客戶端此時就會創建連接的結構體對象并初始化填充屬性準備通信。但是ack經過網絡發到服務端需要時間,在這個時間間隔內,CS對于是否建立好連接認知不一致!此時客戶端認為建立好連接了,就會開始給服務端發送數據。如果服務端沒收到這個ack卻突然收到了一個數據,服務端就會意識到最后一個ack丟失了,對方雖然建立好連接了但是我沒有,于是服務端就給客戶端發送一個帶有rst標記位的報文!
rst標記位只有這一種場景嗎?
三次握手雙方都建立成功了即第三次ack成功發送,但是服務端斷網重啟了,客戶端不知道,發送信息時服務端就會回應一個帶有rst的報文。即rst應用于建立連接時正常通信中,都會出現協議/網絡問題導致要使用rst
第三種場景
服務器滿載了,即建立連接數已最大,客戶端就會給服務端發送帶有rst的報文請求重新建立連接。
SYN: 請求建立連接; 我們把攜帶SYN標識的稱為同步報文段
FIN: 通知對方, 本端要關閉了, 我們稱攜帶FIN標識的為結束報文段
總結標記位
tcp報文:有的用來進行數據通信 有的用來進行特殊功能–連接管理等 或者二者皆有之。
總結報頭屬性
tcp是可靠的協議 故協議中有很多屬性就提高了可靠性,有的屬性為了提升傳輸效率。有很多為可靠性的設計的策略在tcp中也是體現不出來的比如流量控制,重傳。下一篇講。