tcp, http, http2的一些理解

http短鏈接

  • 爲何Http是無狀態的短鏈接呢?而TCP是有狀態的長鏈接?Http不是創建在TCP的基礎上嗎,爲何還能是短鏈接?如今明白了,Http就是在每次請求完成後就把TCP鏈接關了,因此是短鏈接。而咱們直接經過Socket編程使用TCP協議的時候,由於咱們本身能夠經過代碼區控制何時打開鏈接何時關閉鏈接,只要咱們不經過代碼把鏈接關閉,這個鏈接就會在客戶端和服務端的進程中一直存在,相關狀態數據會一直保存着
  • HTTP 1.0 初衷主要是解決WEB文檔在網絡中的傳輸問題,由於傳輸文件是一個低頻的請求,不必進行長時間鏈接,因此HTPP 1.0 被設計成短鏈接,每進行一次HTPP通訊後就會斷開TCP鏈接。
    HTTP 1.1版本隨着互聯網的發展,HTTP 再也不只是傳送簡單的文件信息,多樣化的文本信息開始普遍應用,像html 這樣的網頁訪問的同時會同時附帶很是多的圖片之類的信息,若是每一個請求都要進行TCP鏈接和斷開(三次握手和四次揮手),這樣勢必會形成不少額外的通訊開銷。
    因此爲了解決此問題 HTTP 協議1.1版本會在請求的時候,只要任意一端沒有明確的提出斷開鏈接則保持TCP鏈接狀態。

socket是什麼

  • 套接字(socket)是通訊的基石,是支持TCP/IP協議的網絡通訊的基本操做單元。它是網絡通訊過程當中端點的抽象表示,包含進行網絡通訊必須的五種信息:鏈接使用的協議,本地主機的IP地址,本地進程的協議端口,遠地主機的IP地址,遠地進程的協議端口。

三次握手

image.png

  • 第一次握手:

客戶主機發起鏈接請求,設置SYN標誌位爲1,同時客戶端隨機選擇了一個初始序號client_isn,而且存放在TCP報文字段的序號
image.pngcss

  • 第二次握手:

接下來,當服務端接收到該報文後,會爲其分配TCP 緩存和變量(這使得TCP容易受到被稱爲SYN 洪泛攻擊的拒絕服務攻擊)緊接着,服務端會返回一個SYNACK 報文到客戶端,其中SYN標誌位爲1確認號設置爲client_isn + 1,而且選一個本身的初始序號server_isn,並放置在序號字段中
image.pnghtml

  • 第三次握手:

當收到服務器發來的SYNACK報文段後,客戶端也須要給該鏈接分配緩存和變量,而後再次發送一個確認報文給服務端,其中,SYN標誌位設置爲0,將確認號設置爲server_isn + 1,另外,這次報文能夠攜帶負載數據
image.pngjava

問題

爲何要三次握手而不是兩次?

簡單來講,三次握手的目的是爲了讓雙方驗證各自的接收能力和發送能力算法

  • 第一次握手,A 發送SYNBB接收到了後,能確認什麼呢? 顯然,B能確認A發送能力和B接收能力;
  • 第二次握手,B發送SYNACKAA接收到後,能確認什麼呢? A能確認B的發送能力和A本身的接收能力,此外,A收到了SYNACK,那麼說明前面A發的SYN成功到達B的手中,因此也能確認A本身的發送能力和B接收能力;至此,A已經確認了雙方各自的發送能力和接收能力都是OK的,所以轉爲ESTABLISHED狀態;
  • 第三次握手,A發送ACKBB接收後,能確認什麼呢?
    直接的,B能確認A發送能力和B接收能力,另外因爲B能收到ACK說明前面發送的SYNACK已經成功被接受了,說明能確認A接收能力和B發送能力。

若是使用兩次握手,就不能確認上述所說的四種能力,那麼就會致使問題。編程

假定不採用第三次報文握手,那麼只要B發出確認,新的鏈接就創建了。瀏覽器

