思考
在進(jìn)入正文之前,我們先看看這些面試問的一些問題寺谤!
TCP和UDP區(qū)別仑鸥?TCP報文是什么?TCP的三次握手变屁?兩次行不行眼俊?TCP的四次揮手?TIME_WAIT知道嗎粟关?
TCP為什么是安全的疮胖?TCP為什么會有黏包問題?
TCP特性
我們都知道TCP/UDP都是傳輸層的協(xié)議闷板!這個一定需要知道澎灸,意思是它只負(fù)責(zé)傳輸,類似貨車運(yùn)東西遮晚。
基于TCP的應(yīng)用層協(xié)議有那些呢性昭?常用的基于TCP的應(yīng)用層協(xié)議有 SMTP:(簡單的郵件傳輸協(xié)議)HTTP ,Telnet,以及FTP(文件傳輸協(xié)議)都是基于TCP的县遣。
基于UDP呢?SNMP:(簡單的網(wǎng)絡(luò)管理協(xié)議)TFTP:簡單的文件傳送協(xié)議糜颠,以及DNS:域名解析協(xié)議。
TCP是面向字節(jié)流的 萧求,具有可靠性其兴,面向連接的這三個特性。我們從這三個特性來展開討論
面向字節(jié)流
為什么是說TC平時面向字節(jié)流的呢
按照圖示所描述的那樣夸政,我們在傳輸層的上面可能有很多的程序元旬,它們都需要發(fā)起網(wǎng)絡(luò)傳輸?shù)姆?wù),就拿經(jīng)常使用的微信來說,我們平時發(fā)的都是文本法绵,怎么到TCP是字節(jié)流了呢?
我們看看TCP的報文結(jié)構(gòu)酪碘,在計算機(jī)的網(wǎng)絡(luò)里面朋譬,報文就代表一次傳輸?shù)臄?shù)據(jù)。
我們看到在傳輸層兴垦,它是不知道這個數(shù)據(jù)要送往那個ip 的徙赢,但他會知道是目標(biāo)端口和源端口。
不加上其他的數(shù)據(jù)TCP的一個報文頭部數(shù)據(jù)就占了探越,20個字節(jié)狡赐,所以這也是TCP的一個缺點(diǎn),報頭占空間大钦幔。
可變選項
可變選項是TCP代表的內(nèi)容可以有多種枕屉,比如擴(kuò)大窗口
為什么說TCP是面向字節(jié)流呢?
TCP不會把應(yīng)用層傳遞的內(nèi)容一下子都都發(fā)送出去鲤氢,因為網(wǎng)絡(luò)是不穩(wěn)定的搀擂,如果一旦發(fā)送失敗就會造成大量的數(shù)據(jù)浪費(fèi)。所以TCP會把應(yīng)用層傳遞的內(nèi)容卷玉,變成字節(jié)流哨颂,給他分段并且進(jìn)行編號,分批發(fā)送相种。
- TCP會把應(yīng)用層發(fā)送的數(shù)據(jù)威恼,放到自己的發(fā)送緩沖區(qū),同時給這些字節(jié)標(biāo)上序號寝并。
- TCP從發(fā)送緩沖區(qū)取出適當(dāng)大小的字節(jié)箫措,組成一個TCP報文發(fā)送出去
- 接收方也會有一個數(shù)據(jù)接受緩沖區(qū),在合適的時候進(jìn)行組包傳給給接收方衬潦。
這樣的方式意味著不會一下子發(fā)送很大的數(shù)據(jù)蒂破,但是會引入一個新問題,就是接收方并不知道這些字節(jié)流代表什么别渔?會出現(xiàn)上一個請求的數(shù)據(jù)發(fā)送的數(shù)據(jù)和下一個請求發(fā)送的數(shù)據(jù)都是一起發(fā)送過來的出現(xiàn)了粘連的情況附迷。
可靠性
根據(jù)上面的TCP報文結(jié)構(gòu)我們知道了,TCP會對每一個發(fā)送的包進(jìn)行編號哎媚,這個編號在接收方在接受到一個數(shù)據(jù)包之后喇伯,會向發(fā)送方確認(rèn)數(shù)據(jù)包,也就是我們上面看到數(shù)據(jù)包的 ACK標(biāo)志位會被設(shè)置為1拨与。
但是會出現(xiàn)一種問題稻据,如果我發(fā)送的這個包接收方并沒有收到或者它發(fā)送的ACK的這個數(shù)據(jù)包我也沒有收到,出現(xiàn)了數(shù)據(jù)包丟失的情況。TCP為了保證數(shù)據(jù)的完整性它會采用超時重傳機(jī)制捻悯,會在發(fā)送一個編號的包之后進(jìn)行計時匆赃,如果在超時,便會重寫發(fā)送這個數(shù)據(jù)包今缚。所以接收方可能會收到兩個相同序號的數(shù)據(jù)包算柳,這時候需要根據(jù)序號來去重。
如果在發(fā)送方發(fā)送一個字段就開始等待ACK確認(rèn)姓言,這樣的效率太低了瞬项,所以TCP會一下子發(fā)送一連串的數(shù)據(jù)包。但是也不能無限制的發(fā)送何荚,我們知道接收方是有一個緩沖區(qū)的囱淋,他可能會受到內(nèi)存,網(wǎng)絡(luò)以及其他因素的限制餐塘,不可能隨時都接受大量的數(shù)據(jù)妥衣。這時候TCP就引入滑動窗口的機(jī)制
- 發(fā)送方需要根據(jù)接收方的緩存區(qū)大小,設(shè)置發(fā)送窗口大小戒傻。
- 當(dāng)窗口里面的數(shù)據(jù)都被確認(rèn)回復(fù)之后称鳞,窗口會進(jìn)行滑動發(fā)送下一段的數(shù)據(jù)。
在TCP的報文里面我們可以看到有一個窗口字段稠鼻,這個就是用來控制發(fā)送方窗口的冈止,當(dāng)接收方處理不過來這么多數(shù)據(jù)的時候,它就會縮小窗口來進(jìn)行流量控制候齿。
但是新的問題產(chǎn)生了熙暴!由于發(fā)送一連串的數(shù)據(jù)包,接收方如果對每一個都進(jìn)行確認(rèn)又會導(dǎo)致效率變低慌盯。TCP會進(jìn)行累計確認(rèn)周霉,比如發(fā)送方發(fā)送了1,2亚皂,3俱箱,4,四個數(shù)據(jù)包灭必,接收方只用回復(fù)ACK 4這個序號的數(shù)據(jù)包就可以了狞谱,代表前面?zhèn)鬟f1,2禁漓,3跟衅,4已經(jīng)都被接收了。
快速重傳機(jī)制
但這樣發(fā)送連續(xù)窗口的時候播歼,我們發(fā)生丟包的時候按照超時重傳的機(jī)制會造成大量的數(shù)據(jù)冗余問題伶跷。TCP可以采用超時重傳的機(jī)制來提高傳輸效率比如發(fā)送方 ,發(fā)送了 1,2叭莫,3蹈集,4,5雇初,6拢肆,這么長串的數(shù)據(jù)包,報文段1成功接收并被確認(rèn)ACK 2抵皱,接收端的期待序號為2善榛!結(jié)果2因為某些原因沒收到辩蛋,3到達(dá)了呻畸,于是還是ack回2,后面的4和5都到了悼院,但是還是ack回2伤为,因為2還是沒有收到,于是發(fā)送端收到了三個ack=2的確認(rèn)据途,知道了2還沒有到绞愚,于是就馬上重轉(zhuǎn)2。然后颖医,接收端收到了2位衩,此時因為3,4熔萧,5都收到了糖驴,于是ack回6。示意圖如下:
但是為什么是三次ACK報文呢佛致?
首先要明白一點(diǎn)贮缕,即使發(fā)送端是按序發(fā)送,由于TCP包是封裝在IP包內(nèi)俺榆,IP包在傳輸時亂序感昼,意味著TCP包到達(dá)接收端也是亂序的,亂序的話也會造成接收端發(fā)送冗余ACK罐脊。這時候我們就要區(qū)分是亂序造成還是因為丟包引起的定嗓。
例如:我們發(fā)送 N-1 N N+1 N+2 一連串的報文
從以上羅列的情況可以看出,
在沒丟失的情況下萍桌,有40%的可能出現(xiàn)3次冗余ACK
在亂序的情況下必定是2次冗余ACK
在丟失的情況下蜕乡,必定出現(xiàn)3次冗余ACK
基于這樣的概率,選定3次冗余ACK作為閾值也算是合理的梗夸。在實際抓包中层玲,大多數(shù)的快速重傳都會在大于3次冗余ACK后發(fā)生。
選擇確認(rèn)機(jī)制(SACK)
超時重傳還是會有冗余的ACK報文出現(xiàn),我們還可以在報文里面的選擇的字段辛块,添加需要重傳的序號畔派,減少冗余的確認(rèn)報文。
網(wǎng)絡(luò)擁塞
在網(wǎng)絡(luò)世界里面?zhèn)鬟f數(shù)據(jù)是不穩(wěn)定的润绵,收到各種因素的影響线椰。我們知道TCP發(fā)送方會根據(jù)接收方的緩存區(qū)的大小來調(diào)整自己的發(fā)送窗口。但是它是怎么確定這個大小的呢?
如果所示(這里的Y軸代表窗口大小尘盼,X軸代表傳輸?shù)妮喆危?br> TCP在網(wǎng)絡(luò)擁塞的時候會遵循 四個特性:慢開始憨愉、快恢復(fù)、快重傳卿捎、擁塞避免配紫。
- 在最開始的時候會把窗口設(shè)置為一個極小值,然后每傳遞一輪如果沒有發(fā)生超時重傳午阵,就會把窗口變?yōu)樵瓉淼膬杀丁?/li>
- 在達(dá)到某個閾值(這閾值是根據(jù)當(dāng)前網(wǎng)絡(luò)情況決定的)的時候開始進(jìn)入網(wǎng)絡(luò)擁塞躺孝,每輪把窗口大小提高1,慢慢試探底桂。
- 當(dāng)發(fā)生超時的時候植袍,表示可能發(fā)生了網(wǎng)絡(luò)的擁塞,這時候會重新回到慢啟動(這里被稱為快恢復(fù))
- 如果出現(xiàn)了快速重傳的現(xiàn)象籽懦,標(biāo)明當(dāng)前的網(wǎng)絡(luò)情況不好于个,會將閾值變?yōu)楫?dāng)前的一半。
- 如果出現(xiàn)了丟包情況暮顺,會盡快重傳丟失的包(快重傳)
面向連接
TCP的連接是指的是:通信雙方之間的記錄厅篓,并不是真的去進(jìn)行連接了 ,根據(jù)上面的可靠性我們可以知道拖云,TCP是一個雙工的通信贷笛,發(fā)送發(fā)可以給接收方發(fā)送報文,接收方也可以給發(fā)送方返回報文宙项。它們都維護(hù)了一個緩存區(qū)乏苦,記錄了雙方的發(fā)送信息。
為了記錄信息尤筐,TCP會經(jīng)歷握手的環(huán)節(jié)汇荐。
服務(wù)器A向服務(wù)器B發(fā)送數(shù)據(jù),則它會先經(jīng)歷握手盆繁。
- SYN_SEND, 服務(wù)器A會發(fā)送一個SYN的報文包掀淘,它的假設(shè)它的序號為X,自己進(jìn)入SYN_SEND狀態(tài)
- SYN_RECED油昂。服務(wù)器B接受到服務(wù)器A發(fā)送的SYN報文之后革娄,會立即回復(fù)ACK報文倾贰,發(fā)送序號是x+1,期望接受序號是Y拦惋。
*ESTABLISHED: 當(dāng)服務(wù)器A接受到ACK報文之后匆浙,會向服務(wù)器B也發(fā)送一個ACK報文,發(fā)送序號是y厕妖。服務(wù)器B接受到ACK報文后也會進(jìn)入 ESTABLISHED首尼,它們就可以發(fā)送數(shù)據(jù)進(jìn)行通信了。
斷開連接言秸,同樣的TCP在斷開連接的時候也會經(jīng)歷一些流程软能,也就是我們說的四次揮手。
還是上面的例子举畸,服務(wù)器A要和服務(wù)器B進(jìn)行斷開連接的請求
- FIN_WAIT-1: 服務(wù)器A發(fā)送一個FIN報文給服務(wù)器B查排,代表自己已經(jīng)沒有數(shù)據(jù)發(fā)送了,自身進(jìn)入FIN_WAIT-1狀態(tài)。
- CLOSE_WAIT:服務(wù)器B在接受到服務(wù)器A發(fā)送的FIN字段之后俱恶,自身進(jìn)入CLOSE_WAIT雹嗦,并且會回復(fù)服務(wù)器A一個ACK字段范舀。
- FIN_WAIT-2:服務(wù)器A接受到回復(fù)的ACK報文之后合是,自身會進(jìn)入FIN_WAIT-2狀態(tài)。(這種階段在實際中很少見)
- TIME_WAIT:服務(wù)器B沒有數(shù)據(jù)發(fā)送了锭环,會向服務(wù)器A發(fā)送FIN字段聪全。這時候服務(wù)器A會進(jìn)入TIME_WAIT狀態(tài)。(如果服務(wù)器B同時發(fā)送FIN ACK標(biāo)志位都為1的報文辅辩,會立即進(jìn)入這個狀態(tài)难礼,不會進(jìn)入FIN_WAIT-2)
LAST_ACK:服務(wù)器B發(fā)送FIN字段之后,自身會進(jìn)入LAST_ACK狀態(tài)玫锋。
CLOSE:當(dāng)服務(wù)器A等待2MSL單位時間之后蛾茉,會向服務(wù)器B發(fā)送ACK字段,正式的關(guān)閉連接撩鹿。這個2MSL在官方文檔里面是4分鐘谦炬,Linux很早就改成60s了。
這里我們可以解答開始的問題了节沦,為什么要三次握手键思?
答:因為TCP是雙工的,建立連接的雙方都可以向?qū)Ψ絺鬟f數(shù)據(jù)甫贯,如果只用兩次握手只能保證單方的發(fā)送吼鳞。比如A,B兩次握手,B不向A發(fā)送ACK報文叫搁,B并不能保證它的數(shù)據(jù)一定能發(fā)送到A赔桌。
為什么要四次揮手供炎?
答:因為在對方發(fā)送FIN的時候只能代表對方?jīng)]有數(shù)據(jù)傳遞了,但是接收方還有數(shù)據(jù)要向?qū)Ψ絺鬟f疾党,所以在發(fā)送數(shù)據(jù)完了之后碱茁,再回復(fù)FIN字段,代表數(shù)據(jù)傳輸完畢可以關(guān)閉了仿贬。
為什么要由TIME_WAIT階段:第一個原因是為了確保最后發(fā)送的ACK報文能傳遞成功纽竣,如果失敗,我們在這個時間里面可以進(jìn)行超時重傳茧泪。第二個原因是為了保證后序因為超時重傳的數(shù)據(jù)包都失效蜓氨,不干擾下一次連接。
為什么會有黏包問題队伟?
TCP是面向字節(jié)流穴吹,會把應(yīng)用層傳遞的數(shù)據(jù)變成數(shù)據(jù)流,TCP不懂這些數(shù)據(jù)流的意義嗜侮,他只知道從應(yīng)用層拿到數(shù)據(jù)流港令,切割成一份份報文,然后發(fā)送給目標(biāo)對象锈颗。而如果應(yīng)用層傳輸下來的是兩個數(shù)據(jù)包顷霹,那么極有可能出現(xiàn)這種情況:
- 應(yīng)用層需要向目標(biāo)進(jìn)程發(fā)送兩份數(shù)據(jù),一份音頻击吱,一份文本
- TCP只知道接收到一個流淋淀,并把流拆分成4段進(jìn)行發(fā)送
- 中間第二個報文的數(shù)據(jù)就出現(xiàn)兩個文件的數(shù)據(jù)混在一起,這就是粘包
- 目標(biāo)進(jìn)程應(yīng)用層在接收到數(shù)據(jù)之后覆醇,需要把這些數(shù)據(jù)拆分成正確的兩個文件朵纷,就是拆包
粘包與拆包都是應(yīng)用層需要解決的問題,可以在每個文件的最后附加上一些特殊的字節(jié)永脓,如換行符袍辞;或者控制每個報文只包含一個文件的數(shù)據(jù),不足的用0補(bǔ)充常摧。
UDP
我們都知道TCP還有一個好兄弟搅吁,它就是UDP,UDP只實現(xiàn)了傳輸層的最小功能排宰,他會給應(yīng)用層傳遞的報文添加一個頭部就交給下一層了似芝,并沒有做分包這樣的控制。
頭部
它的頭部很簡單板甘,只包含以下字段:
- 源端口党瓮、目標(biāo)端口:端口號用來區(qū)分主機(jī)的不同進(jìn)程
- 校驗碼:用于校驗數(shù)據(jù)包在傳輸?shù)倪^程中沒有出現(xiàn)錯誤,例如某個1變成了0
- 長度:報文的長度
優(yōu)缺點(diǎn)
由于頭部簡單盐类,也沒有任何的控制措施寞奸,UDP具有以下缺點(diǎn):
- 無法保證消息完整呛谜、正確到達(dá),UDP是一個不可靠的傳輸協(xié)議枪萄;
- 缺少擁塞控制容易互相競爭資源導(dǎo)致網(wǎng)絡(luò)系統(tǒng)癱瘓
但是由于沒有這些機(jī)制的限制隐岛,UDP很放飛自我,它也有一些優(yōu)點(diǎn):
- 效率更快瓷翻;不需要建立連接以及擁塞控制
- 連接更多的客戶聚凹;沒有連接狀態(tài),不需要為每個客戶創(chuàng)建緩存等
- 分組首部字節(jié)少齐帚,開銷卸恃馈;TCP首部固定首部是20字節(jié)对妄,而UDP只有8字節(jié)湘今;更小的首部意味著更大比例的數(shù)據(jù)部分
- 在一些需要高效率允許可限度誤差的場景下可以使用。如直播場景剪菱,并不需要保證每個數(shù)據(jù)包都完整到達(dá)摩瞎,* 允許一定的丟包率,這個時候TCP的可靠特性反而成為了累贅孝常;精簡的UDP更高的效率是更加適合的選擇
- 可以進(jìn)行廣播旗们;UDP并不是面向連接的,所以可以同時對多個進(jìn)程進(jìn)行發(fā)送報文
UDP的適用場景
UDP適用于對傳輸模型需要應(yīng)用層高度自定義茫因、允許出現(xiàn)丟包蚪拦、需要高效率的場景杖剪、需要廣播冻押;例如
- 視屏直播
- DNS
- RIP路由選擇協(xié)議