Linux之UDP和TCP報(bào)頭管理方式
關(guān)于UDP和TCP 我們就要重點(diǎn)聊一聊傳輸層(負(fù)責(zé)數(shù)據(jù)能夠從發(fā)送端傳輸接收端.)
一、關(guān)于端口號(hào)
1.1 端口號(hào)的理解
端口號(hào)(Port)標(biāo)識(shí)了一個(gè)主機(jī)上進(jìn)行通信的不同的應(yīng)用程序;
每一個(gè)應(yīng)用層服務(wù)(進(jìn)程)都綁定著自己的協(xié)議,而具體這個(gè)數(shù)據(jù)要傳輸給哪一個(gè)應(yīng)用程序,是要根據(jù)具體的端口號(hào)來決定的,而交給哪個(gè)服務(wù)的本質(zhì)也是交給哪個(gè)進(jìn)程又因?yàn)檫M(jìn)程bind了自己的端口號(hào)所以O(shè)S究竟會(huì)把數(shù)據(jù)交給誰,其實(shí)是根據(jù)端口號(hào)來確認(rèn)的!
在TCP/IP協(xié)議中, 用 "源IP", "源端口號(hào)", "目的IP", "目的端口號(hào)", "協(xié)議號(hào)" 這樣一個(gè)五元組來標(biāo)識(shí)一個(gè)通信(可以通過netstat -n查看);
1.2 端口號(hào)范圍的劃分
0 - 1023: 知名端口號(hào), HTTP, FTP, SSH等這些廣為使用的應(yīng)用層協(xié)議, 他們的端口號(hào)都是固定的.
1024 - 65535: 操作系統(tǒng)動(dòng)態(tài)分配的端口號(hào). 客戶端程序的端口號(hào), 就是由操作系統(tǒng)從這個(gè)范圍分配的.
1.3 認(rèn)識(shí)知名端口號(hào)
有些服務(wù)器是非常常用的, 為了使用方便, 人們約定一些常用的服務(wù)器, 都是用以下這些固定的端口號(hào):
- 1、ssh服務(wù)器, 使用22端口
- 2、ftp服務(wù)器, 使用21端口
- 3、telnet服務(wù)器, 使用23端口
- 4、http服務(wù)器, 使用80端口
- 5、https服務(wù)器, 使用443
執(zhí)行下面的命令, 可以看到知名端口號(hào)
cat /etc/services
我們自己寫一個(gè)程序使用端口號(hào)時(shí), 要避開這些知名端口號(hào).
1.4 一個(gè)進(jìn)程可以bind多個(gè)端口號(hào)
一個(gè)端口號(hào)不能被多個(gè)進(jìn)程bind,但是一個(gè)進(jìn)程可以被多個(gè)端口號(hào)bind的(一個(gè)進(jìn)程可以有多個(gè)文件描述符,而每一個(gè)文件描述符都可以對(duì)應(yīng)一個(gè)端口號(hào),)!!
問題:什么情況下需要一個(gè)進(jìn)程綁定多個(gè)端口號(hào)呢??
---->比如HTTP和HTTPS,同一個(gè)Web服務(wù)器可能需要監(jiān)聽80和443端口,分別處理HTTP和HTTPS的請(qǐng)求(每次調(diào)用bind都會(huì)為該進(jìn)程創(chuàng)建一個(gè)獨(dú)立的socket,這些socket是由內(nèi)核管理的,他們之間是相互獨(dú)立互不影響的,所以bind多個(gè)套接字就對(duì)應(yīng)監(jiān)聽多個(gè)套接字?。。?/span>
1.5 相關(guān)命令
1、netstat是一個(gè)用來查看網(wǎng)絡(luò)狀態(tài)的重要工具.
- 語法:netstat [選項(xiàng)]
- 功能:查看網(wǎng)絡(luò)狀態(tài)
常用選項(xiàng):
- n 拒絕顯示別名,能顯示數(shù)字的全部轉(zhuǎn)化成數(shù)字
- l 僅列出有在 Listen (監(jiān)聽) 的服務(wù)狀態(tài)
- p 顯示建立相關(guān)鏈接的程序名
- t (tcp)僅顯示tcp相關(guān)選項(xiàng)
- u (udp)僅顯示udp相關(guān)選項(xiàng)
- a (all)顯示所有選項(xiàng),默認(rèn)不顯示LISTEN相關(guān)
其中unix是域間套接字,用來進(jìn)行本地通信(懂了網(wǎng)絡(luò)套接字之后學(xué)習(xí)成本就不大了?。?/span>
2、pidof在查看服務(wù)器的進(jìn)程id時(shí)非常方便.
語法:pidof [進(jìn)程名]
功能:通過進(jìn)程名, 查看進(jìn)程id
二、UDP報(bào)頭
2.1 研究協(xié)議的一些具體問題
1、報(bào)頭和有效載荷是如何進(jìn)行分離的??
2、有效載荷應(yīng)該交付給上層的那一個(gè)協(xié)議呢?(協(xié)議字段、方案)
3、認(rèn)識(shí)報(bào)頭的組成
4、學(xué)習(xí)協(xié)議的周邊知識(shí)
2.2 UDP協(xié)議端格式
問題1:報(bào)頭和有效載荷是如何分離的呢??
------>UDP采用的是定長(zhǎng)分離(8字節(jié)),通過讀取前8個(gè)字節(jié),可以讀到UDP的長(zhǎng)度(16位UDP長(zhǎng)度, 表示整個(gè)數(shù)據(jù)報(bào)(UDP首部+UDP數(shù)據(jù))的最大長(zhǎng)度;),然后數(shù)據(jù)的大小就是UDP長(zhǎng)度-8,就實(shí)現(xiàn)了報(bào)頭和有效載荷的分離——>固定長(zhǎng)度+自描述字段
問題2:有效載荷又是如何交付給上層的??
——>分離之后通過報(bào)頭信息的源端口號(hào)和目的端口號(hào),確認(rèn)應(yīng)該交付給那個(gè)進(jìn)程(一般來說已經(jīng)bind相關(guān)端口號(hào)了)
問題3:UDP可以保證可靠性么??
——> 雖然不保證可靠性,但是至少要是對(duì)的,其中UDP校驗(yàn)和就是一種檢驗(yàn)UDP數(shù)據(jù)包是否有誤的校驗(yàn)機(jī)制,他通過了某種算法將UDP數(shù)據(jù)包中的所有數(shù)據(jù)進(jìn)行計(jì)算然后存儲(chǔ)在報(bào)頭字段中,以便確保接收方在收到數(shù)據(jù)包后進(jìn)行校驗(yàn)。如果校驗(yàn)失敗的話,就直接把這個(gè)數(shù)據(jù)包丟棄!!
問題4:UDP有什么應(yīng)用場(chǎng)景?
——>比如我們的視頻傳輸就是UDP協(xié)議,其實(shí)就是無數(shù)張相片的組成傳輸過來,而你切換清晰度無非就跟圖片的一些像素信息有關(guān),像素越高越清晰一般就會(huì)越大,可能就會(huì)導(dǎo)致udp的傳送速度變慢,有時(shí)候你電視劇看著看著出現(xiàn)緩沖了,你可能就會(huì)把畫質(zhì)調(diào)低一點(diǎn)這樣傳輸就會(huì)快一點(diǎn),甚至有些時(shí)候?qū)嵲诰W(wǎng)絡(luò)太擁塞了可能會(huì)出現(xiàn)丟包的情況,那么視頻就會(huì)出現(xiàn)異常,但是UDP對(duì)這種現(xiàn)象是不負(fù)責(zé)任的??!因?yàn)樗肷砭筒槐WC可靠性。。而如果你想保證可靠性那你就應(yīng)該用TCP協(xié)議,所以研究UDP的應(yīng)用場(chǎng)景,其實(shí)就是基于上層不同協(xié)議對(duì)應(yīng)的應(yīng)用場(chǎng)景是否需要選擇UDP!
問題5:如果UDP數(shù)據(jù)包太大怎么辦??
——>我們注意到, UDP協(xié)議首部中有一個(gè)16位的最大長(zhǎng)度. 也就是說一個(gè)UDP能傳輸?shù)臄?shù)據(jù)最大長(zhǎng)度是64K(包含UDP首部).然而64K在當(dāng)今的互聯(lián)網(wǎng)環(huán)境下, 是一個(gè)非常小的數(shù)字.如果我們需要傳輸?shù)臄?shù)據(jù)超過64k,就需要在應(yīng)用層手動(dòng)的分包, 多次發(fā)送并在接收端手動(dòng)拼裝(面向數(shù)據(jù)報(bào))
2.3 UDP的特點(diǎn)
UDP傳輸?shù)倪^程類似于寄信、寄快遞。
- 1、無連接: 知道對(duì)端的IP和端口號(hào)就直接進(jìn)行傳輸, 不需要建立連接;
- 2、不可靠: 沒有確認(rèn)機(jī)制, 沒有重傳機(jī)制; 如果因?yàn)榫W(wǎng)絡(luò)故障該段無法發(fā)到對(duì)方, UDP協(xié)議層也不會(huì)給應(yīng)用層 返回任何錯(cuò)誤信息;
- 3、面向數(shù)據(jù)報(bào): 不能夠靈活的控制讀寫數(shù)據(jù)的次數(shù)和數(shù)量;
應(yīng)用層交給UDP多長(zhǎng)的報(bào)文, UDP原樣發(fā)送, 既不會(huì)拆分, 也不會(huì)合并,要么就收一個(gè),要么就不收,沒有半個(gè)或者一個(gè)半的情況??!
舉例:用UDP傳輸100字節(jié)的數(shù)據(jù),如果如果發(fā)送端調(diào)用一次sendto, 發(fā)送100個(gè)字節(jié), 那么接收端也必須調(diào)用對(duì)應(yīng)的一次recvfrom, 接收100個(gè) 字節(jié); 而不能循環(huán)調(diào)用10次recvfrom, 每次接收10個(gè)字節(jié);
2.4 UDP的緩沖區(qū)
1、UDP沒有真正意義上的發(fā)送緩沖區(qū)(因?yàn)樗恍枰。。? 調(diào)用sendto會(huì)直接交給內(nèi)核, 由內(nèi)核將數(shù)據(jù)傳給網(wǎng)絡(luò)層協(xié)議進(jìn)行后 續(xù)的傳輸動(dòng)作;
2、UDP具有接收緩沖區(qū). 但是這個(gè)接收緩沖區(qū)不能保證收到的UDP報(bào)的順序和發(fā)送UDP報(bào)的順序一致; 如果緩沖區(qū)滿了, 再到達(dá)的UDP數(shù)據(jù)就會(huì)被丟棄;
3、UDP的socket既能讀也能寫,也是全雙工
2.5 基于UDP的應(yīng)用層協(xié)議
- NFS: 網(wǎng)絡(luò)文件系統(tǒng)
- TFTP: 簡(jiǎn)單文件傳輸協(xié)議
- DHCP: 動(dòng)態(tài)主機(jī)配置協(xié)議
- BOOTP: 啟動(dòng)協(xié)議(用于無盤設(shè)備啟動(dòng))
- DNS: 域名解析協(xié)議
- 當(dāng)然, 也包括你自己寫UDP程序時(shí)自定義的應(yīng)用層協(xié)議;
2.6 UDP的結(jié)構(gòu)
因?yàn)長(zhǎng)inux系統(tǒng)是用C語言寫的,所以UDP報(bào)頭的結(jié)構(gòu)其實(shí)涉及到結(jié)構(gòu)體的位段。
填上報(bào)頭數(shù)據(jù),然后帶上有效載荷形成UDP報(bào)文,我們就可以發(fā)送了??!
UDP并不需要有發(fā)送緩沖區(qū),是因?yàn)榘l(fā)送方準(zhǔn)備好了就可以直接發(fā)了,但是他需要有接收緩沖區(qū),因?yàn)樗褦?shù)據(jù)發(fā)給對(duì)方之后可能對(duì)方并不能立即去處理這個(gè)數(shù)據(jù),所以在對(duì)方的接收緩沖區(qū)里可能會(huì)存在多個(gè)UDP報(bào)文,那么也就必然要求OS必須將多個(gè)UDP報(bào)文給管理起來?。∷孕枰让枋鲈俳M織!!
sk_buff就是服務(wù)端管理UDP報(bào)文的緩沖區(qū) 其中start指向緩沖區(qū)的頭部(報(bào)文的頭部)、end指向緩沖區(qū)的尾部、pos指向有效數(shù)據(jù)的尾部。然后再用鏈表形式去鏈接管理起來。此時(shí)我們OS將對(duì)UDP報(bào)文的管理轉(zhuǎn)化成了對(duì)sk_buff結(jié)構(gòu)體的管理
所以新的UDP數(shù)據(jù)就會(huì)連接在這個(gè)鏈表上,而UDP數(shù)據(jù)的丟棄就是把他對(duì)應(yīng)的結(jié)構(gòu)體給釋放了
三、TCP報(bào)頭
3.1 傳輸控制協(xié)議的理解
TCP全稱為 "傳輸控制協(xié)議(Transmission Control Protocol"). 人如其名, 要對(duì)數(shù)據(jù)的傳輸進(jìn)行一個(gè)詳細(xì)的控制;
我們平時(shí)調(diào)用的write、read、recv、send其實(shí)本質(zhì)上都是拷貝函數(shù),要么將用戶緩沖區(qū)的內(nèi)容拷貝到發(fā)送緩沖區(qū),要么將接受緩沖區(qū)的內(nèi)容拷貝到用戶緩沖區(qū)進(jìn)行處理,本質(zhì)來說就是不斷把數(shù)據(jù)放到網(wǎng)絡(luò)中,而具體數(shù)據(jù)要什么時(shí)候發(fā)送,發(fā)送多少,出錯(cuò)了怎么辦,是由TCP協(xié)議自主決定的?。?!!
其實(shí)通過TCP協(xié)議控制網(wǎng)絡(luò)傳輸可靠性將數(shù)據(jù)從一臺(tái)主機(jī)的發(fā)送緩沖區(qū)安全地交付到另一臺(tái)主機(jī)的接收緩沖區(qū) ,要求數(shù)據(jù)原封不動(dòng)的發(fā)送過去,所謂發(fā)送,本質(zhì)上是一種也是一種跨網(wǎng)絡(luò)的拷貝?。?/strong>
當(dāng)然,對(duì)方也可以對(duì)我們發(fā)送,因?yàn)門CP是全雙工的,有自己的發(fā)送緩沖區(qū)和接受緩沖區(qū)!
問題1:TCP和文件系統(tǒng)的關(guān)聯(lián)?
----->TCP其實(shí)和文件系統(tǒng)很像,就是我們使用系統(tǒng)調(diào)用接口本質(zhì)上是將用戶層緩沖區(qū)數(shù)據(jù)刷新到內(nèi)核的文件緩沖區(qū),但是具體這個(gè)文件緩沖區(qū)什么時(shí)候刷新到磁盤上是由OS自主決定的,而網(wǎng)絡(luò)只不過是將磁盤換成了網(wǎng)卡,這進(jìn)一步說明了Linux中一切皆文件的思想 ,他們IO的思路是一模一樣的,只不過文件是在本地的話,出錯(cuò)的概率很低,而網(wǎng)卡文件需要經(jīng)過網(wǎng)絡(luò),所以需要有TCP協(xié)議來確??煽啃裕?/strong>!
問題2:先暫時(shí)不考慮可靠性,先分析數(shù)據(jù)在網(wǎng)絡(luò)中的流動(dòng)是怎樣的??
----->所以我們想要發(fā)送一個(gè)網(wǎng)絡(luò)數(shù)據(jù),先在應(yīng)用層通過相關(guān)的協(xié)議對(duì)結(jié)構(gòu)化數(shù)據(jù)做序列化,然后放到發(fā)送緩沖區(qū)中,然后TCP會(huì)幫我們?cè)谶m當(dāng)?shù)臅r(shí)機(jī)把數(shù)據(jù)發(fā)送到對(duì)方的接收緩沖區(qū),等待對(duì)方的應(yīng)用層通過read讀取。此時(shí)一般會(huì)出現(xiàn)兩種情況
(1)對(duì)方上層由于忙碌始終無法處理,使得多次發(fā)送的報(bào)文都堆積在接收緩沖區(qū),當(dāng)他不忙碌時(shí)可能一次read就都讀上去了?。∪缓笊蠈釉傩枰獙?duì)這些數(shù)據(jù)解析成一個(gè)個(gè)完整報(bào)文(因?yàn)槭敲嫦蜃止?jié)流的),然后反序列化成結(jié)構(gòu)化數(shù)據(jù)供上層使用。而如果湊不齊一個(gè)報(bào)文,就將他們暫時(shí)存儲(chǔ)在自己的用戶層緩沖區(qū),等下次read的時(shí)候如果湊齊了再一起拿出來解析
(2)對(duì)方的上層會(huì)在合適的時(shí)機(jī)進(jìn)行read,但是如果發(fā)送方始終沒有發(fā)送,或者網(wǎng)絡(luò)太擁堵導(dǎo)致接收緩沖區(qū)一直沒有數(shù)據(jù),那么read就會(huì)阻塞住,然后OS就會(huì)把這個(gè)進(jìn)程設(shè)置為S狀態(tài),而當(dāng)緩沖區(qū)有數(shù)據(jù)的時(shí)候(也就是說OS某些資源就緒的時(shí)候)OS會(huì)再次調(diào)度這個(gè)進(jìn)程去把數(shù)據(jù)從緩沖區(qū)讀上來?。?!
所以以前我們覺得OS某些資源不就緒從而使得進(jìn)程暫時(shí)進(jìn)入S狀態(tài)大多數(shù)指的是因?yàn)橛布乃俣缺容^慢,所以需要等待硬件資源就緒,但是今天我們發(fā)現(xiàn)在網(wǎng)絡(luò)的情況中,也有可能是因?yàn)閷?duì)方始終不給你發(fā)數(shù)據(jù),或者由于網(wǎng)絡(luò)擁塞造成接收緩沖區(qū)沒有數(shù)據(jù)可處理,也是屬于資源不就緒的情況,此時(shí)調(diào)用read的這個(gè)進(jìn)程就必須阻塞住??!
問題3:為什么UDP不需要有發(fā)送緩沖區(qū),而TCP必須要有發(fā)送緩沖區(qū)呢?
------>因?yàn)閁DP發(fā)了就是發(fā)了,他并不關(guān)心對(duì)方的接收緩沖區(qū)是不是滿了,如果滿了他就會(huì)丟包,此時(shí)你也是無感的,UDP不保證可靠性,但是TCP需要保證可靠性?。?!比如說對(duì)方如果來不及把數(shù)據(jù)拿到上層處理導(dǎo)致對(duì)方接受緩沖區(qū)快滿了,TCP會(huì)意識(shí)到對(duì)方接受能力不行(后面會(huì)說),就會(huì)減緩發(fā)送的速度,但是我們上層用戶是無感的?。∧憧赡軙?huì)一直往緩沖區(qū)里寫!所以此時(shí)數(shù)據(jù)就會(huì)被存儲(chǔ)在發(fā)送緩沖區(qū)里了??!當(dāng)然TCP也不會(huì)任由這種情況發(fā)展,他會(huì)通過某種方式要求對(duì)方趕緊清空自己的接收緩沖區(qū)進(jìn)行接收!
3.2TCP協(xié)議段格式
問題1:TCP報(bào)頭如何將數(shù)據(jù)有效地分離呢??
-——>和UDP一樣,通過固定長(zhǎng)度+自描述字段,先讀取定長(zhǎng)的20個(gè)字節(jié),然后再?gòu)睦锩孀x取4位首部長(zhǎng)度(4位首部長(zhǎng)度是包含選項(xiàng)的,他會(huì)先算出選項(xiàng)的大小,然后再讀取若干選項(xiàng),那么剩下的就是數(shù)據(jù)了?。。?/strong>
問題2: 然后將有效載荷交付給上層呢
---->通過自描述字段中的源端口號(hào)和目的端口號(hào),就可以知道交付給上層的哪個(gè)進(jìn)程了!!
問題3:關(guān)于16位校驗(yàn)和
----->16位校驗(yàn)和: 發(fā)送端填充, CRC校驗(yàn). 接收端校驗(yàn)不通過, 則認(rèn)為數(shù)據(jù)有問題. 此處的檢驗(yàn)和不光包含TCP首部, 也包含TCP數(shù)據(jù)部分.
問題4:為什么TCP的報(bào)頭沒有長(zhǎng)度呢??
——>因?yàn)門CP是面向字節(jié)流(后面會(huì)說)的,一直發(fā)的話他就會(huì)一直收。所以不需要長(zhǎng)度
3.3 TCP確認(rèn)應(yīng)答機(jī)制
TCP憑什么保證可靠性呢??必須要基于確認(rèn)應(yīng)答機(jī)制!!當(dāng)對(duì)方成功收到了數(shù)據(jù),就需要對(duì)你做出響應(yīng)??!
應(yīng)答機(jī)制其實(shí)廣泛發(fā)生在外面的生活中,舉例:假設(shè)我和我的好朋友打電話,我總會(huì)是先喊“喂”,此時(shí)我必須得聽到對(duì)方的回復(fù)我才能確定他聽到了我的聲音,而我又得再回復(fù)一次他的回復(fù)才能讓他知道我收到了這個(gè)回復(fù),如此往復(fù),后一條應(yīng)答都可以證明前一條消息被對(duì)方給收到了!!可是這個(gè)世界上并不存在百分之百的應(yīng)答?。?!因?yàn)樽钚碌囊粭l消息是不可能收到應(yīng)答!否則就會(huì)陷入死循環(huán)!所以我們是無法保證所有發(fā)出去的消息都能得到百分之百的回應(yīng),我們只能保證我們歷史發(fā)過的消息可以得到回應(yīng)!!
因此在TCP的方案里,并不要求對(duì)應(yīng)答做應(yīng)答,因?yàn)槭强蛻舳私o服務(wù)器發(fā)消息,服務(wù)端是被動(dòng)的,所以我客戶端能收到應(yīng)答就可以保證我發(fā)出的數(shù)據(jù)被服務(wù)器收到了?。。ㄎ业目蛻舳藳]有必要給服務(wù)端發(fā)消息確保應(yīng)答是否收到?。。?mdash;—>這保證了服務(wù)端到客戶端方向上的可靠性!??!因?yàn)?span>我們的目的是保證通信雙方兩個(gè)方向上的可靠性!
所以應(yīng)答是否有收到呢???萬一丟了怎么辦??TCP對(duì)于發(fā)送失敗會(huì)有自己另外的策略,有時(shí)候會(huì)有需要重發(fā)的場(chǎng)景,因此我們會(huì)把沒收到響應(yīng)的數(shù)據(jù)暫時(shí)保存在緩沖區(qū)中維持一段時(shí)間?。?/p>
記?。?!應(yīng)答不一定是單獨(dú)發(fā)的??!因?yàn)槿绻藭r(shí)服務(wù)端也正好想發(fā)消息給你,先發(fā)應(yīng)答再發(fā)消息顯然效率是比較低的!所以他會(huì)在發(fā)消息的時(shí)候順便應(yīng)答,這就是捎帶應(yīng)答!!
3.416位窗口大小
我們前面提到了一個(gè)關(guān)于對(duì)方接受能力的問題,如果對(duì)方接受能力不足但是你用戶層一直發(fā)消息導(dǎo)致丟包問題顯然是不可靠的,所以我們的TCP除了應(yīng)該有發(fā)送緩沖區(qū)的存在,還應(yīng)該有一些配套的解決方案??!
首先TCP里面有一種機(jī)制就是超時(shí)重傳(下一篇會(huì)說),就是他在發(fā)送的時(shí)候不會(huì)立馬將數(shù)據(jù)從緩沖區(qū)清空,而是如果在規(guī)定時(shí)間內(nèi)沒有收到對(duì)方的應(yīng)答,他就會(huì)判定數(shù)據(jù)丟失了然后再補(bǔ)發(fā)。但是這種方案顯然不夠合理,因?yàn)槲疫@個(gè)數(shù)據(jù)千里迢迢來到了你這里,浪費(fèi)了這么多的網(wǎng)絡(luò)資源,卻因?yàn)槟愕慕邮漳芰Σ恍袑?dǎo)致我在沒有明顯錯(cuò)誤的情況下被你丟棄,那么曾經(jīng)的發(fā)送不就是沒有意義了的嗎??所以我們更好的方式就是應(yīng)該考慮在發(fā)送的時(shí)候就去控制傳輸速度避免報(bào)文的丟失??!
而通過控制用戶發(fā)送速度來讓對(duì)方能夠來得及接收從而規(guī)避大量的丟包情況,這種方案叫做流量控制(下一篇會(huì)說)。
可是,發(fā)慢一點(diǎn)的依據(jù)是什么呢???我怎么知道對(duì)方的緩沖區(qū)接受能力是多少呢??別忘了TCP是有確認(rèn)應(yīng)答機(jī)制的!!我發(fā)給你一個(gè)報(bào)文,你是要響應(yīng)的??!而且你響應(yīng)的時(shí)候會(huì)加上報(bào)文,而報(bào)文里的字段包含了16位的窗口大小,而窗口大小填充的就是服務(wù)端剩余空間的大??!!TCP可以因此推斷出對(duì)方的接收能力從而控制傳輸速度??!
同理,雙方在通信的時(shí)候都會(huì)有報(bào)文的往來,所以其實(shí)在建立連接的時(shí)候雙方不僅僅只是建立連接了,同時(shí)也協(xié)商了雙方的接收能力,那么雙方都可以互相進(jìn)行流量控制!
問題:16位數(shù)字最大表示65535, 那么TCP窗口最大就是65535字節(jié)么?
——>實(shí)際上, TCP首部40字節(jié)選項(xiàng)中還包含了一個(gè)窗口擴(kuò)大因子M, 實(shí)際窗口大小是窗口字段的值左移 M 位;
3.5序號(hào)解決數(shù)據(jù)包亂序
tcp的原始過程,發(fā)一條消息確認(rèn)響應(yīng)之后再發(fā)送下一條,但是如果我們想要發(fā)送很多信息,但是服務(wù)端卻一直不應(yīng)答,顯然這種串型的效率是很低的?。∷晕覀兊目蛻舳艘话銜?huì)一次向服務(wù)端發(fā)送一堆消息!!
批量化發(fā)請(qǐng)求,也意味著需要批量化應(yīng)答??!那么在提高效率的同時(shí)就會(huì)伴生出兩個(gè)問題(1)數(shù)據(jù)亂序(發(fā)出去的數(shù)據(jù)并未按照順序被接收) (2)哪個(gè)應(yīng)答對(duì)應(yīng)哪個(gè)請(qǐng)求
問題1:解決數(shù)據(jù)包亂序的問題
——>報(bào)文里會(huì)攜帶序號(hào)?。∈怯脕肀WC數(shù)據(jù)的按時(shí)到達(dá)的,另一方接收緩沖區(qū)會(huì)根據(jù)序號(hào)做排序!!其實(shí)這里的序號(hào)對(duì)應(yīng)的就是發(fā)送的數(shù)據(jù)的最后一個(gè)字符的下標(biāo)??! TCP對(duì)每個(gè)字節(jié)的數(shù)據(jù)都進(jìn)行了編號(hào),即為序列號(hào)
但是要記住的是,雖然是按照字節(jié)做的編號(hào),但是發(fā)送的時(shí)候不是一個(gè)字節(jié)一個(gè)字節(jié)發(fā)的,而是一個(gè)數(shù)據(jù)塊一個(gè)數(shù)據(jù)塊地發(fā)!!
問題2:一次返回這么多應(yīng)答,你怎么確定哪個(gè)應(yīng)答對(duì)應(yīng)的是哪個(gè)數(shù)據(jù)呢??
——>需要引入確認(rèn)序號(hào)(填充的是收到的報(bào)文序號(hào)+1)的定義:表示確認(rèn)序號(hào)之前的數(shù)據(jù)都已經(jīng)被接收到了,下一次發(fā)送,請(qǐng)從確認(rèn)序號(hào)指定的數(shù)字開始發(fā)送??!
問題3:應(yīng)答不是只有報(bào)文沒有數(shù)據(jù)嗎??那我為什么不直接把你的序號(hào)+1返回去而是要同時(shí)擁有序號(hào)和確認(rèn)序號(hào)呢??
——>(1)因?yàn)榭赡軙?huì)存在“捎帶應(yīng)答”的情況(雙重身份),可能我服務(wù)器也會(huì)順便發(fā)送我的緩沖區(qū)數(shù)據(jù),本身的數(shù)據(jù)是用的順序序號(hào),而應(yīng)答用的是確認(rèn)序號(hào),所以必須分開不能復(fù)用??!
(2)服務(wù)端和客戶端地位是對(duì)等的,所以客戶端給服務(wù)端發(fā)消息的時(shí)候服務(wù)端也可能在給客戶端發(fā)消息,所以捎帶應(yīng)答的情況是經(jīng)常會(huì)出現(xiàn)的?。∷员仨氁袃山M序號(hào)!!
問題4:序號(hào)會(huì)越界或者跟別的數(shù)據(jù)序號(hào)出現(xiàn)沖突嗎??
——>序號(hào)一般是回繞的,而且回繞特別大,一般不會(huì)沖突!!
3.6 引入六個(gè)標(biāo)記位
我們都知道,TCP協(xié)議是基于鏈接的,在正常通信之前需要進(jìn)行鏈接,在正常通信之后需要進(jìn)行斷開連接,可是我怎么知道你是這個(gè)報(bào)文是打算做哪個(gè)工作呢???——>所以我們可以知道TCP報(bào)文一定有各種不同的類型?。《煌念愋蜎Q定了服務(wù)端要做不同的工作!!
接收方如何得知報(bào)頭的類型各自是什么呢??——>需要引入6個(gè)標(biāo)記位,標(biāo)記位存在的意義就是為了區(qū)分TCP報(bào)頭類型的
我們要知道服務(wù)端:客戶端是1:n,所以服務(wù)端必然是存在多個(gè)鏈接的!所以O(shè)S必須想辦法把這些“鏈接”管理起來——先描述再組織!所以當(dāng)雙方建立連接成功后比如會(huì)建立一個(gè)相關(guān)的結(jié)構(gòu)體,然后斷開連接后會(huì)各自釋放空間。
(1)ACK: 確認(rèn)號(hào)是否有效(應(yīng)答)
(2)SYN: 請(qǐng)求建立連接; 我們把攜帶SYN標(biāo)識(shí)的稱為同步報(bào)文段(請(qǐng)求三次握手)
(3)FIN: 通知對(duì)方, 本端要關(guān)閉了(請(qǐng)求四次揮手)
(4)RST: 對(duì)方要求重新建立連接; 我們把攜帶RST標(biāo)識(shí)的稱為復(fù)位報(bào)文段(鏈接重置)
我們要知道,TCP為了保證可靠性,提供了很多健全的方案,但是這并不代表可以覆蓋所有的情況,因?yàn)楹芏鄷r(shí)候會(huì)出現(xiàn)一些不可抗力因素(比如協(xié)議bug、網(wǎng)絡(luò)bug等等) 所以TCP是允許鏈接失敗的?。”热缒炒挝帐譀]有成功(因?yàn)樽詈笠粋€(gè)ACK可能沒有收到?。?/strong>
對(duì)于客戶端來說,只要把第三次握手的報(bào)文發(fā)出,他就認(rèn)為鏈接建立好了,然后把自己的準(zhǔn)備工作給做好了!!可是一旦ACK真的丟失了,那么服務(wù)端會(huì)認(rèn)為鏈接沒有建立好,也就不會(huì)做這些準(zhǔn)備工作,因?yàn)橹虚g存在時(shí)間窗口,所以雙方會(huì)出現(xiàn)認(rèn)知不一致的情況,此時(shí)客戶端會(huì)直接發(fā)送數(shù)據(jù),當(dāng)服務(wù)端接收到后會(huì)覺得很奇怪“你明明沒有和我鏈接成功,為什么要給我發(fā)數(shù)據(jù)呢??” 于是這是他會(huì)意識(shí)到可能是客戶端誤以為跟自己鏈接成功了,所以他會(huì)趕快通過RST告訴客戶端“兄弟,你別傳了,你的連接是失敗的,你再重新連接一次吧”這其實(shí)就是鏈接重置!!
(5)PSH: 提示接收端應(yīng)用程序立刻從TCP緩沖區(qū)把數(shù)據(jù)讀走+
首先我們要知道,其實(shí)接收緩沖區(qū)就相當(dāng)于一個(gè)內(nèi)存空間,而OS負(fù)責(zé)接收數(shù)據(jù),上層負(fù)責(zé)拿數(shù)據(jù),其實(shí)本質(zhì)上就是一個(gè)生產(chǎn)消費(fèi)模型,所以流量控制本質(zhì)上就是對(duì)生產(chǎn)消費(fèi)模型的一種同步機(jī)制!!
問題:因?yàn)榈弥獙?duì)方接受緩沖區(qū)沒啥空間了,于是TCP就控制幾乎不往緩沖區(qū)里寫了,可是我得等到什么時(shí)候呢??萬一對(duì)方已經(jīng)把接收緩沖區(qū)的數(shù)據(jù)刷走了,那我又怎么知道呢???
——>方案1:發(fā)送方會(huì)定期詢問對(duì)方,看看對(duì)方的接收緩沖區(qū)是不是讀完了??!
方案2:接受方知道自己之前數(shù)據(jù)快滿了,所以刷新之后會(huì)給對(duì)方發(fā)個(gè)響應(yīng)告訴他說可以繼續(xù)發(fā)了
這兩種策略同時(shí)存在,如果對(duì)方就是一直不拿走,那么TCP會(huì)給對(duì)方發(fā)送PSH,警告他盡快拿走!
(6)URG: 緊急指針是否有效
3.7緊急指針
報(bào)文有一個(gè)地方是16位緊急指針,他記錄的是緊急數(shù)據(jù)(需要高優(yōu)先級(jí)處理的數(shù)據(jù))的偏移量!
問題1:我知道了偏移量,但是具體有多大呢???
——>他被要求了一個(gè)報(bào)文只能攜帶一個(gè)字節(jié)的緊急數(shù)據(jù),所以我們需要軟件功能來提供一些狀態(tài)編號(hào)!!
問題2:緊急數(shù)據(jù)要怎么讀怎么寫呢??
——>send和recive,他們中的選項(xiàng)有一個(gè)叫做MSG_OOB
緊急數(shù)據(jù)也叫做帶外數(shù)據(jù)
問題3:緊急數(shù)據(jù)的場(chǎng)景?
——>比如說你當(dāng)前服務(wù)器正在進(jìn)行一個(gè)周期性的服務(wù)或者是服務(wù)器爆滿導(dǎo)致卡頓,此時(shí)你無論如何申請(qǐng)都得不到對(duì)方的響應(yīng),這個(gè)時(shí)候你很好奇服務(wù)器究竟怎么了,于是你需要發(fā)送詢問,但是這個(gè)詢問必須通過帶外數(shù)據(jù)的方式去發(fā)送,才能讓服務(wù)端優(yōu)先處理,然后服務(wù)端會(huì)通過一個(gè)描述的狀態(tài)碼,在將一個(gè)帶外數(shù)據(jù)返回給你,你就知道服務(wù)端究竟是什么情況了!!
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
在Unix/Linux上使用通用二進(jìn)制文件安裝MySQL方式
本文介紹了如何在Unix/Linux平臺(tái)上從壓縮的tar文件二進(jìn)制發(fā)行版安裝MySQL,首先,需要下載并解壓縮發(fā)行版,然后創(chuàng)建一個(gè)符號(hào)鏈接并將其添加到PATH變量中,接下來,設(shè)置發(fā)行版的所有權(quán)和訪問權(quán)限,初始化數(shù)據(jù)目錄,啟動(dòng)MySQL服務(wù)器,并設(shè)置配置文件2025-02-02關(guān)閉linux終端還讓程序繼續(xù)執(zhí)行的實(shí)現(xiàn)方式
這篇文章主要介紹了關(guān)閉linux終端還讓程序繼續(xù)執(zhí)行的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12Ubuntu14.04服務(wù)器環(huán)境下配置PHP7.0+Apache2+Mysql5.7的方法
這篇文章主要介紹了Ubuntu14.04服務(wù)器環(huán)境下配置PHP7.0+Apache2+Mysql5.7的方法,較為詳細(xì)的分析了Ubuntu14.04操作系統(tǒng)環(huán)境下配置PHP7.0+Apache2+Mysql5.7的具體步驟與相關(guān)命令使用技巧,需要的朋友可以參考下2018-04-04深入理解Apache?RocketMQ?中Message?消息的核心概念
深入理解一下Apache?RocketMQ中Message(消息)這個(gè)核心概念,這份文檔詳細(xì)闡述了消息的定義、在模型中的位置、內(nèi)部屬性、約束和使用建議,感興趣的朋友跟隨小編一起學(xué)習(xí)吧2025-08-08centos7安裝mysql并jdbc測(cè)試實(shí)例詳解
這篇文章主要介紹了centos7安裝mysql并jdbc測(cè)試實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02快速搭建簡(jiǎn)易、高效、多線程http服務(wù)器
通過Node.js來快速高效的搭建一個(gè)高性能http服務(wù)器,github上Charlie Robbins分享了一個(gè)開源項(xiàng)目,讓大家都可以輕松的搭建臨時(shí)高性能http服務(wù)器2018-02-02Linux centos如何讓普通用戶獲取root權(quán)限
這篇文章主要介紹了Linux centos如何讓普通用戶獲取root權(quán)限問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05