現假定一種異常狀況,即A發出的SYN報文段並無丟失,而是在某些網絡節點長時間滯留了,以至延誤到鏈接釋放後的某個時間纔到達B。原本這是一個早已失效的報文段。但B收到此失效的鏈接請求報文段後,卻誤覺得是A又發出一次新的鏈接請求,因而就向A發出確認報文段,贊成創建鏈接。緩存

因爲如今A並無發出創建鏈接的請求,所以不會理睬B的確認,也不會向B發送數據,但B卻覺得新的運輸鏈接已經創建了,並一直等待A發來的數據。B的許多資源就這樣白白浪費了。服務器

ACK報文丟失致使第三次握手失敗網絡

當客戶端收到服務端的SYNACK應答後,其狀態變爲ESTABLISHED,並會發送ACK包給服務端,準備發送數據了。若是此時ACK在網絡中丟失(如上圖所示),過了超時計時器後,那麼服務端會從新發送SYNACK包,重傳次數根據/proc/sys/net/ipv4/tcp_synack_retries來指定,默認是5次。若是重傳指定次數到了後,仍然未收到ACK應答,那麼一段時間後,Server自動關閉這個鏈接。併發

問題就在這裏,客戶端已經認爲鏈接創建,而服務端則可能處在SYN-RCVD或者CLOSED,接下來咱們須要考慮這兩種狀況下服務端的應答:

  • 服務端處於CLOSED,當接收到鏈接已經關閉的請求時,服務端會返回RST 報文,客戶端接收到後就會關閉鏈接,若是須要的話則會重連,那麼那就是另外一個三次握手了。
  • 服務端處於SYN-RCVD,此時若是接收到正常的ACK 報文,那麼很好,鏈接恢復,繼續傳輸數據;若是接收到寫入數據等請求呢?注意了,此時寫入數據等請求也是帶着ACK 報文的,實際上也能恢復鏈接,使服務器恢復到ESTABLISHED狀態,繼續傳輸數據。

(ISN)是固定的嗎?

不固定,client_isn是隨機生成的,而server_isn則須要根據SYN 報文中的源、ip和端口,加上服務器自己的密碼數進行相同的散列獲得,顯然這也不是固定的。

三次握手過程當中能夠攜帶數據嗎?

第三次握手是能夠攜帶數據的,而前兩次不行。

四次揮手

image.png
首先,當前客戶端和服務器的狀態都爲ESTABLISHED

  • 第一次揮手

客戶主機發起鏈接釋放的請求,設置FIN1,固然,序號seq也會帶上,這裏假設爲u;發送完畢後,客戶端進入 FIN-WAIT-1 狀態
image.png

  • 第二次揮手

服務端接收到FIN 報文後,會返回一個ACK 報文回去,此時設置ACK1確認號u + 1;代表本身接受到了客戶端關閉鏈接的請求,但尚未準備好關閉鏈接。發送完畢後,服務器端進入 CLOSE-WAIT 狀態,客戶端接收到這個確認包以後,進入 FIN-WAIT-2 狀態,等待服務器端關閉鏈接
image.png

  • 第三次揮手

服務器端準備好關閉鏈接時,向客戶端發送結束鏈接請求,FIN 置爲1;發送完畢後,服務器端進入 LAST-ACK 狀態,等待來自客戶端的最後一個ACK
image.png

  • 第四次揮手

客戶端接收到服務端傳來的FIN 報文後,知道服務器已經準備好關閉了,發送一個確認包,並進入 TIME-WAIT狀態,等待可能出現的要求重傳的ACK 報文;服務器端接收到這個確認包以後,關閉鏈接,進入 CLOSED 狀態。
客戶端等待了某個固定時間(兩個最大段生命週期,2MSL,2 Maximum Segment Lifetime)以後,沒有收到服務器端的 ACK ,認爲服務器端已經正常關閉鏈接,因而本身也關閉鏈接,進入 CLOSED 狀態
image.png

