客戶主機發起鏈接請求,設置SYN
標誌位爲1
,同時客戶端隨機
選擇了一個初始序號client_isn
,而且存放在TCP報文
字段的序號
中css
接下來,當服務端接收到該報文後,會爲其分配TCP 緩存和變量
(這使得TCP容易受到被稱爲SYN 洪泛攻擊
的拒絕服務攻擊)緊接着,服務端會返回一個SYNACK 報文
到客戶端,其中SYN
標誌位爲1
,確認號
設置爲client_isn + 1
,而且選一個本身的初始序號server_isn
,並放置在序號
字段中html
當收到服務器發來的SYNACK
報文段後,客戶端也須要給該鏈接分配緩存和變量,而後再次發送一個確認報文給服務端,其中,SYN
標誌位設置爲0
,將確認號
設置爲server_isn + 1
,另外,這次報文能夠攜帶負載數據java
簡單來講,三次握手的目的是爲了讓雙方驗證各自的接收能力和發送能力。算法
SYN
到B
,B
接收到了後,能確認什麼呢? 顯然,B
能確認A
的發送
能力和B
的接收
能力;B
發送SYNACK
到A
,A
接收到後,能確認什麼呢? A
能確認B
的發送能力和A
本身的接收能力,此外,A
收到了SYNACK
,那麼說明前面A
發的SYN
成功到達B
的手中,因此也能確認A
本身的發送
能力和B
的接收
能力;至此,A
已經確認了雙方各自的發送能力和接收能力都是OK
的,所以轉爲ESTABLISHED
狀態;A
發送ACK
到B
,B
接收後,能確認什麼呢? 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
狀態,繼續傳輸數據。不固定,client_isn
是隨機生成的,而server_isn
則須要根據SYN 報文
中的源、ip和端口
,加上服務器自己的密碼數
進行相同的散列獲得,顯然這也不是固定的。
第三次握手是能夠攜帶數據的
,而前兩次不行。
首先,當前客戶端和服務器的狀態都爲ESTABLISHED
客戶主機發起鏈接釋放的請求,設置FIN
爲1
,固然,序號seq
也會帶上,這裏假設爲u
;發送完畢後,客戶端進入 FIN-WAIT-1
狀態
服務端接收到FIN 報文
後,會返回一個ACK 報文
回去,此時設置ACK
爲1
,確認號
爲u + 1
;代表本身接受到了客戶端關閉鏈接的請求,但尚未準備好關閉鏈接。發送完畢後,服務器端進入 CLOSE-WAIT
狀態,客戶端接收到這個確認包以後,進入 FIN-WAIT-2
狀態,等待服務器端關閉鏈接
服務器端準備好關閉鏈接時,向客戶端發送結束鏈接請求,FIN
置爲1
;發送完畢後,服務器端進入 LAST-ACK
狀態,等待來自客戶端的最後一個ACK
客戶端接收到服務端傳來的FIN 報文
後,知道服務器已經準備好關閉了,發送一個確認包,並進入 TIME-WAIT
狀態,等待可能出現的要求重傳的ACK 報文
;服務器端接收到這個確認包以後,關閉鏈接,進入 CLOSED
狀態。
客戶端等待了某個固定時間(兩個最大段生命週期,2MSL
,2 Maximum Segment Lifetime)以後,沒有收到服務器端的 ACK
,認爲服務器端已經正常關閉鏈接,因而本身也關閉鏈接,進入 CLOSED
狀態
TIME-WAIT
狀態,爲何須要這個狀態呢?要確保服務器是否已經收到了咱們的ACK 報文
,若是沒有收到的話,服務器會從新發FIN 報文
給客戶端,那麼客戶端再次收到FIN 報文
以後,就知道以前的 ACK 報文
丟失了,就會再次發送ACK 報文
。
關鍵就在中間兩步。
SYN 報文
後,能夠直接發送SYNACK 報文
。其中ACK
是用來應答的,SYN
是用來同步的。FIN 報文
時,極可能並不會當即關閉SOCKET
,因此只能先回復一個ACK 報文
,告訴客戶端,「你發的FIN 報文
我收到了」。只有等到服務器全部的報文都發送/接收完了,我才能發送FIN 報文
,所以不能一塊兒發送,須要四次握手ACK 報文
可以到達服務器。咱們必須假設網絡是不可靠的,ACK 報文
可能丟失。若是服務端發出FIN 報文
後沒有收到ACK 報文
,就會重發FIN 報文
,此時處於TIME-WAIT
狀態的客戶端就會重發ACK 報文
。固然,客戶端也不能無限久的等待這個可能存在的FIN 報文
,由於若是服務端正常接收到了ACK 報文
後是不會再發FIN 報文
的。所以,客戶端須要設置一個計時器,那麼等待多久最合適呢?所謂的MSL
(Maximum Segment Lifetime)指一個報文在網絡中最大的存活時間,2MSL就是一個發送和一個回覆所需的最大時間。若是直到2MSL
時間後,客戶端都沒有再次收到FIN 報文
,那麼客戶端推斷ACK 報文
已經被服務器成功接收,因此結束TCP 鏈接
。ACK 報文
後,再通過時間2MSL
,就可使因爲網絡不通暢產生的滯留報文段失效。這樣下一個新的鏈接中就不會出現舊的鏈接請求報文每一個HTTP請求都要經歷一次DNS解析、三次握手、傳輸和四次揮手。反覆建立和斷開TCP鏈接的開銷巨大,在如今看來,這種傳輸方式簡直是糟糕透頂。
人們認識到短鏈接的弊端,提出了持久鏈接的概念,在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開始,即便請求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,支持的大部分也會將其默認關閉。
multiplexing即多路複用,在SPDY中提出,同時也在HTTP/2中實現。
multiplexing技術可以讓多個請求和響應的傳輸徹底混雜在一塊兒進行,經過streamId來互相區別。這完全解決了holb問題,同時還容許給每一個請求設置優先級,服務端會先響應優先級高的請求。
如今Chrome、FireFox、Opera、IE、Safari的最新版本都支持SPDY,Nginx/Apache HTTPD/Jetty/Tomcat等服務端也都提供了對SPDY的支持。
下面從一個真實的gRPC SayHello
請求,查看它在HTTP/2上是怎樣實現的
能夠看到下面這些Header:
而後請求的參數在DATA frame裏:
簡而言之,gGRPC把元數據放到HTTP/2 Headers裏,請求參數序列化以後放到 DATA frame裏
HTTP / 2 支持 HTTP / 1.1 的全部核心功能,但旨在經過多種方式提升效率
HTTP/2 將每個請求變成流,每個流都有本身的 ID,有本身的優先級,這些流能夠由客戶端發送到服務端,也能夠由服務端發送到客戶端,將數據劃分爲幀,頭部信息爲 head 幀,實體信息爲 data 幀,最後將這些流亂序發送到一個 TCP 鏈接中,以下圖:
若是服務端推送資源是唄客戶端緩存過的,客戶端是有權力拒絕服務端的推送的,瀏覽器能夠經過發送 RST_STREAM 幀來拒收。
HTTP /1 的請求頭較大,並且是以純文本發送,HTTP/2 對消息頭進行了壓縮,採用的是 HACK 算法,可以節省消息頭佔用的網絡流量,其主要是在兩端創建了索引表,消息頭在傳輸時能夠採用索引,而 HTTP/1.x 每次請求,都會攜帶大量冗餘頭信息,浪費了不少帶寬資源
gRPC 是 Google 基於 HTTP/2 以及 protobuf 的,要了解 gRPC 協議,只須要知道 gRPC 是如何在 HTTP/2 上面傳輸就能夠了。
gRPC 一般有四種模式,unary,client streaming,server streaming 以及 bidirectional streaming,對於底層 HTTP/2 來講,它們都是 stream,而且仍然是一套 request + response 模型。
gRPC 的 request 一般包含
Response 主要包含
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。
gRPC 的 service 接口是基於 protobuf 定義的,咱們能夠很是方便的將 service 與 HTTP/2 關聯起來。
基於 http2 協議的特性:gRPC 容許定義以下四類服務方法
這是一個編碼算法,咱們都知道,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
Tag,Length,Value ,這是序列化後存儲的二進制的格式,Tag 你們簡單理解爲就是 proto 文件中字段後面的編號,Length 是這個字段對應的值的字節長度,Value 就是具體的值了,最終將全部數據拼裝成一個流,以下圖:
由圖咱們得知,ProtoBuffer 存儲是緊密的,各個字段很是緊湊,不會浪費空間,若某個字段沒有賦值,則不會出如今序列化後的數據中,相應字段在解碼時纔會被設置默認值
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/...