HTTP請求的構建
- 請求行
- 請求方法,如get post put delete
-
首部字段
-
key value,如Accept-Charset 表示客戶端可以接受的字符集,防止傳過來是另外的字符集,導致亂碼出現。
Content-Type指正文格式,例如進行post請求,如果正文是json就應該將這個值設為json
-
HTTP請求的發送
面向鏈接的方式發送,通過stream二進制流的方式傳送誒對方,到了tcp層,會把二進制流轉化為一個個的報文發給服務器。
發送每個報文對需要對方回應ack,如果沒有回應就一直發送,tcp每發送一個報文都需要加上源地址喝目標地址,放到ip頭里面,交給ip層傳輸。
ip層查看目標地址和自己是否是同一個局域網,如果是就發送arp協議請求這個目標地址對應的mac地址,將源mac和目標mac放入mac頭發送,如果不在同一個局域網,發送到網管,還需要發送arp協議,獲取網關mac,將源mac和網關mac放入mac頭發送。
網關收到包發現mac符合,取出目標ip,根據路由協議找到下一跳路由,獲取下一跳路由mac,將包發給下一跳路由器。最終到達目標的局域網,這個時候最后一跳發現目標地址在自己的某一個出口的局域網,於是在整個局域網發送arp,得到目標mac地址,將包發出去。
目標及其發現mac地址符合,ip地址符合,解析tcp的頭,查看序列號,如果正確就返回一個ack,不是就丟棄。tcp頭里有端口號,http服務器正在監聽端口號,於是目標機器知道是http服務器這個進程想要這個包,於是將包發送給http服務器。
HTTP返回的構建
狀態碼這是大家都很熟悉的如200,201,301,302,403,404,500
首部字段key value,如Retry-After,表示告訴客戶端應該在多長時間再嘗試,content-type 返回的是html還是json,然后后面的發送過程和請求時客戶端發送的過程一致。這就是一個正常的http請求和返回的完整過程。
HTTP 2.0
HTTP 1.1 在應用層以純文本的形式進行通信,每次通信都要帶完整的http頭,而且不考慮pipeline模式的話,每次過程在實時行和並發性都存在問題。
http2.0 對http的頭進行壓縮,將每次攜帶的大量key value在兩端建立一個索引表,對相同的頭只發送索引表中的索引。
http協議將一個tcp的連接中,切分為多個流,每個流都有自己的id,而且流可以是一個雙向的虛擬通道,流也是有優先級別的。http中還有所有的傳輸消息分割為更小的信息和真,並對他們采用二進制格式編碼。
通過這兩種機制,http2.0的客戶端可以講多個請求分到不同的流中,將請求內容拆成幀,這些幀可以亂序傳送,根據幀首部的流標識符重新組裝,根據優先級決定處理哪個流的數據。
比如一個頁面要發送三個獨立的請求,分別獲取css,js,jpg,如果使用http1.1就是串行的,如果使用2.0就可以在一個鏈接里,客戶端和服務端可以同時發送多個請求或回應,而不是按照順序一對一對應。
HTTP2.0解決了http1.1的隊首阻塞問題,減少了tcp連接數對服務器性能的影響,同時加快了頁面組件的傳輸速度。
QUCI協議
雖然2.0是可以解決1.1的高並發阻塞問題,但是2.0也是基於tcp洗衣的,當其中任何一個包遇到問題,都會阻塞住。
機制一:自定義連接機制
tcp連接是由四元組標識的,分別是源ip,源端口,目的ip,目的端口,任何一個元素發生變化時,就需要重新連接。再次進行三次握手,導致延時,在udp中,是以一個64位隨機數作為id來標識,而且是無連接的,只要id不變就不需要重新建立連接。
機制二:自定義重傳機制
quic有個序列號是遞增的,任何一個序列號的包只發送一次,下次加一。例如,發送一個包,序號是100,發現沒有返回,再次發送就是101,如果返回的ack是100,就是對第一個包的響應,返回ack101是第二個包的響應。quic還定義了一個offset概念,發送的數據在這個數據流離有個偏移量offset,可以通過offset查看數據發送到哪里,只要這個offset的包沒有來就重發,如果來了就拼接。
機制三:無阻塞的多路復用
有了自定義連接和重傳機制,就可以解決上面http2.0的多路復用問題。同一條QUIC連接上可以創建多個stream,發送多個http請求,但是quic是基於udp的,一個鏈接上多個stream之間沒有依賴,這樣,如果stream2丟了一個udp,后面跟着stream3的udp,雖然stream需要重傳,但是stream3的包無需等待就可以發送。
機制四:自定義流量控制
tcp的流量控制是通過滑動窗口協議,quic通過window_update,來告訴對端它可以接受的字節數,但是quic的窗口是適應自己的多路復用機制的,不但在一個鏈接上控制窗口,還在一個連接中的每個stream控制窗口。
在tcp協議中,接收端的窗口的起點是下一個要接受並且ack的包,即便后來的包到了,放在緩存里,窗口也不能右移,只要前面的沒到,后面的到了也不能ack,導致后面的到了,也有可能超時重傳,浪費貸款。
quic的ack是基於offset的,每個offset包來了,進了緩存就可以應答,應答后不會重發,中間的空檔會等待到來或者重發即可,而窗口的起始位置就是當前收到的最大offset,從這個offset到當前的stream所能容納的最大緩存,就是真正窗口大小。