四次揮手重要的是TIME-WAIT狀態,爲何須要這個狀態呢?

要確保服務器是否已經收到了咱們的ACK 報文,若是沒有收到的話,服務器會從新發FIN 報文給客戶端,那麼客戶端再次收到FIN 報文以後,就知道以前的 ACK 報文丟失了,就會再次發送ACK 報文

爲何握手只要三次,揮手卻要四次?

關鍵就在中間兩步。

  • 創建鏈接時,當服務器收到客戶端的SYN 報文後,能夠直接發送SYNACK 報文。其中ACK是用來應答的,SYN是用來同步的。
  • 可是關閉鏈接時,當服務器收到FIN 報文時,極可能並不會當即關閉SOCKET,因此只能先回復一個ACK 報文,告訴客戶端,「你發的FIN 報文我收到了」。只有等到服務器全部的報文都發送/接收完了,我才能發送FIN 報文,所以不能一塊兒發送,須要四次握手

爲何 TIME_WAIT 狀態須要通過 2MSL 才能轉換到 CLOSE 狀態?

  • 第一,爲了保證客戶端發送的最後一個ACK 報文可以到達服務器。咱們必須假設網絡是不可靠的,ACK 報文可能丟失。若是服務端發出FIN 報文後沒有收到ACK 報文,就會重發FIN 報文,此時處於TIME-WAIT狀態的客戶端就會重發ACK 報文。固然,客戶端也不能無限久的等待這個可能存在的FIN 報文,由於若是服務端正常接收到了ACK 報文後是不會再發FIN 報文的。所以,客戶端須要設置一個計時器,那麼等待多久最合適呢?所謂的MSL(Maximum Segment Lifetime)指一個報文在網絡中最大的存活時間,2MSL就是一個發送和一個回覆所需的最大時間。若是直到2MSL時間後,客戶端都沒有再次收到FIN 報文,那麼客戶端推斷ACK 報文已經被服務器成功接收,因此結束TCP 鏈接
  • 第二,防止已失效的鏈接請求報文段出如今新的鏈接中。客戶端在發送完最後一個ACK 報文後,再通過時間2MSL,就可使因爲網絡不通暢產生的滯留報文段失效。這樣下一個新的鏈接中就不會出現舊的鏈接請求報文

HTTP鏈接方式的進化史:

HTTP/0.9時代:短鏈接

每一個HTTP請求都要經歷一次DNS解析、三次握手、傳輸和四次揮手。反覆建立和斷開TCP鏈接的開銷巨大,在如今看來,這種傳輸方式簡直是糟糕透頂。

HTTP/1.0時代:持久鏈接概念提出

人們認識到短鏈接的弊端,提出了持久鏈接的概念,在HTTP/1.0中獲得了初步的支持。
持久鏈接,即一個TCP鏈接服務屢次請求:
客戶端在請求header中攜帶Connection:
Keep-Alive,便是在向服務端請求持久鏈接。若是服務端接受持久鏈接,則會在響應header中一樣攜帶Connection:
Keep-Alive,這樣客戶端便會繼續使用同一個TCP鏈接發送接下來的若干請求。(Keep-Alive的默認參數是[timout=5,
max=100],即一個TCP鏈接能夠服務至多5秒內的100次請求)

當服務端主動切斷一個持久鏈接時(或服務端不支持持久鏈接),則會在header中攜帶Connection: Close,要求客戶端中止使用這一鏈接。

HTTP/1.1時代:持久鏈接成爲默認的鏈接方式;提出pipelining概念

HTTP/1.1開始,即便請求header中沒有攜帶Connection: Keep-Alive,傳輸也會默認以持久鏈接的方式進行。
目前全部的瀏覽器都默認請求持久鏈接,幾乎全部的HTTP服務端也都默認開啓對持久鏈接的支持,短鏈接正式成爲過去式。(HTTP/1.1的發佈時間是1997年,最後一次對協議的補充是在1999年,咱們能夠誇張地說:HTTP短鏈接這個概念已通過時了近20年了。)
同時,持久鏈接的弊端被提出 —— HOLB(Head of Line Blocking)
即持久鏈接下一個鏈接中的請求仍然是串行的,若是某個請求出現網絡阻塞等問題,會致使同一條鏈接上的後續請求被阻塞。

