樹(shù)脂工藝品網(wǎng)站建設(shè)公司北京優(yōu)化seo排名優(yōu)化
目錄
四位首部長(zhǎng)度
序號(hào)
捎帶應(yīng)答
標(biāo)記位
超時(shí)重傳機(jī)制
連接管理機(jī)制(RST標(biāo)記位)
三次握手及四次揮手的原因
TCP的全稱(chēng)是傳輸控制協(xié)議(Transmission Control Protocol),也就是說(shuō),對(duì)于放到TCP發(fā)送緩沖區(qū)中的數(shù)據(jù),如何發(fā),什么時(shí)候發(fā),出錯(cuò)了怎么辦,這些都是有TCP協(xié)議控制的
下面是TCP報(bào)文的基本格式
標(biāo)準(zhǔn)報(bào)頭是20個(gè)字節(jié),并且在傳輸層,并不會(huì)像我們?cè)趹?yīng)用層一樣還需要序列化,傳輸層直接發(fā)送的就是結(jié)構(gòu)化的數(shù)據(jù),因?yàn)檫@樣發(fā)送成本是最低的,發(fā)送帶寬最小。那它不是會(huì)有跨平臺(tái),大小端的問(wèn)題嗎?Linux內(nèi)核中對(duì)它進(jìn)行了處理:
不同的客戶(hù)端都同時(shí)可以基于TCP協(xié)議向服務(wù)器發(fā)送信息,那么OS中就會(huì)存在很多收到的報(bào)文,這些報(bào)文可能正在網(wǎng)絡(luò)層或數(shù)據(jù)鏈路層中,還沒(méi)有拷貝到傳輸層的接收緩沖區(qū),可能就在緩沖區(qū)中,還沒(méi)來(lái)的及處理,總之各種情況
那么OS就要對(duì)大量的已經(jīng)收到的,但是暫未處理的報(bào)文進(jìn)行管理,如何管理呢?先描述再組織
而描述這種報(bào)文的結(jié)構(gòu)體就叫做struct sk_buffer,它里面存有指針,就指向存著報(bào)文的內(nèi)存空間,對(duì)于每層協(xié)議來(lái)說(shuō),報(bào)頭在前面,正文在后面,我們?nèi)绻獎(jiǎng)冸x報(bào)頭,只需要指針向后移動(dòng)一定的字節(jié)數(shù)即可,添加報(bào)頭就是向前移動(dòng)。所以封裝與解包本質(zhì)就是指針移動(dòng),然后進(jìn)行一定操作
四位首部長(zhǎng)度
那下面來(lái)解釋一下TCP報(bào)文中的4位首部長(zhǎng)度:
這個(gè)長(zhǎng)度指的是報(bào)頭的20個(gè)字節(jié)加上選項(xiàng)的長(zhǎng)度,并且基本單位是為4字節(jié)
也就是說(shuō)4位能表示的最大數(shù)字是15,即報(bào)頭+選項(xiàng)的最大長(zhǎng)度是15*4=60字節(jié),選項(xiàng)的范圍是[0,40]
我們都說(shuō)TCP協(xié)議是可靠的,它保證可靠性,具體體現(xiàn)是什么呢?
因?yàn)槲覀儼l(fā)到網(wǎng)絡(luò)中的消息,我們根本不知道它去了哪里,我們?nèi)绻胫浪袥](méi)有被對(duì)方收到,就只能看有沒(méi)有收到應(yīng)答
所以我們可以確定的是如果我收到應(yīng)答,那么我發(fā)的消息對(duì)方肯定收到了。這依靠的就是確認(rèn)應(yīng)答機(jī)制:我收到了消息就要應(yīng)答
所以可靠性是指:我能知道對(duì)方收到了我發(fā)的消息,同時(shí)我也得知道對(duì)方?jīng)]收到我發(fā)的消息
發(fā)送數(shù)據(jù)和應(yīng)答這些細(xì)節(jié)其實(shí)都是雙方OS完成的,這也就是“傳輸控制協(xié)議”中“控制”的一個(gè)體現(xiàn)
序號(hào)
我們發(fā)送數(shù)據(jù)可以串行發(fā),也就是發(fā)送一條等應(yīng)答,然后再發(fā)下一條等應(yīng)答……,但是這樣效率太慢了
我們可以一次發(fā)多條,但是問(wèn)題就來(lái)了,對(duì)方也給我很多應(yīng)答,那如果有一條消息對(duì)方?jīng)]收到,那我怎么知道是那一條消息呢?并且對(duì)方怎么判斷那條消息在前,那條消息在后呢?(因?yàn)槲业陌l(fā)送順序并不一定是對(duì)方的接收順序)
這個(gè)問(wèn)題的解決方法就是報(bào)頭中的序號(hào),我們可以給消息帶序號(hào),對(duì)方收到了一堆消息也可以通過(guò)排序序號(hào)來(lái)知道我發(fā)的消息的順序,然后再按順尋處理數(shù)據(jù)即可,所以應(yīng)答就要把確認(rèn)序號(hào)帶上,這個(gè)確認(rèn)序號(hào)一般是發(fā)來(lái)消息的序號(hào)+1
確認(rèn)序號(hào)的含義就是該確認(rèn)序號(hào)之前的數(shù)據(jù),對(duì)方已經(jīng)全部收到了,下次發(fā)送請(qǐng)從確認(rèn)序號(hào)開(kāi)始
我們知道TCP是有發(fā)送緩沖區(qū),并且TCP是面向字節(jié)流的,其實(shí)我們可以把這個(gè)緩沖區(qū)看成一個(gè)一定長(zhǎng)度的char數(shù)組,那么緩沖區(qū)中每個(gè)字節(jié)其實(shí)都有自己的序號(hào),這個(gè)序號(hào)就是下標(biāo),確認(rèn)序號(hào)我們前面說(shuō)就是發(fā)來(lái)的報(bào)頭中的序號(hào)+1,那么新的序號(hào)其實(shí)就是確認(rèn)序號(hào)+我要發(fā)送的報(bào)文的長(zhǎng)度
捎帶應(yīng)答
TCP是支持全雙工的,既要向?qū)Ψ桨l(fā)送消息,也要向?qū)Ψ桨l(fā)送應(yīng)答,所以需要序號(hào)和確認(rèn)序號(hào)兩個(gè)
如果只發(fā)應(yīng)答,只需要發(fā)一個(gè)報(bào)頭(報(bào)頭中添加確認(rèn)序號(hào))即可,那么我可不可以發(fā)送一個(gè)報(bào)文,其中既有應(yīng)答(確認(rèn)序號(hào)),又有我想給對(duì)方發(fā)的數(shù)據(jù)(序號(hào)和正文)呢?當(dāng)然可以,我們叫做捎帶應(yīng)答
標(biāo)記位
下面我們來(lái)解釋TCP報(bào)頭中的標(biāo)志位:
為什么要有標(biāo)志位呢?其實(shí)就是區(qū)分不同的TCP報(bào)文類(lèi)型
就比如我們知道基于TCP通信時(shí),客戶(hù)端要connect服務(wù)器,這是發(fā)的報(bào)文就是建立連接的報(bào)文,同時(shí)還有正常通信的報(bào)文,還有close時(shí)斷開(kāi)連接的報(bào)文等等,這些報(bào)文類(lèi)型都不同,那么為了區(qū)分這些報(bào)文類(lèi)型,就有了標(biāo)志位
下面我們簡(jiǎn)單通過(guò)三次握手這個(gè)過(guò)程來(lái)介紹三個(gè)標(biāo)記位:ACK(確認(rèn)標(biāo)記位),SYN(同步標(biāo)記位),FIN(斷開(kāi)連接標(biāo)記位)
這個(gè)過(guò)程是我們?cè)诳蛻?hù)端調(diào)用connect后OS自動(dòng)完成的,首先客戶(hù)端向服務(wù)器發(fā)送帶SYN標(biāo)記位(即將此位 置為1)的報(bào)頭,服務(wù)器收到后向客戶(hù)端發(fā)送SYN+ACK的報(bào)頭,客戶(hù)端收到后向服務(wù)器發(fā)送ACK的報(bào)頭,至此三次握手成功后服務(wù)器accept獲取連接。我們將服務(wù)器置為監(jiān)聽(tīng)狀態(tài)是因?yàn)橹挥衛(wèi)isten狀態(tài)的服務(wù)器才能受理SYN的請(qǐng)求
雙方建立好連接之后是要對(duì)這個(gè)連接進(jìn)行管理的,用內(nèi)核數(shù)據(jù)結(jié)構(gòu)去管理,,這樣就會(huì)消耗內(nèi)存空間和時(shí)間。所以說(shuō)維護(hù)連接是有成本的
下面是四次揮手的大致過(guò)程:這個(gè)過(guò)程也是OS自動(dòng)完成的
先調(diào)用close的一方(A)要向?qū)Ψ桨l(fā)送FIN請(qǐng)求,對(duì)方(B)收到后會(huì)發(fā)送ACK,之后B會(huì)發(fā)送FIN請(qǐng)求,A收到后發(fā)送ACK
超時(shí)重傳機(jī)制
這個(gè)機(jī)制其實(shí)在報(bào)頭中是沒(méi)有體現(xiàn)的,當(dāng)我們向?qū)Ψ桨l(fā)送請(qǐng)求時(shí),如果我們遲遲都收不到應(yīng)答,我們只能主觀的認(rèn)為對(duì)方?jīng)]收到數(shù)據(jù)(也有可能收到了,只不過(guò)對(duì)方的ACK可能沒(méi)發(fā)過(guò)來(lái),我們肯定要按最差情況處理),此時(shí),我們就認(rèn)為超時(shí)了,我們需要重傳。
也就是說(shuō),如果我們發(fā)送的請(qǐng)求在一個(gè)時(shí)間段內(nèi)都沒(méi)有收到應(yīng)答,就要觸發(fā)超時(shí)重傳機(jī)制。
那萬(wàn)一對(duì)方收到了呢?只不過(guò)是ACK沒(méi)發(fā)過(guò)來(lái),因?yàn)閳?bào)頭中是有序號(hào)的,對(duì)方可以根據(jù)這個(gè)序號(hào)進(jìn)行去重
上面說(shuō)一個(gè)時(shí)間段內(nèi),那這是多少時(shí)間呢?
因?yàn)榫W(wǎng)絡(luò)的狀態(tài)是動(dòng)態(tài)的,TCP為了保證無(wú)論在任何環(huán)境下都能比較高性能的通信,因此會(huì)動(dòng)態(tài)計(jì)算這個(gè)超時(shí)時(shí)間
Linux,超時(shí)以500ms為一個(gè)單位進(jìn)行控制,等待時(shí)間以此是1*500,2*500,4*500,這樣以指數(shù)形式遞增,累積到一定的重傳次數(shù),TCP就會(huì)認(rèn)為網(wǎng)絡(luò)或?qū)Χ酥鳈C(jī)出現(xiàn)異常,強(qiáng)制關(guān)閉連接
連接管理機(jī)制(RST標(biāo)記位)
有這樣一種情況,就是三次握手的第三次ACK發(fā)送之后丟掉了,這時(shí)客戶(hù)端已經(jīng)認(rèn)為建立好連接了,但是服務(wù)器卻沒(méi)有建立好連接。
此時(shí)客戶(hù)端都可以向服務(wù)器發(fā)送數(shù)據(jù)了,此時(shí)服務(wù)器因?yàn)闆](méi)有建立好連接但是收到了數(shù)據(jù),此時(shí)就會(huì)向客戶(hù)端發(fā)送帶有RST標(biāo)記位的報(bào)頭來(lái)表示reset(重置),即重新進(jìn)行三次握手
所以RST這個(gè)標(biāo)記位就是用來(lái)解決建立連接出現(xiàn)異常的問(wèn)題的
三次握手及四次揮手的原因
三次握手:
如果一次就可以握手,那太扯了,因?yàn)榭蛻?hù)端連應(yīng)答都沒(méi)有收到,連最基本的網(wǎng)絡(luò)連通性都無(wú)法確認(rèn)
兩次的話(huà)服務(wù)器是比客戶(hù)端先建立連接的,維護(hù)連接是有成本的,如果客戶(hù)端是惡意連接的,瘋狂發(fā)送SYN請(qǐng)求(SYN洪水攻擊),那么服務(wù)器資源就會(huì)被浪費(fèi),從而導(dǎo)致真正想連接的用戶(hù)無(wú)法連接
所以三次握手就是因?yàn)?#xff1a;
1.需要保證網(wǎng)絡(luò)(信道)是健康的,三次握手,客戶(hù)端服務(wù)器雙方都會(huì)有一次確定的收發(fā),他們就可以確認(rèn)是全雙工
2.確保雙方OS是健康且愿意通信的
四次揮手:
它的原因其實(shí)和三次握手是一樣的,關(guān)鍵就是四次揮手沒(méi)有進(jìn)行捎帶應(yīng)答,因?yàn)殡m然比如客戶(hù)端關(guān)閉連接,但是服務(wù)器還有消息要發(fā)。那么此時(shí)用戶(hù)如何拿取數(shù)據(jù)呢?
首先一般應(yīng)用層代碼是有邏輯的,一般按照協(xié)議收完消息后才會(huì)close;其次,其實(shí)我們可以僅關(guān)閉寫(xiě)端,不關(guān)閉讀端,是有這樣的接口的
如果選項(xiàng)是SHUT_RDWR,就相當(dāng)于close