1.HTTP
當(dāng)我們?yōu)g覽網(wǎng)頁(yè)時(shí),地址欄中使用最多的多是https://開(kāi)頭的url,它與我們所學(xué)的http協(xié)議有什么區(qū)別?
http協(xié)議又叫超文本傳輸協(xié)議,它是應(yīng)用層中使用最多的協(xié)議,
http與我們常說(shuō)的socket有什么區(qū)別嗎?
我們使用的網(wǎng)絡(luò)可以分為(會(huì)話層和表示層可以忽略),每一層使用下一層的功能,并為上一層提供接口,我們經(jīng)常聽(tīng)說(shuō)的http協(xié)議就是應(yīng)用層的協(xié)議,其中應(yīng)用層協(xié)議包括ftp等等,而應(yīng)用層還需要使用傳輸層的協(xié)議,http使用的就是tcp協(xié)議,http3計(jì)劃使用udp協(xié)議。不過(guò)不管是tcp還是udp都是使用網(wǎng)絡(luò)層的ip協(xié)議。不過(guò)傳輸層及以下層都是在操作系統(tǒng)內(nèi)核中的,不利于我們使用,所以在用戶態(tài)設(shè)計(jì)了socket接口來(lái)幫助我們使用tcp或者udp的網(wǎng)絡(luò)協(xié)議。而http協(xié)議則是已經(jīng)定義好解析標(biāo)準(zhǔn)的能讓我們直接使用的協(xié)議。
1.1 http1.0
http1.0是個(gè)短連接,即每發(fā)一次請(qǐng)求,就建立一次連接,一次響應(yīng)后就釋放連接,這種方式簡(jiǎn)化了http的請(qǐng)求響應(yīng),但是卻造成了重大的浪費(fèi):不能并行請(qǐng)求,每次請(qǐng)求都需要建立連接和釋放連接,導(dǎo)致每次請(qǐng)求都需要三次握手和四次揮手。
1.2 http1.1
http1.1為了優(yōu)化http1.0加入了很多東西。
①keep-alive(長(zhǎng)連接):長(zhǎng)連接,每個(gè)連接完成一次請(qǐng)求后先不關(guān)閉,在一定時(shí)間內(nèi)可以發(fā)送多次請(qǐng)求。通過(guò)設(shè)置請(qǐng)求頭Connection: keep-alive來(lái)實(shí)現(xiàn)
http1.1的長(zhǎng)連接減少了三次握手和四次揮手的次數(shù),不過(guò)需要前一次請(qǐng)求的響應(yīng)返回后才能發(fā)送下一次請(qǐng)求,但是有個(gè)問(wèn)題,在不釋放連接的情況下如何才能判斷請(qǐng)求已經(jīng)結(jié)束,例如瀏覽器怎么知道數(shù)據(jù)傳輸完畢了?http1.10的短連接和http1.1的長(zhǎng)連接
- 通過(guò) Content-Length 的長(zhǎng)度信息,判斷響應(yīng)數(shù)據(jù)傳輸完畢。但是如果是大文件或者動(dòng)態(tài)生成的數(shù)據(jù)呢?這些數(shù)據(jù)一開(kāi)始可能不知道它們的長(zhǎng)度,這時(shí)候就需要用到Transfer-Encoding: chunked請(qǐng)求頭,這個(gè)請(qǐng)求頭標(biāo)識(shí)需要響應(yīng)使用數(shù)據(jù)分塊傳輸,并且每個(gè)塊會(huì)有長(zhǎng)度和數(shù)據(jù),如果接收到長(zhǎng)度為0的塊標(biāo)識(shí)數(shù)據(jù)接收完畢。http長(zhǎng)連接? --? http請(qǐng)求與連接的關(guān)系
②pipelining(管道化技術(shù)):雖然長(zhǎng)連接減少了握手消耗,但是每次都要前一次請(qǐng)求完成之后才能發(fā)送下一次請(qǐng)求,管道化技術(shù)允許在一個(gè)請(qǐng)求沒(méi)有響應(yīng)之前發(fā)送多次請(qǐng)求。不過(guò)管道話技術(shù)要求響應(yīng)必須按照請(qǐng)求發(fā)送的順序返回,否則無(wú)法解析。存在隊(duì)頭阻塞。實(shí)際使用不多。
③增加并發(fā)連接數(shù)量與多域名:http1為了實(shí)現(xiàn)多并發(fā),增加http連接的數(shù)量。
2.HTTPS
上面所說(shuō)的http協(xié)議都是明文傳輸?shù)?,而且不?huì)對(duì)應(yīng)用數(shù)據(jù)和主機(jī)進(jìn)行驗(yàn)證,這樣即使數(shù)據(jù)被竊取和篡改我們也不得而知,所以為了保證http的安全,在它的基礎(chǔ)上加了TSL/SSL安全協(xié)議。形成了我們所說(shuō)的https協(xié)議。
先盜一張https驗(yàn)證的過(guò)程圖
微信圖片_20210802184236.jpg
信息的安全傳輸解決三個(gè)問(wèn)題,保密性,完整性與端點(diǎn)鑒別。
(a,b為傳輸方,c為攻擊方)
1.保密性
http之所以不安全最大的原因就是使用明文傳輸,所以加密成了最大的問(wèn)題,目前分為兩大類加密算法(md5由于不能解密,不能算作加密算法)
①對(duì)稱加密:DES(分組加密算法),AES(可以看作是DES的升級(jí)版)
②非對(duì)稱加密:RSA、DSA
對(duì)稱密鑰加密過(guò)程
由于目前公鑰加密算法的開(kāi)銷(xiāo)比較大,所以一般采用對(duì)稱加密來(lái)保密信息。但是對(duì)稱加密過(guò)程,密鑰如何傳輸?這時(shí)可以使用公私鑰來(lái)進(jìn)行傳輸,因?yàn)楣借€使用公鑰加密,私鑰解密,所以只要是對(duì)方公鑰加密的數(shù)據(jù)只有對(duì)方的私鑰才能解密(私鑰不公開(kāi),保證了傳輸過(guò)程中,即使數(shù)據(jù)泄露也沒(méi)辦法解密)對(duì)應(yīng)圖中④的過(guò)程:
note:不過(guò)又出現(xiàn)了一個(gè)問(wèn)題,如何保證a得到的是b的公鑰,而不是別人偽造的,這就需要下面的端點(diǎn)鑒別。
2.完整性
為了防止http數(shù)據(jù)在傳輸?shù)倪^(guò)程中被篡改,完整性也是必須要考慮的問(wèn)題,如何保證信息的完整性。
①發(fā)送方使用自己的私鑰加密信息,接收方接收到后只能使用發(fā)送方的公鑰解密
②使用信息摘要算法md5,sha生成散列加在正文數(shù)據(jù)的后面
使用MAC鑒別碼保證報(bào)文完整
第一種方法由于中間人沒(méi)有發(fā)送方的私鑰所以只能解密發(fā)送傳輸?shù)臄?shù)據(jù),但是不能修改數(shù)據(jù),因?yàn)橐坏┲虚g人修改數(shù)據(jù),接收方利用發(fā)送方的公鑰解密得到的數(shù)據(jù)就是錯(cuò)誤的(信息中有專門(mén)的比特位用于驗(yàn)證信息的正確性),但是向前面說(shuō)的公私鑰加密開(kāi)銷(xiāo)比較大,所以不建議采用。
信息摘要算法是利用散列函數(shù)將信息映射成一段固定長(zhǎng)的數(shù)據(jù),由于該算法具有不可逆性而且不同的數(shù)據(jù)散列得到的數(shù)據(jù)基本不會(huì)相同。所以接收方得到數(shù)據(jù)后,將正文數(shù)據(jù)散列得到的數(shù)據(jù)于散列值比對(duì),如果不同,則正文數(shù)據(jù)遭篡改。
3.端點(diǎn)鑒別
保證了數(shù)據(jù)的完整性和保密性后,最終的問(wèn)題就是如何確定發(fā)送方的問(wèn)題。如何不進(jìn)行端點(diǎn)鑒別,就可能會(huì)出現(xiàn)中間人攻擊的風(fēng)險(xiǎn),如圖中間人攻擊端點(diǎn)A和端點(diǎn)B通信,但是中間人C攔截了請(qǐng)求并冒充B和A通信(中間人C將自己的公鑰發(fā)給A,讓A誤以為這是B的公鑰),然后C作為客戶端向端點(diǎn)B發(fā)請(qǐng)求(將A發(fā)送的報(bào)文解密后,用BC之間的共享密鑰加密),這樣A誤以為是在和B進(jìn)行https加密通信,但是其實(shí)中間人已經(jīng)知曉所有的報(bào)文。那么如何知曉這是公鑰是不是B的?
這就需要用到證書(shū),證書(shū)是CA機(jī)構(gòu)用自己的私鑰加密后的得到的東西,用來(lái)證明公鑰持有者的身份,證書(shū)包括以下東西:
- 持有者信息;
- 證書(shū)認(rèn)證機(jī)構(gòu)(CA)的信息;
- CA 對(duì)這份文件的數(shù)字簽名及使用的算法;
- 證書(shū)有效期以及一些額外信息;
CA是權(quán)威的受信任的證書(shū)頒發(fā)機(jī)構(gòu),CA的公鑰會(huì)內(nèi)嵌在瀏覽器中。由于CA使用自己的私鑰加密證書(shū),所以可以保證證書(shū)不可以偽造。服務(wù)器在發(fā)送公鑰的時(shí)候還要發(fā)送自己的證書(shū),客戶端解密該證書(shū)的時(shí)候?qū)⒆C書(shū)中的公鑰與服務(wù)器發(fā)來(lái)的公鑰比較,如果兩者相同,則直接說(shuō)明服務(wù)器發(fā)來(lái)的公鑰是可靠的;如果不相同說(shuō)明服務(wù)器端身份有問(wèn)題。例如上圖,中間人C將自己的公鑰和證書(shū)發(fā)給端點(diǎn)A,A解密證書(shū),發(fā)現(xiàn)證書(shū)中的網(wǎng)站信息與B不同,說(shuō)明訪問(wèn)的端點(diǎn)不是B。
證書(shū)分為根證書(shū)和中間證書(shū),我們我大部分的證書(shū)都是由中間CA機(jī)構(gòu)頒發(fā)的,中間CA機(jī)構(gòu)是受根CA機(jī)構(gòu)信任的能夠頒發(fā)CA證書(shū)的機(jī)構(gòu),這樣就形成了一個(gè)證書(shū)鏈:客戶端信任操作系統(tǒng)瀏覽器->瀏覽器信任根CA機(jī)構(gòu)->根CA機(jī)構(gòu)信任中間CA機(jī)構(gòu)->中間CA機(jī)構(gòu)信任中間證書(shū)。所以瀏覽器對(duì)于收到的多級(jí)證書(shū),需要從站點(diǎn)證書(shū)開(kāi)始逐級(jí)驗(yàn)證,直至出現(xiàn)操作系統(tǒng)或?yàn)g覽器內(nèi)置的受信任 CA 根證書(shū)。
證書(shū)鏈
2.1TLS的流程
https是在http的基礎(chǔ)上添加tls協(xié)議保證安全性的,那么tls的過(guò)程又是如何呢?SSL/TLS工作原理以及幾幅圖,拿下 HTTPS
tls握手的過(guò)程:這里以比較熟知的RSA密鑰交換算法為例說(shuō)明,下面會(huì)說(shuō)明DHE密鑰交換算法,RSA算法主要分為以下幾步:tls握手的過(guò)程
①Client Hello:握手第一步是客戶端向服務(wù)端發(fā)送 Client Hello 消息,這個(gè)消息里包含了一個(gè)客戶端生成的隨機(jī)數(shù) Random1、客戶端支持的加密套件(Support Ciphers)和 SSL Version 等信息。
②Server Hello:第二步是服務(wù)端向客戶端發(fā)送 Server Hello 消息,這個(gè)消息會(huì)從 Client Hello 傳過(guò)來(lái)的 Support Ciphers 里確定一份加密套件,這個(gè)套件決定了后續(xù)加密和生成摘要時(shí)具體使用哪些算法,另外還會(huì)生成一份隨機(jī)數(shù) Random2。注意,至此客戶端和服務(wù)端都擁有了兩個(gè)隨機(jī)數(shù)(Random1+ Random2),這兩個(gè)隨機(jī)數(shù)會(huì)在后續(xù)生成對(duì)稱秘鑰時(shí)用到。
③Certificate:這一步是服務(wù)端將自己的證書(shū)下發(fā)給客戶端,讓客戶端驗(yàn)證自己的身份,客戶端驗(yàn)證通過(guò)后取出證書(shū)中的公鑰。Server Key Exchange:如果是DH算法,這里發(fā)送服務(wù)器使用的DH參數(shù)。RSA算法不需要這一步。Server Hello Done:通知客戶端 Server Hello 過(guò)程結(jié)束。
④Certificate Verify:客戶端收到服務(wù)端傳來(lái)的證書(shū)后,先從 CA 驗(yàn)證該證書(shū)的合法性,驗(yàn)證通過(guò)后取出證書(shū)中的服務(wù)端公鑰,再生成一個(gè)隨機(jī)數(shù) Random3,再用服務(wù)端公鑰非對(duì)稱加密 Random3生成 PreMaster Key。
⑤Client Key Exchange:上面客戶端根據(jù)服務(wù)器傳來(lái)的公鑰生成了 PreMaster Key,Client Key Exchange 就是將這個(gè) key 傳給服務(wù)端,服務(wù)端再用自己的私鑰解出這個(gè) PreMaster Key得到客戶端生成的 Random3。至此,客戶端和服務(wù)端都擁有 Random1+ Random2+ Random3,兩邊再根據(jù)同樣的算法就可以生成一份秘鑰,握手結(jié)束后的應(yīng)用層數(shù)據(jù)都是使用這個(gè)秘鑰進(jìn)行對(duì)稱加密。為什么要使用三個(gè)隨機(jī)數(shù)呢?這是因?yàn)?SSL/TLS 握手過(guò)程的數(shù)據(jù)都是明文傳輸?shù)?,并且多個(gè)隨機(jī)數(shù)種子來(lái)生成秘鑰不容易被暴力破解出來(lái)。
⑥Change Cipher Spec(Client):這一步是客戶端通知服務(wù)端后面再發(fā)送的消息都會(huì)使用前面協(xié)商出來(lái)的秘鑰加密了,是一條事件消息。Encrypted Handshake Message(Client Finish):客戶端將前面的握手消息生成摘要再用協(xié)商好的秘鑰加密,這是客戶端發(fā)出的第一條加密消息。服務(wù)端接收后會(huì)用秘鑰解密,能解出來(lái)說(shuō)明前面協(xié)商出來(lái)的秘鑰是一致的。
⑦Change Cipher Spec(Server)和Encrypted Handshake Message(Server)與客戶端的意思基本相同,使用前面的加密數(shù)據(jù)和加密之前的握手消息。
2.2https的優(yōu)化
①會(huì)話復(fù)用tls
https為了安全引入了tls,但是一次連接無(wú)形中又增加了tls握手的部分,而且要命的是每次都需要將這個(gè)過(guò)程走一遍。為了復(fù)用TLS過(guò)程,我們引入了session id與sesson ticket機(jī)制 Session會(huì)話恢復(fù)SessionID&SessionTicket
session id
session id是指一次tls握手完成后(第一次握手?jǐn)y帶空的session id,服務(wù)器端就知道首次連接),服務(wù)器端生成session id返回,客戶端存下,而且客戶端會(huì)將生成的密鑰存放在tls層中,接著下次連接時(shí)客戶端攜帶此session id發(fā)起請(qǐng)求,服務(wù)器端收到后與內(nèi)存中的session id比較,如果相同則使用上次的密鑰。發(fā)送一個(gè)Change Cipher Spec報(bào)文后,就發(fā)送加密后的驗(yàn)證信息Finished。session ticket
session ticket與session id的過(guò)程差不多,但是session ticket為了解決有多臺(tái)服務(wù)器的問(wèn)題(服務(wù)器負(fù)載均衡導(dǎo)致兩次請(qǐng)求到不同機(jī)器),所以session ticket是首次tls握手完成后服務(wù)端將加密協(xié)議,算法,參數(shù)等加密生成會(huì)話票據(jù),服務(wù)器不存儲(chǔ),傳給客戶端存儲(chǔ),等客戶端下次tls握手時(shí),服務(wù)器驗(yàn)證票據(jù)并根據(jù)票據(jù)生成加密密鑰,同時(shí)生成新的NewSessionTicket防止過(guò)期。
②前向安全算法
普通的密鑰交換算法(rsa)需要使用公私鑰傳輸加密密鑰,如果私鑰一旦泄露,那么中間人就可以通過(guò)私鑰解密出加密密鑰當(dāng)前會(huì)話以及后續(xù)會(huì)話就不安全了。為了安全性,設(shè)計(jì)出了ECDHE(DHE)算法TLS/SSL 協(xié)議中的RSA、ECDHE、ECDH流程與區(qū)別。
RSA算法DHE算法流程
上面那張圖是RSA算法交換加密密鑰的過(guò)程,下面的圖則是ECDHE算法交換密鑰的過(guò)程,ECDHE與DHE的過(guò)程相同,只是加密算法不同而已,下面以DHE算法流程為例說(shuō)明:
(1):客戶端發(fā)送client hello申請(qǐng)DHE加密。
(2):服務(wù)器允許DHE算法并生成加密使用的參數(shù)p,q,然后服務(wù)器生成隨機(jī)數(shù)Xb作為自己的臨時(shí)私鑰,接著計(jì)算Pb = q^Xb mod p,最后發(fā)送sever hello,和server key exchange(包括Pb,p, q等加密參數(shù))至客戶端,Xb僅自己保存。
(3):客戶端收到Pb后,生成自己的臨時(shí)私鑰Xa,接著計(jì)算Pa = q^Xa mod p,然后將Pa發(fā)送給服務(wù)器端,客戶端發(fā)送client key exchange (包括Pa)至服務(wù)器,
(4):客戶端計(jì)算Sa=Pb ^Xa mod p;服務(wù)器收到Pa后計(jì)算Sb= Pa^Xb mod p,DHE與ECDHE算法使用離散對(duì)數(shù)的概念保證了Sa與Sb的相同和高度保密性,所以密鑰交換成功,最后雙方通過(guò)S加解密數(shù)據(jù)。算法的加密過(guò)程可以查看DH密鑰交換算法,DHE與DH不同的是DHE使用臨時(shí)的私鑰,這樣可以保證即使一次通信密鑰被破解,但是其他的通信仍是安全的。
③TFO(tcp fast open)TFO詳解
傳統(tǒng)的http通信需要先進(jìn)性3次握手后才能發(fā)送數(shù)據(jù),但是為了提高速度,一般在第三次握手時(shí)會(huì)攜帶應(yīng)用數(shù)據(jù),即使服務(wù)器沒(méi)有收到,客戶端也可以在超時(shí)后重發(fā),但是每次http通信還是需要浪費(fèi)1RTT的時(shí)間?左邊是tcp三次握手發(fā)送數(shù)據(jù),右邊是TFO應(yīng)用流程
如圖為了彌補(bǔ)這種浪費(fèi),人們?cè)O(shè)計(jì)出了TFO通信方式,在第一次通信時(shí)按照正常的3次握手來(lái)完成,同時(shí)第一次通信完成后服務(wù)器端會(huì)發(fā)送cookie給客戶端,接下來(lái)的tcp通信客戶端會(huì)發(fā)送cookie與應(yīng)用數(shù)據(jù),這個(gè)過(guò)程就和上面的tsl復(fù)用的思路一樣。
- 如果cookie驗(yàn)證成功,則將應(yīng)用數(shù)據(jù)上傳給應(yīng)用程序,并且服務(wù)器端會(huì)發(fā)送客戶端syn和應(yīng)用數(shù)據(jù)確認(rèn)的ack;
- 如果cookie驗(yàn)證不成功,則服務(wù)器會(huì)將數(shù)據(jù)丟棄,然后只發(fā)送syn確認(rèn)的ack,客戶端通過(guò)服務(wù)器端的ack就可以知道cookie是否驗(yàn)證成功,如果不成功,則客戶端第三次握手的時(shí)候再發(fā)送一遍應(yīng)用數(shù)據(jù),這就又回到了沒(méi)有TFO的過(guò)程。
④hsts(HTTP Strict-Transport-Security)HSTS詳解-CSDN博客
在訪問(wèn)網(wǎng)站的時(shí)候我們經(jīng)常會(huì)直接敲域名www.baidu.com,而不會(huì)添加http://或者h(yuǎn)ttps://,而瀏覽器為了兼容http會(huì)先使用http詢問(wèn)是否能夠進(jìn)行https請(qǐng)求,如果瀏覽器只支持https會(huì)返回3XX狀態(tài)碼表示路徑已經(jīng)改變。然后瀏覽器才會(huì)使用新的https://開(kāi)頭的域名訪問(wèn)server,但是這樣每次都會(huì)多發(fā)一次請(qǐng)求,而且還會(huì)造成中間人攻擊
hsts就是為了解決一個(gè)問(wèn)題,當(dāng)訪問(wèn)過(guò)一次server之后,server會(huì)返回http->可能發(fā)生的中間人攻擊
Strict-Transport-Security: max-age=31536000; includeSubDomains,這個(gè)響應(yīng)行表示如果瀏覽器接收到使用 HTTP 加載資源的請(qǐng)求,則必須嘗試使用 HTTPS 請(qǐng)求替代。 如果 HTTPS 不可用,則必須直接終止連接;max-age是強(qiáng)制執(zhí)行的時(shí)間,這里是一年,只要一年時(shí)間內(nèi)瀏覽器再次訪問(wèn)這個(gè)域名,就可以再次強(qiáng)制執(zhí)行一年;includeSubDomains表示當(dāng)前域名及其子域名均開(kāi)啟HSTS保護(hù);而且對(duì)于tls握手過(guò)程中有問(wèn)題的證書(shū)也會(huì)禁止訪問(wèn),hsts不僅提升了訪問(wèn)速度,而且增強(qiáng)了安全性。
TLS1.3
TLS1.3相對(duì)于之前的版本有了更加安全和高效的密鑰交換流程,以下是截取tls1.3相比之前協(xié)議的改變
接下來(lái)具體說(shuō)一下tls的哪些改變,詳細(xì)的過(guò)程可以參考tls詳解:tls1.3相比以前版本的改變
①1RTT的tls連接時(shí)間左邊是tls1.3的一般流程,右邊tls1.3的會(huì)話復(fù)用流程tls1.3還廢除了rsa算法,因?yàn)樗恢С智跋虬踩赃@里使用DHE算法作為講解。wires hark抓包的new session ticket
- 客戶端發(fā)送client hello,支持的加密套件,(該作用和之前一樣)、supproted_versions 拓展(包含自己支持的TLS協(xié)議版本號(hào))、supproted_groups 拓展(表示自己支持的橢圓曲線類型)、key_share拓展(包含客戶端利用supprot_groups中各橢圓曲線算法生成的public key)
wireshark捕獲的clienthello包- server 發(fā)送Server Hello,攜帶如下幾個(gè)重要信息supproted_versions 拓展(包含自己從client的supproted_versions中選擇的TLS協(xié)議版本號(hào))、key_share拓展(包含自己選中的橢圓曲線,以及自己計(jì)算出來(lái)的公鑰,接著發(fā)送Change Ciper Spec和Finished(與tls1.2相同)
wireshark捕獲的server hello包tls1.3相比1.2的DHE算法減少了一個(gè)RTT,原因在于tls再client hello階段就計(jì)算出了所有橢圓曲線算法的公鑰供server選擇。
②0RTT的會(huì)話復(fù)用
與上圖tls1.2的會(huì)話復(fù)用相同,tls1.3的會(huì)話復(fù)用使用了相同的sessionid和ticket機(jī)制,也是1RTT的時(shí)間。但是tls1.3的會(huì)話復(fù)用還可以實(shí)現(xiàn)0RTT的連接,具體做法是:
- client hello中除發(fā)送上面的信息外還需要額外發(fā)送:psk_key_exchange_modes(psk的交換模式)、pre_shared_key拓展(將之前握手后發(fā)送的ticket處理之后形成的預(yù)共享密鑰,簡(jiǎn)稱psk,由于server可能會(huì)發(fā)送多次,所以可能會(huì)出現(xiàn)多個(gè)),early_data拓展(server是否支持0RTT的連接)以及應(yīng)用數(shù)據(jù)。
- server hello會(huì)有多種情況,如果server驗(yàn)證ticket成功并且愿意0RTT連接,那么sever hello會(huì)發(fā)送Encrypted Extensions,里面攜帶了early data拓展表示自己會(huì)讀取early data,說(shuō)明0RTT成功;如果 server沒(méi)有配置讀取early data的選項(xiàng),但是ticket驗(yàn)證成功,那么server就會(huì)忽略client發(fā)送的Application data,這樣只表示會(huì)話復(fù)用成功,但不立刻接收數(shù)據(jù),需要1RTT;如果ticket驗(yàn)證失敗,則回到了上面tls1.3 1RTT建立連接的過(guò)程。
0RTT的會(huì)話復(fù)用
3.HTTP/2
HTTP/2是對(duì)之前HTTP協(xié)議的擴(kuò)展,而非替代,HTTP 方法、狀態(tài)代碼、URI 和標(biāo)頭字段等這些核心概念不變,只是傳輸過(guò)程做了優(yōu)化。http1為了實(shí)現(xiàn)請(qǐng)求的多并發(fā),只能創(chuàng)建多個(gè)連接和域名分片上下手,但是六個(gè)瀏覽器都有自己的最大連接數(shù),這種策略不能從根本上解決問(wèn)題;而且創(chuàng)建多個(gè)連接也會(huì)增加握手消耗。
①ALPN協(xié)議:http2設(shè)計(jì)的時(shí)候?yàn)榱丝紤]兼容性的問(wèn)題,需要客戶端對(duì)服務(wù)器發(fā)起一次詢問(wèn),但是這樣浪費(fèi)一次RTT,為了減少浪費(fèi),客戶端會(huì)在client hello時(shí)加上http詢問(wèn)請(qǐng)求,這樣做不僅減少了浪費(fèi),還達(dá)到了h2強(qiáng)制使用tls的目的,確保通信安全,這種機(jī)制叫做ALPN(Application Layer Protocol Negotiation)wireshark捕獲的alpn協(xié)議
②二進(jìn)制分幀:h2為了應(yīng)對(duì)http1中的隊(duì)頭阻塞問(wèn)題什么是http隊(duì)頭阻塞和tcp隊(duì)頭阻塞以及如何解決,設(shè)計(jì)出了二進(jìn)制分幀技術(shù),http1中的數(shù)據(jù)都是通過(guò)文本文本傳輸?shù)?,但是二進(jìn)制結(jié)構(gòu)更有利于數(shù)據(jù)處理,因?yàn)橛?jì)算機(jī)只認(rèn)識(shí)二進(jìn)制,所以傳輸時(shí)不需要文本與二進(jìn)制之間的轉(zhuǎn)換;http1中的管道技術(shù)雖然支持反多次請(qǐng)求,但是響應(yīng)必須按照FIFO的順序返回,不能實(shí)現(xiàn)真正的并發(fā),所以存在隊(duì)頭阻塞問(wèn)題。
針對(duì)這個(gè)問(wèn)題,h2將每個(gè)響應(yīng)分成若干個(gè)幀序列,而每個(gè)幀序列都有流號(hào),標(biāo)記屬于哪個(gè)響應(yīng),這樣即使每個(gè)幀序列亂序到客戶端,還是能夠根據(jù)流id重新組裝,解決了http方面的隊(duì)頭阻塞,真正實(shí)現(xiàn)多路復(fù)用。
例如下圖,客戶端請(qǐng)求first函數(shù)和second函數(shù),響應(yīng)便將每個(gè)函數(shù)分割成若干個(gè)幀序列,然后填寫(xiě)流id和長(zhǎng)度(方便客戶端組裝)。其中響應(yīng)頭會(huì)生成headers frame,請(qǐng)求行會(huì)生成data frame(除此之外還有其他種類的幀負(fù)責(zé)標(biāo)記幀的用途)aa+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | Type (8) | Flags (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+======================================================>=======+ | Frame Payload (0...) ... +---------------------------------------------------------------+
Length代表整個(gè) frame 的長(zhǎng)度,用一個(gè) 24 位無(wú)符號(hào)整數(shù)表示,頭部的 9 字節(jié)不算在這個(gè)長(zhǎng)度里。Type定義 frame 的類型,用 8 bits 表示。幀類型決定了幀主體的格式和語(yǔ)義,如果 type 為 unknown 應(yīng)該忽略或拋棄。Flags是為幀類型相關(guān)而預(yù)留的布爾標(biāo)識(shí)。標(biāo)識(shí)對(duì)于不同的幀類型賦予了不同的語(yǔ)義。如果該標(biāo)識(shí)對(duì)于某種幀類型沒(méi)有定義語(yǔ)義,則它必須被忽略且發(fā)送的時(shí)候應(yīng)該賦值為 (0x0)R是一個(gè)保留的比特位。這個(gè)比特的語(yǔ)義沒(méi)有定義,發(fā)送時(shí)它必須被設(shè)置為 (0x0), 接收時(shí)需要忽略- Stream Identifier 用作流控制,用 31 位無(wú)符號(hào)整數(shù)表示。客戶端建立的 sid 必須為奇數(shù),服務(wù)端建立的 sid 必須為偶數(shù),值 (0x0) 保留給與整個(gè)連接相關(guān)聯(lián)的幀 (連接控制消息),而不是單個(gè)流
-Frame Payload是主體內(nèi)容,由幀類型決定。幀類型主要有以下幾種:
HEADERS: 報(bào)頭幀 (type=0x1),用來(lái)打開(kāi)一個(gè)流或者攜帶一個(gè)首部塊片段DATA: 數(shù)據(jù)幀 (type=0x0),裝填主體信息,可以用一個(gè)或多個(gè) DATA 幀來(lái)返回一個(gè)請(qǐng)求的響應(yīng)主體PRIORITY: 優(yōu)先級(jí)幀 (type=0x2),指定發(fā)送者建議的流優(yōu)先級(jí),可以在任何流狀態(tài)下發(fā)送 PRIORITY 幀,包括空閑 (idle) 和關(guān)閉 (closed) 的流PUSH_PROMISE: 推送幀 (type=0x5),服務(wù)端推送,客戶端可以返回一個(gè) RST_STREAM 幀來(lái)選擇拒絕推送的流RST_STREAM: 流終止幀 (type=0x3),用來(lái)請(qǐng)求取消一個(gè)流,或者表示發(fā)生了一個(gè)錯(cuò)誤,payload 帶有一個(gè) 32 位無(wú)符號(hào)整數(shù)的錯(cuò)誤碼 (Error Codes),不能在處于空閑 (idle) 狀態(tài)的流上發(fā)送 RST_STREAM 幀SETTINGS: 設(shè)置幀 (type=0x4),設(shè)置此連接的參數(shù),作用于整個(gè)連接,設(shè)置流量控制窗口的大小PING: PING 幀 (type=0x6),判斷一個(gè)空閑的連接是否仍然可用,也可以測(cè)量最小往返時(shí)間 (RTT)GOAWAY: GOWAY 幀 (type=0x7),用于發(fā)起關(guān)閉連接的請(qǐng)求,或者警示嚴(yán)重錯(cuò)誤。GOAWAY 會(huì)停止接收新流,并且關(guān)閉連接前會(huì)處理完先前建立的流WINDOW_UPDATE: 窗口更新幀 (type=0x8),用于執(zhí)行流量控制功能,可以作用在單獨(dú)某個(gè)流上 (指定具體 Stream Identifier) 也可以作用整個(gè)連接 (Stream Identifier 為 0x0),只有 DATA 幀受流量控制影響。初始化流量窗口后,發(fā)送多少負(fù)載,流量窗口就減少多少,如果流量窗口不足就無(wú)法發(fā)送,WINDOW_UPDATE 幀可以增加流量窗口大小。note:數(shù)據(jù)優(yōu)先級(jí):
http2可以進(jìn)行幀的多路復(fù)用,但是某些文件可能需要盡快處理?為此http2設(shè)計(jì)了數(shù)據(jù)幀優(yōu)先級(jí):通過(guò)幀之間的依賴關(guān)系和權(quán)重值來(lái)分配資源帶寬。例如上圖示例Ⅰ,可知幀AB處于同一級(jí)別,但是權(quán)重不同,所以幀A將分配到3/4的帶寬,而幀B只分配到1/4的帶寬。幀的依賴關(guān)系與權(quán)重
示例Ⅱ說(shuō)明,D是C的父級(jí),所以先分配D的帶寬,再分配C的帶寬。
示例Ⅲ說(shuō)明,先D的帶寬,再分配C的帶寬,最后根據(jù)權(quán)重分配AB的帶寬。
Note:但是數(shù)據(jù)流依賴關(guān)系和權(quán)重只表示傳輸優(yōu)先級(jí),而不是一種必要行為,因此不能保證特定的處理或傳輸順序。因?yàn)槲覀儾幌M趦?yōu)先級(jí)較高的資源受到阻止時(shí),還阻止服務(wù)器處理優(yōu)先級(jí)較低的資源。③頭部壓縮:h2的另一個(gè)優(yōu)勢(shì)便是頭部壓縮,在大多數(shù)時(shí)候,我們請(qǐng)求或者響應(yīng)的數(shù)據(jù)可能很少(例如ajax請(qǐng)求),但是同樣需要傳輸各種請(qǐng)求頭,尤其是cookie。這無(wú)疑給http帶來(lái)負(fù)擔(dān),而頭部壓縮就是解決重復(fù)請(qǐng)求頭發(fā)送的問(wèn)題。為了方便,所以直接使用鏈接中的圖片 HTTP/2 頭部壓縮技術(shù)介紹
正如圖中所示,左邊是請(qǐng)求頭,中間分為上下兩部分,上面是靜態(tài)表(將常見(jiàn)的請(qǐng)求頭編碼),下面是動(dòng)態(tài)表(請(qǐng)求響應(yīng)時(shí)動(dòng)態(tài)添加的內(nèi)容,如cookies)(兩個(gè)表使用連續(xù)的空間,所以畫(huà)在了一起)右邊是根據(jù)頭部壓縮得到的編碼。頭部壓縮每個(gè)字節(jié)都表示不同的含義,根據(jù)字節(jié)前面的標(biāo)識(shí)就可以分辨是哪種請(qǐng)求頭。下面講解一下頭部壓縮的編解碼大致原理:頭部壓縮示意圖
- 請(qǐng)求頭在靜態(tài)索引表或者動(dòng)態(tài)表中的,如method:get或者path:/或者動(dòng)態(tài)表添加的內(nèi)容等,可以直接使用索引表示。索引表中的內(nèi)容以1開(kāi)始,剩下的七位表示索引表中的條目,這樣只用1個(gè)字節(jié)就可以表示原來(lái)很多字節(jié)的內(nèi)容。
0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 1 | Index (7+) | +---+---------------------------+請(qǐng)求頭根據(jù)索引表編碼
- 請(qǐng)求頭不在字典表中,但是需要添加到動(dòng)態(tài)表中的,使用“01”開(kāi)頭,后面如果是“000000”,說(shuō)明請(qǐng)求頭參數(shù)名不在字典中,否則可以根據(jù)索引值在靜態(tài)或者動(dòng)態(tài)表中找到條目。接著后跟的字節(jié)標(biāo)識(shí)請(qǐng)求頭參數(shù)值,H為1表示使用哈夫曼編碼,否則表示使用字符串編碼。如下圖選中的字節(jié)“0110000”表示不在表中,但是請(qǐng)求頭參數(shù)名在字典中,為32表示cookie;剩下的字節(jié)“10011100”表示參數(shù)值使用哈夫曼編碼,長(zhǎng)度是28,而開(kāi)頭字母u的哈夫曼編碼表示為“101101”,正好與接下來(lái)的序列相符,這里我們看出頭部壓縮的好處。
字符串的哈夫曼編碼可能不是以整八位比特位結(jié)束的,這就需要在哈夫曼編碼的最后添加“EOS(填充1)”結(jié)束標(biāo)記。(哈夫曼編碼是根據(jù)各字符經(jīng)常使用頻率而創(chuàng)造的序列,可以減少編碼量,如字符a需要使用1個(gè)字節(jié),但哈夫曼編碼“00011”即可表示它),一般我們也會(huì)看到十六進(jìn)制表示的請(qǐng)求頭,這是由二進(jìn)制編碼轉(zhuǎn)換而來(lái)的。哈夫曼編碼表//請(qǐng)求頭的參數(shù)名在字典中,但是參數(shù)值不在表中 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 1 | Index (6+) | +---+---+-----------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+ //請(qǐng)求頭的參數(shù)名和參數(shù)值都不在表中 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 1 | 0 | +---+---+-----------------------+ | H | Name Length (7+) | +---+---------------------------+ | Name String (Length octets) | +---+---------------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+需要添加到動(dòng)態(tài)表中的請(qǐng)求頭編碼
- 請(qǐng)求頭不在字典中,但是不添加到動(dòng)態(tài)表中,使用“0001”開(kāi)頭,大致過(guò)程與第二種情況相同。
//請(qǐng)求頭的參數(shù)名在字典中,但是參數(shù)值不在表中 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 1 | Index (4+) | +---+---+-----------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+ ///請(qǐng)求頭的參數(shù)名和參數(shù)值都不在表中 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 1 | 0 | +---+---+-----------------------+ | H | Name Length (7+) | +---+---------------------------+ | Name String (Length octets) | +---+---------------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+請(qǐng)求頭不在字典中,并且不添加動(dòng)態(tài)表中但是需要說(shuō)明的是,如果請(qǐng)求頭參數(shù)名在字典中的索引很大,比如1234怎么辦,我們這種結(jié)構(gòu)只能存儲(chǔ)最大值為15的索引?這就需要用到前綴編碼,以第三種情況眾多的四位前綴編碼為例,存儲(chǔ)x = 1234過(guò)程,這個(gè)過(guò)程與十進(jìn)制轉(zhuǎn)換二進(jìn)制的過(guò)程有些相似:
- 首先將最大值1111,填入后四位;那么x = x-15 = 1219;
- 接著需要將剩余的部分填入接下來(lái)的字節(jié)中,計(jì)算x = x%128=67(為什么使用128而不是256,因?yàn)樾枰晃粯?biāo)識(shí)這個(gè)數(shù)據(jù)是否結(jié)束,因?yàn)閿?shù)據(jù)可能需要填入多個(gè)字節(jié)中,為0則表示數(shù)據(jù)結(jié)束。)需要將67填到下個(gè)字節(jié)的剩下七位中。接著計(jì)算x = x/128=9;
- x = 9<128,數(shù)據(jù)計(jì)算結(jié)束,將下個(gè)字節(jié)的首位置零(標(biāo)識(shí)這個(gè)數(shù)據(jù)一表示完成),剩下7位填入9;得到的序列就是 0001 1111,1100 0011,0000 1001。解碼時(shí)可以將后兩個(gè)字節(jié)去除首部顛倒順序排列,再加上1111就可以得到1234。 計(jì)算代碼如下。
//編碼過(guò)程 if I < 2^N - 1, encode I on N bits else encode (2^N - 1) on N bits I = I - (2^N - 1) while I >= 128 encode (I % 128 + 128) on 8 bits I = I / 128 encode I on 8 bits //解碼過(guò)程 decode I from the next N bits if I < 2^N - 1, return I else M = 0 repeat B = next octet I = I + (B & 127) * 2^M M = M + 7 while B & 128 == 128 return I-還有一種請(qǐng)求頭不在表中,但是絕對(duì)不添加索引的首部;這種和第三種情況相同,唯一不同的是,這種情況會(huì)作用于網(wǎng)絡(luò)中的每一跳(每一個(gè)路由設(shè)備)如果中間通過(guò)代理,代理必須原樣轉(zhuǎn)發(fā)不能另行編碼。而上一種首部只是作用當(dāng)前跳,通過(guò)代理后可能會(huì)被重新編碼
④Server Push
在http1中,上一個(gè)響應(yīng)發(fā)送完了,服務(wù)器才能發(fā)送下一個(gè)請(qǐng)求,但在http2里,可以將多個(gè)響應(yīng)一起發(fā)送。例如在一個(gè)html文件中有個(gè)多js,css文件,為了快速解析頁(yè)面,可以直接向客戶端推送js文件,從而減少客戶端請(qǐng)求的時(shí)間。http2多路復(fù)用的好處:
- 連接少,握手成本就少。
- 壓縮比高,因?yàn)橐粋€(gè)連接上積累的信息很多,壓縮比會(huì)更高。
- 更好地利用 TCP 的特性,為什么?因?yàn)?TCP 的擁塞控制流量控制都是基于單個(gè)連接的,如果使用很多個(gè)連接,特別是在網(wǎng)絡(luò)擁塞的情況下,會(huì)放大擁塞的系數(shù),加劇網(wǎng)絡(luò)擁塞,如果使用一個(gè)連接,當(dāng)?shù)弥摯翱谝呀?jīng)擁塞,響應(yīng)很慢便不會(huì)繼續(xù)發(fā)送了,但是多個(gè)連接的情況,可能大家都會(huì)嘗試發(fā)一下,而導(dǎo)致加劇網(wǎng)絡(luò)擁塞。
- 使用更少的域名,這也是為了更好地使用連接,并且減少了 DNS 解析時(shí)間。
http2存在的缺陷:http2雖然實(shí)現(xiàn)了多路復(fù)用,但是并沒(méi)有解決tcp方面的隊(duì)頭阻塞。而且,同一個(gè)連接內(nèi)多個(gè)流傳輸,可能會(huì)導(dǎo)致RTT時(shí)間變長(zhǎng),相比http1容易出現(xiàn)超時(shí)重傳;再者,h2適合于大量冗余的請(qǐng)求,對(duì)于少量請(qǐng)求并沒(méi)有太大優(yōu)勢(shì)。
4.HTTP3
前面提過(guò)http2雖然解決了http方面的隊(duì)頭阻塞,但是并沒(méi)有解決傳輸層的隊(duì)頭阻塞(tcp層),那么tcp層的隊(duì)頭阻塞是什么意思?
tcp是可靠的傳輸層協(xié)議,所以我們的http以及ftp都是用tcp作為傳輸層協(xié)議,但是也是因?yàn)閠cp的可靠傳輸造就了tcp的隊(duì)頭阻塞問(wèn)題;因?yàn)閠cp為了保證數(shù)據(jù)準(zhǔn)確且有序的發(fā)送到接收端,使用了滑動(dòng)窗口協(xié)議。
滑動(dòng)窗口協(xié)議:發(fā)送端會(huì)為發(fā)送的數(shù)據(jù)標(biāo)上序列號(hào)(便于接收端組裝處理),接收端接收只有接收到按需到達(dá)的數(shù)據(jù)后才能交給應(yīng)用層;如果有發(fā)送報(bào)文丟失,那么未按序列到達(dá)的報(bào)文只能等待之前序列號(hào)的報(bào)文到達(dá)才行。例如,發(fā)送依次發(fā)送1,2,3,4,5,6六段報(bào)文,到達(dá)順序?yàn)?,2,4,3,6,5報(bào)文丟失,那么12報(bào)文按序到達(dá)可以直接交給應(yīng)用層處理,4報(bào)文需要等到3報(bào)文到達(dá)后才能交給應(yīng)用層,6報(bào)文屬于未按序列到達(dá),此時(shí)只能等到5報(bào)文超時(shí)重傳才能向上傳遞。所以,即使http2使用流分幀技術(shù),但是tcp層還是需要一個(gè)個(gè)的按序傳輸才行。
除此之外,為了提高了網(wǎng)絡(luò)傳輸?shù)男阅?,人們開(kāi)始研究新型的http協(xié)議。如果還從tcp協(xié)議入手,不僅不能解決http2的隊(duì)頭阻塞,還有一些其他的問(wèn)題:滑動(dòng)窗口協(xié)議- 中間設(shè)備的僵化,一些路由器、網(wǎng)關(guān)或者防火墻默認(rèn)tcp協(xié)議的端口和選項(xiàng)導(dǎo)致tcp無(wú)法出現(xiàn)使用新的格式。
- 操作系統(tǒng)的僵化,tcp協(xié)議棧是屬于內(nèi)核的一部分,我們只能使用了socket接口。一旦修改tcp,可以需要修改操作系統(tǒng)的代碼。
- 握手時(shí)間長(zhǎng),tcp建立連接總是需要三次握手,四次揮手,這樣無(wú)形中增加了浪費(fèi)。
udp可以很好的避開(kāi)上面的所有問(wèn)題,所以新的http3協(xié)議使用udp為傳輸層協(xié)議,但是udp是不可靠的傳輸層協(xié)議,所以就需要自己實(shí)現(xiàn)tcp的流量控制和擁塞控制。
4.1http3還是比http2的優(yōu)點(diǎn)
- 更快的連接時(shí)延
http1一次tcp握手,兩次tls握手,至少需要三次握手才能發(fā)送數(shù)據(jù);TLS1.3可以實(shí)現(xiàn)兩次握手就發(fā)送數(shù)據(jù);到后來(lái)的TFO+TLS1.3的session reuse才能真正實(shí)現(xiàn)0RTT的握手連接,但是這需要通信雙發(fā)都支持TFO,TLS1.3而且是session復(fù)用的情況下。
而udp不需要握手就可以直接發(fā)送數(shù)據(jù),再加上支持tls1.3,所以可以實(shí)現(xiàn)1RTT或者0RTT的連接。
- 沒(méi)有tcp的隊(duì)頭阻塞
沒(méi)有使用tcp,自然也不會(huì)有tcp的隊(duì)頭阻塞,但是如何保證數(shù)據(jù)有序到達(dá)呢?
http3使用http2中流的思想,在應(yīng)用層和傳輸層加了個(gè)quic協(xié)議層,將從http3應(yīng)用層收到的數(shù)據(jù)封裝成quic幀,每幀都有流id和字節(jié)長(zhǎng)度。這樣接收到數(shù)據(jù)后就可以根據(jù)流id和字節(jié)長(zhǎng)度重新拼接數(shù)據(jù)。同樣的,一個(gè)連接可以建立多個(gè)流,而每個(gè)流可都以不影響的發(fā)送各自的幀,因?yàn)閡dp不需要整個(gè)字節(jié)序列按序達(dá)到就可以交給quic層,而quic則可以根據(jù)流id組裝數(shù)據(jù)。
http2與http3的協(xié)議框架
- 加密的報(bào)文頭部
tcp使用tls雖然能加密報(bào)文數(shù)據(jù),但是tcp首部沒(méi)有加密。所以在傳輸過(guò)程中很容易被中間網(wǎng)絡(luò)設(shè)備篡改,注入和竊聽(tīng)。比如修改序列號(hào)、滑動(dòng)窗口。quic 的 packet 可以說(shuō)是武裝到了牙齒。除了個(gè)別報(bào)文外所有報(bào)文頭部都是經(jīng)過(guò)認(rèn)證的,報(bào)文 Body 都是經(jīng)過(guò)加密的。
- 連接遷移
什么叫連接遷移?就是當(dāng)網(wǎng)絡(luò)環(huán)境發(fā)生變化時(shí),這條連接不中斷。一般我們使用手機(jī)上網(wǎng)時(shí),可能會(huì)因?yàn)橐苿?dòng)或者切換網(wǎng)絡(luò)而導(dǎo)致IP地址變化,這樣我們又需要與原來(lái)的服務(wù)器進(jìn)行重新連接。而我們的請(qǐng)求可能也需要重發(fā)。又比如,我們使用一個(gè)公共ip上網(wǎng)時(shí),可能會(huì)因?yàn)楦?jìng)爭(zhēng)需要重新進(jìn)行端口映射。這是因?yàn)閭鹘y(tǒng)網(wǎng)絡(luò)使用四元組(源ip,目的ip,源端口,目的端口)來(lái)辨別連接,http3不使用四元組,而是用conntion id來(lái)標(biāo)識(shí)連接。即使ip或者端口發(fā)生變化,連接也不會(huì)中斷,上層邏輯不感知變化。
4.2擁塞控制 [QUIC-流量控制]
擁塞控制是為了防止過(guò)多的數(shù)據(jù)進(jìn)入網(wǎng)絡(luò)導(dǎo)致網(wǎng)絡(luò)阻塞,網(wǎng)絡(luò)數(shù)據(jù)丟失率高(例如中間設(shè)備未能及時(shí)的接收數(shù)據(jù)包而丟棄)
http3的udp并沒(méi)有完成擁塞控制算法,這就需要我們自己實(shí)現(xiàn)與擁塞控制相同的功能。雖然工作量多了,但是現(xiàn)在設(shè)計(jì)的擁塞控制功能可以使用最新的算法,彌補(bǔ)或者避免過(guò)去tcp的缺陷。
①可插拔的算法設(shè)計(jì):
- 我們可以在用戶層實(shí)現(xiàn)自己的擁塞控制算法,可以很方便的升級(jí)和修改配置,不需要改變內(nèi)核部分。
- 不同應(yīng)用程序的不同連接可以配置不同的擁塞控制算法,更精準(zhǔn)有效。
②避免RTT計(jì)算的歧義
RTT是一次數(shù)據(jù)在客戶端接收端往返的時(shí)間,它會(huì)根據(jù)上次數(shù)據(jù)的往返時(shí)間(從發(fā)送數(shù)據(jù)開(kāi)始到接收到數(shù)據(jù)的Ack為止)不斷更新。但是如果發(fā)生重傳,原來(lái)數(shù)據(jù)的Ack與重傳數(shù)據(jù)Ack的序列號(hào)相同,這就會(huì)在計(jì)算RTT的時(shí)候就會(huì)發(fā)生歧義。無(wú)論以哪個(gè)計(jì)算都不能確定RTT一定是準(zhǔn)確的。為了解決這個(gè)問(wèn)題,http3在每個(gè)quic幀上都添加了自增的Packet Number,這樣就可以區(qū)分到底是哪個(gè)數(shù)據(jù)的ack了。但是單純依靠嚴(yán)格遞增的 Packet Number 肯定是無(wú)法保證數(shù)據(jù)的順序性和可靠性。QUIC 又引入了一個(gè) Stream Offset 的概念。quic幀可以依靠 Stream 的 Offset 來(lái)保證應(yīng)用數(shù)據(jù)的順序,即使發(fā)生重傳,幀也可以根據(jù)offset進(jìn)行排序。
包發(fā)生重傳時(shí)RTT可能出現(xiàn)的歧義
④更多的sack塊
如果幀沒(méi)有按序到達(dá),為了減少重傳的幀序列,會(huì)在tcp幀的首部增加已經(jīng)到達(dá)的幀的范圍,例如,1,2,3,4,5,如果1245到達(dá),3缺失,在發(fā)送ack幀時(shí)就可以選擇發(fā)送2的確認(rèn)ack以及4至5的sack。這就告訴發(fā)送端幀2收到,可以從幀3發(fā)送,但是幀45已經(jīng)收到,不需要再發(fā)送了。原來(lái)的tcp首部由于長(zhǎng)度的限制最多填寫(xiě)3個(gè)幀范圍。而現(xiàn)在的quic首部可以填寫(xiě)更多sack幀范圍。
⑤不允許Reneging
什么叫 Reneging 呢?就是接收方已經(jīng)接收并且上報(bào)給 SACK 選項(xiàng)的內(nèi)容 ,但是接收方因?yàn)榉?wù)器資源有限,比如 Buffer 溢出,內(nèi)存不夠等情況而丟棄數(shù)據(jù),這種方式在http3中不被允許,可以減少干擾。
4.3流量控制 QUIC-流量控制
流量控制用于解決發(fā)送端和接收端速度不匹配的情況,如果發(fā)送端發(fā)送太快會(huì)導(dǎo)致接收端無(wú)法接收而丟棄數(shù)據(jù)包,數(shù)據(jù)接收率低;如果接收端太快而發(fā)送端比較慢,也會(huì)使得網(wǎng)絡(luò)傳輸率低。流量控制就是為了平衡接收端和發(fā)送端的速度問(wèn)題或者特定數(shù)據(jù)流的傳輸請(qǐng)求等(例如視頻倍速播放等)。
流量控制的大致過(guò)程:圖中flow control receive offset表示最大的接收窗口,也可以看成初始時(shí)服務(wù)器端允許客戶端發(fā)送的最大窗口,綠色部分表示接收端讀取的數(shù)據(jù),黃色部分表示到達(dá)但還沒(méi)有接受的數(shù)據(jù)(空白間隙表示沒(méi)有按序到達(dá)的塊),flow control receive window表示接收窗口,當(dāng)接收端讀取的數(shù)據(jù)(綠色部分)大于最大接收窗口(flow control receive offset)的一半時(shí),會(huì)增加最大接收窗口(讀取多少,擴(kuò)大多少),同時(shí)發(fā)送WINDOW_UPDATE幀通知客戶端發(fā)送窗口。如果接收窗口(flow control receive window)小于0,則發(fā)送BLOCKED幀通知發(fā)送端停止發(fā)送。
http3流控中接收端發(fā)送WINDOW_UPDATE幀通知對(duì)端接收窗口的大小增長(zhǎng),比如說(shuō)能接收更多的數(shù)據(jù),發(fā)送端也可以發(fā)送BLOCKED幀表明數(shù)據(jù)發(fā)送受制于接收端的窗口,無(wú)法發(fā)送數(shù)據(jù)。
接收窗口quic的流量控制與tcp的不同在于:TCP 為了保證可靠性,窗口左邊沿向右滑動(dòng)時(shí)的長(zhǎng)度取決于已經(jīng)確認(rèn)的字節(jié)數(shù)。如果中間出現(xiàn)丟包,就算接收到了更大序號(hào)的 Segment,窗口也無(wú)法超過(guò)這個(gè)序列號(hào);而QUIC 不同,它的滑動(dòng)只取決于接收到的最大偏移字節(jié)數(shù)(圖中黃色最右邊的位置),即使中間有包未收到,也跟接收窗口沒(méi)有關(guān)系。接收窗口擴(kuò)大
此外,http3和http2一樣,同時(shí)提供流級(jí)和鏈接級(jí)別的流量控制。還有流和連接兩種流量控制,連接的流量控制是針對(duì)連接內(nèi)所有流的。
參考:哈夫曼編碼的理解
https過(guò)程解析
淺談SSL/TLS工作原理 - 知乎 (zhihu.com)
ALPN協(xié)議
HTTP/2協(xié)議
使用 Wireshark 抓取 HTTP1/2 流量
TLS 1.3 協(xié)議詳解
TCP選項(xiàng)之SACK選項(xiàng)概述
TCP 的那些事 | D-SACK
TCP中的RST標(biāo)志(Reset)詳解
TCP的快速重傳機(jī)制與累計(jì)確認(rèn)機(jī)制
HTTP協(xié)議學(xué)習(xí)筆記--http1.0、http1.1、http2.0、https、http3
面試官:一個(gè)TCP連接可以發(fā)多少個(gè)HTTP請(qǐng)求? - 知乎 (zhihu.com)
圖解 ECDHE 密鑰交換算法 - 愛(ài)碼網(wǎng) (likecs.com)
幾幅圖,拿下 HTTPS (qq.com)
關(guān)于隊(duì)頭阻塞(Head-of-Line blocking),看這一篇就足夠了
HPACK 介紹 (gohalo.me)
HTTP2 詳解 - 簡(jiǎn)書(shū) (jianshu.com)
QUIC詳解-網(wǎng)絡(luò)編程/專項(xiàng)技術(shù)區(qū)
HTTP 協(xié)議概述 - 博客園 (cnblogs.com)