因此HTTP/1.1中提出了pipelining概念,即客戶端能夠在一個請求發送完成後不等待響應便直接發起第二個請求,服務端在返回響應時會按請求到達的順序依次返回,這樣就極大地下降了延遲。

然而pipelining並無完全解決HOLB,爲了讓同一個鏈接中的多個響應可以和多個請求匹配上,響應仍然是按請求的順序串行返回的。因此pipelining並無被普遍接受,幾乎全部代理服務都不支持pipelining,部分瀏覽器不支持pipelining,支持的大部分也會將其默認關閉。

SPDY和HTTP/2:multiplexing

multiplexing即多路複用,在SPDY中提出,同時也在HTTP/2中實現。
multiplexing技術可以讓多個請求和響應的傳輸徹底混雜在一塊兒進行,經過streamId來互相區別。這完全解決了holb問題,同時還容許給每一個請求設置優先級,服務端會先響應優先級高的請求。

如今Chrome、FireFox、Opera、IE、Safari的最新版本都支持SPDY,Nginx/Apache HTTPD/Jetty/Tomcat等服務端也都提供了對SPDY的支持。

http2

image.png

  • HTTP/1裏的header對應HTTP/2裏的 HEADERS frame
  • HTTP/1裏的payload對應HTTP/2裏的 DATA frame

下面從一個真實的gRPC SayHello請求,查看它在HTTP/2上是怎樣實現的
image.png

能夠看到下面這些Header:

  • Header: :authority: localhost:50051
  • Header: :path: /helloworld.Greeter/SayHello
  • Header: :method: POST
  • Header: :scheme: http
  • Header: content-type: application/grpc
  • Header: user-agent: grpc-java-netty/1.11.0

而後請求的參數在DATA frame裏:

  • GRPC Message: /helloworld.Greeter/SayHello, Request

簡而言之,gGRPC把元數據放到HTTP/2 Headers裏,請求參數序列化以後放到 DATA frame裏

HTTP / 2 主要有兩個規範組成

  • Hypertext Transfer Protocol version 2 (超文本傳輸協議版本 2)
  • HPACK - HTTP / 2 的頭壓縮 (HPACK 是一種頭部壓縮算法)

HTTP2 的特性

HTTP / 2 支持 HTTP / 1.1 的全部核心功能,但旨在經過多種方式提升效率

  • HTTP/2 採用二進制傳輸數據,而非 HTTP/1 的文本格式傳輸
  • HTTP / 2 基本協議單元是幀,好比 head(頭部信息)幀,data(傳輸數據細信息)幀
  • HTTP / 2 使用流技術支持多路複用,也就是說提供了在單個鏈接上覆用 HTTP 請求和響應的能力, 多個請求或響應能夠同時在一個鏈接上使用流.
  • HTTP / 2 支持壓縮頭部幀,容許將多個請求壓縮成成一個分組,並且在客戶端和服務器端分別頭部信息創建索引,相同的表頭只須要傳輸索引就能夠。
  • HTTP / 2 支持對請求劃分優先級(就是流的優先級)
  • HTTP / 2 支持 Server Push 技術

HTTP2 的原理

多路複用

HTTP/2 將每個請求變成流,每個流都有本身的 ID,有本身的優先級,這些流能夠由客戶端發送到服務端,也能夠由服務端發送到客戶端,將數據劃分爲幀,頭部信息爲 head 幀,實體信息爲 data 幀,最後將這些流亂序發送到一個 TCP 鏈接中,以下圖:
image.png

  • HTTP/2 中,在一個瀏覽器同域名下的全部請求都是在單個鏈接中完成,這個鏈接能夠承載任意數量的雙向數據流,每一個數據流都以消息的形式發送,而消息又由一個或多個幀組成,多個幀之間能夠亂序發送,根據幀首部的流標識能夠將多個幀從新組裝成一個流。
  • 在 HTTP/1 中,若是想併發發送多個請求,必須建立多個 TCP 鏈接,並且瀏覽器爲了減小負載,會對同一域名下的 TCP 鏈接作限制,這樣當請求量比較大時,會引發阻塞

服務器推送

  • HTTP /1 中客戶端往服務端發送請求嚴格遵照一個請求,一個響應,好比客戶端請求展現網頁時,服務端發揮 HTML 內容,瀏覽器解析時發送 css,js 請求,服務端又返回 css,js 文件,那麼服務端爲何不能在返回網頁時就推送 css,js 內容給客戶端呢,在 HTTP /2 中這已功能已經支持,
  • 服務端主動推送也會遵照同源策略,不會隨便推送第三方的資源到客戶端

若是服務端推送資源是唄客戶端緩存過的,客戶端是有權力拒絕服務端的推送的,瀏覽器能夠經過發送 RST_STREAM 幀來拒收。

  • 每個服務端推送的資源都是一個流

頭部壓縮

HTTP /1 的請求頭較大,並且是以純文本發送,HTTP/2 對消息頭進行了壓縮,採用的是 HACK 算法,可以節省消息頭佔用的網絡流量,其主要是在兩端創建了索引表,消息頭在傳輸時能夠採用索引,而 HTTP/1.x 每次請求,都會攜帶大量冗餘頭信息,浪費了不少帶寬資源

grpc

gRPC 是 Google 基於 HTTP/2 以及 protobuf 的,要了解 gRPC 協議,只須要知道 gRPC 是如何在 HTTP/2 上面傳輸就能夠了。

gRPC 一般有四種模式,unary,client streaming,server streaming 以及 bidirectional streaming,對於底層 HTTP/2 來講,它們都是 stream,而且仍然是一套 request + response 模型。

Request

gRPC 的 request 一般包含

  • Request-Headers
  • 0 或者多個 Length-Prefixed-Message
  • EOS
  1. Request-Headers 直接使用的 HTTP/2 headers,在 HEADERS 和 CONTINUATION frame 裏面派發。定義的 header 主要有 Call-Definition 以及 Custom-Metadata。Call-Definition 裏面包括 Method(其實就是用的 HTTP/2 的 POST),Content-Type 等。而 Custom-Metadata 則是應用層自定義的任意 key-value,key 不建議使用 grpc- 開頭,由於這是爲 gRPC 後續本身保留的。
  2. Length-Prefixed-Message 主要在 DATA frame 裏面派發,它有一個 Compressed flag 用來表示改 message 是否壓縮,若是爲 1,表示該 message 採用了壓縮,而壓縮算法定義在 header 裏面的 Message-Encoding 裏面。而後後面跟着四字節的 message length 以及實際的 message。
  3. EOS(end-of-stream) 會在最後的 DATA frame 裏面帶上了 END_STREAM 這個 flag。用來表示 stream 不會在發送任何數據,能夠關閉了。

Response

Response 主要包含

  • Response-Headers
  • 0 或者多個 Length-Prefixed-Message
  • Trailers,若是遇到了錯誤,也能夠直接返回 Trailers-Only。
  1. Response-Headers 主要包括 HTTP-Status,Content-Type 以及 Custom-Metadata 等。
  2. Trailers-Only 也有 HTTP-Status ,Content-Type 和 Trailers。
  3. Trailers 包括了 Status 以及 0 或者多個 Custom-Metadata。

HTTP-Status 就是咱們一般的 HTTP 200,301,400 這些,很通用就再也不解釋。Status 也就是 gRPC 的 status, 而 Status-Message 則是 gRPC 的 message。Status-Message 採用了 Percent-Encoded 的編碼方式,具體參考這裏

若是在最後收到的 HEADERS frame 裏面,帶上了 Trailers,而且有 END_STREAM 這個 flag,那麼就意味着 response 的 EOS。

Protobuf

gRPC 的 service 接口是基於 protobuf 定義的,咱們能夠很是方便的將 service 與 HTTP/2 關聯起來。

  • Path : /Service-Name/{method name}
  • Service-Name : ?( {proto package name} "." ) {service name}
  • Message-Type : {fully qualified proto message name}
  • Content-Type : "application/grpc+proto"

grpc的好處

  1. 跨語言,protobuf
  2. 性能好,基於http2協議實現的,http2協議提供了不少新的特性,而且在性能上也比http1提升了許多
  3. 標準化狀態碼
  4. 負載均衡,服務發現,日誌,監控等都支持可插拔機制

四類服務方法

基於 http2 協議的特性:gRPC 容許定義以下四類服務方法

  • 單項 RPC:客戶端發送一次請求,等待服務端響應結構,會話結束,就像一次普通的函數調用這樣簡單
  • 服務端流式 RPC:客戶端發起一塊兒請求,服務端會返回一個流,客戶端會從流中讀取一系列消息,直到沒有結果爲止
  • 客戶端流式 RPC:客戶端提供一個數據流並寫入消息發給服務端,一旦客戶端發送完畢,就等待服務器讀取這些消息並返回應答
  • 雙向流式 RPC:客戶端和服務端都一個數據流,均可以經過各自的流進行讀寫數據,這兩個流是相互獨立的,客戶端和服務端均可以按其但願的任意順序獨寫

protobuf的原理

  • Base 128 varint

這是一個編碼算法,咱們都知道,int32 佔四個字節,int64 佔 8 個字節,這是固定的,無論這個數字是 1 仍是 123456,佔的字節數是同樣,那有沒有一種能根據數字大小變長編碼的算法呢?Base 128 varint 就是,在設置二進制網絡協議通訊時,這種好處是可觀的,可以帶來性能上的提高。爲何叫 128 呢,就是由於採用 7bit 的空間存儲數據(一個字節佔 8bit,但只採用 7bit),7bit 最大固然只能存儲 128 了,那麼最高位幹啥呢?最高位用來看成一個標識 (flag), 若是最高位是 0 就表示這個最後一個字節了。

示例:

咱們用一個數字 10 和數字 300 來說解一下上面的 Base 128 varint

先說數字 10,轉化爲二進制後是:0000 1010,爲何只有八位呢,由於 10 用一個字節表示已經足夠了,最高位爲 0(加粗的那個),表示這是最後一個字節了,不須要再用額外的字節來存儲了

再來看數字 300,轉化爲二進制後是:‭‭00010010_1100‬, 轉化成 varint,以下步驟:

按照 7 位進行分開, 0000010_0101100,不夠的補 0
進行反轉:0101100_0000010
最高位補數,第一個字節最高位補 1,第二個字節最高位補 0:10101100_00000010

ProtoBuffer 序列化後的存儲格式

Tag,Length,Value ,這是序列化後存儲的二進制的格式,Tag 你們簡單理解爲就是 proto 文件中字段後面的編號,Length 是這個字段對應的值的字節長度,Value 就是具體的值了,最終將全部數據拼裝成一個流,以下圖:
image.png
由圖咱們得知,ProtoBuffer 存儲是緊密的,各個字段很是緊湊,不會浪費空間,若某個字段沒有賦值,則不會出如今序列化後的數據中,相應字段在解碼時纔會被設置默認值
image.png

T 表明的 tag 是由 fieldNumber(字段編號)和 wireType(上圖中最左邊的 0,1,2...)組成的,fieldNumber 保證了字段不重複和他在數據流中的位置,wireType 標記了數據類型,若是是 varint 編碼,fieldNumber 也保證了數據字節的長度 (L)

參考文章

https://www.zhihu.com/search?...
https://zhuanlan.zhihu.com/p/...
https://learnku.com/articles/...

相關文章
相關標籤/搜索