亚洲最大看欧美片,亚洲图揄拍自拍另类图片,欧美精品v国产精品v呦,日本在线精品视频免费

  • 站長資訊網
    最全最豐富的資訊網站

    通過QUIC協議,來看看怎么學習網絡協議

    本篇文章帶大家了解一下QUIC協議,并以QUIC協議為例,來聊聊如何學習網絡協議,希望對大家有所幫助!

    通過QUIC協議,來看看怎么學習網絡協議

    在之前發(fā)布的關于 s2n-quic 的文章中,有讀者問我如何學習像 QUIC 這樣的網絡協議。對于大部分互聯網從業(yè)者來說,雖然大家每天都在跟網絡打交道,但很少有人會(需要)關心 HTTP 之下的網絡協議的細節(jié),大部分時候,了解個大概,知道如何使用就可以了。如果你對 QUIC 一點概念都沒有,那么,下面這個圖能幫助你很好地了解 QUIC 在 HTTP/3 生態(tài)中的地位:

    通過QUIC協議,來看看怎么學習網絡協議

    那么,如果你就是要詳盡地了解一下 QUIC 的知識,該如何入手呢?

    作為一個曾經的網絡協議和網絡設備的開發(fā)者,我自己的心得是:從 RFC 入手,輔以 wireshark 抓包,來快速掌握目標協議。

    對于 QUIC 而言,我們首先需要閱讀的是 RFC9000。協議的閱讀是非??菰锏氖虑?,需要一定的耐心,如果英文不太好,可以用 google translate 將其翻譯成中文,快速瀏覽一番(泛讀)。第一遍閱讀主要了解里面的主要概念,以及主要流程。

    之后,我們就可以撰寫使用 QUIC 協議的程序,然后通過 wireshark 抓包,通過研究實際的報文,對比 RFC 協議中的內容(精讀),來更深入地理解協議的本質。

    我們還是以上一篇文章中的代碼為基礎,構建 echo 客戶端和服務器。為方便大家閱讀,我把代碼也貼上來。感興趣的同學可以自行 clone 我的 repo,并運行 client/server 代碼。

    客戶端代碼(見 github: tyrchen/rust-training/live_coding/quic-test/examples/client.rs):

    通過QUIC協議,來看看怎么學習網絡協議

    服務端代碼(見 github: tyrchen/rust-training/live_coding/quic-test/examples/server.rs):

    通過QUIC協議,來看看怎么學習網絡協議

    這兩段代碼構建了一個最簡單的 echo server。我們可以使用 wireshark 監(jiān)聽本地 loopback 接口下的 UDP 包,進行抓包。要注意的是,對于 QUIC 這樣使用了 TLS 協議的流量,即便抓到了包,可能只有頭幾個包可讀,后續(xù)的包都是加密內容,無法閱讀。因此,我們在構建 client/server 時,需要想辦法把服務器和客戶端之間協商出來的 session key 抓取下來,供 wireshark 解密使用。一般 SSL/TLS 庫都會提供這個功能。比如對于 Rustls,我們可以在 tls config 中使用 key_log。如果你仔細看上面 server 的代碼,會看到這句:

    let config = Builder::new()     .with_certificate(CERT_PEM, KEY_PEM)?     .with_key_logging()? # 使能 keylogging     .build()?;

    使用了 key_log 后,在啟動 server 的時候,我們只需要指定 SSLKEYLOGFILE 就可以了:

    SSLKEYLOGFILE=session.log cargo run --example server

    在抓包完成后,打開 wireshark 的 preference,選擇 TLS 協議,把 log 的路徑放進去就可以了:

    通過QUIC協議,來看看怎么學習網絡協議

    以下是一次完整的客戶端和服務器的交互的抓包,我們看到,所有 “protected payload” 都被正常顯示出來了:

    通過QUIC協議,來看看怎么學習網絡協議

    因為我們的 echo client 只做了最簡單的動作(只開了一個 bidirectional stream),所以通過這個抓包,我們重點可以研究 QUIC 協議建立連接的過程。

    客戶端發(fā)送的首包

    我們看客戶端發(fā)的第一個報文:

    通過QUIC協議,來看看怎么學習網絡協議

    這個報文包含了非常豐富的信息。首先,和 TCP 握手不同的是,QUIC 的首包非常大,有 1200 字節(jié)之多(協議要求 UDP payload at least 1200 bytes),包含 QUIC 頭,一個 255 字節(jié)的 CRYPTO frame,以及 890 字節(jié) PADDING frame。從 header 可以看到,這個 QUIC 包的類型是 Initial。

    QUIC 報文類型

    對于 QUIC 包來說,Header 是明文,之后的所有 frame payload 都是密文(除了頭幾個包)。我們看到這個首包是一個 Long Header 報文,在 RFC9000 的 17.2 節(jié)中,定義了 Long Header Packet:

    Long Header Packet {    Header Form (1) = 1,    Fixed Bit (1) = 1,    Long Packet Type (2),    Type-Specific Bits (4),    Version (32),    Destination Connection ID Length (8),    Destination Connection ID (0..160),    Source Connection ID Length (8),    Source Connection ID (0..160),    Type-Specific Payload (..),  }

    感興趣的可以自行去閱讀 RFC 相應的章節(jié)。對于 Long Header 報文,有如下幾種類型:

    通過QUIC協議,來看看怎么學習網絡協議

    既然有 Long Header packet,那么就有 Short Header packet,Short Header packet 目前的版本只有一種:

    1-RTT Packet {    Header Form (1) = 0,    Fixed Bit (1) = 1,    Spin Bit (1),    Reserved Bits (2),    Key Phase (1),    Packet Number Length (2),    Destination Connection ID (0..160),    Packet Number (8..32),    Packet Payload (8..), }

    為什么需要 connection id?

    在我們捕獲的這個報文頭中,我們看到有 Source Connection ID(SCID)和 Destination Connection ID(DCID)這個新的概念。你也許會好奇:QUIC 不是基于 UDP/IP 的協議么?底層的協議已經有五元組(src ip / src port / dst ip / dst port / protocol)來描述一個連接(connection),為什么還需要 connection id 這樣一個新的概念?

    這是為了適應越來越多的移動場景。有了 QUIC 層自己的 connection id,底層網絡(UDP/IP)的變化,并不會引發(fā) QUIC 連接的中斷,也就是說,你從家里開車出門,即便手機的網絡從 WIFI(固網運營商分配給你的 IP)切換到蜂窩網絡(移動運營商分配給你的 IP),整個 UDP/IP 網絡變化了,但你的 QUIC 應用只會感受到細微的延遲,并不需要重新建立 QUIC 連接。

    從這個使用場景來看,QUIC 底層使用無連接的 UDP 是非常必要的。

    首包中就包含了 TLS hello?

    我們接下來看看 CRYPTO frame:

    通過QUIC協議,來看看怎么學習網絡協議

    可以看到,QUIC 在建立連接的首包就把 TLS Client Hello 囊括在 CRYPTO frame 中。并且使用的 TLS版本是 1.3。在 Client Hello 的 extension 中,QUIC 協議使用了一個 quic_transport_parameters 的 extension,用來協商 QUIC 自己的一些初始值,比如支持多少個 stream,這個連接中可以最多使用多少個 active connection id 等等。

    QUIC 支持哪些 frame?

    現在我們已經見到了兩種 Frame:CRYPTO 和 PADDING。下表中羅列了 QUIC 所有支持的 frame:

    通過QUIC協議,來看看怎么學習網絡協議

    服務器的回包

    我們來看 server 的回包:

    通過QUIC協議,來看看怎么學習網絡協議

    這里有一些新東西。首先,一個 UDP 包內部可以包含若干個 QUIC payload,我們看到 server 回復了一個 QUIC Initial 報文和一個 QUIC Handshake 報文。在 Initial 報文中,我們看到了一個 ACK frame,可見 QUIC 雖然構建于 UDP,但在 QUIC 協議內部構建了類似 TCP 的確認機制。

    我們之前看到,在 Initial 報文的 CRYPTO frame 中,客戶端發(fā)送了 TLS Client Hello,同樣的,服務器在 Initial 報文的 CRYPTO frame 中發(fā)送了 TLS Server Hello。這個我們就略過不看。

    在 Handshake 報文中:

    通過QUIC協議,來看看怎么學習網絡協議

    服務器發(fā)送了自己的證書,并結束了 TLS handshake。

    客戶端結束 Handshake

    我們再看第三個包,客戶端發(fā)送給服務器結束 TLS 握手:

    通過QUIC協議,來看看怎么學習網絡協議

    這個包依舊包含兩個 QUIC 報文,其中第一個就是一個 ACK frame,來確認收到了服務器的 Server Hello 那個 QUIC 報文;第二個包含一個 ACK frame,確認服務器的 Handshake,隨后有一個 CRYPTO frame 結束客戶端的 TLS handshake。

    TLS 握手結束之后,客戶端和服務器就開始應用層的數據交換,此刻,所有數據都是加密的。

    客戶端發(fā)送一個 “hello” 文本

    在我們的 echo client/server 代碼中,客戶端連接到服務器后,就可以等待用戶在 stdin 的輸入,然后將其發(fā)送到服務器。服務器收到客戶端數據,原封不動發(fā)回,客戶端再將其顯示到 stdout 上。在這個過程的前后,客戶端和服務器間有一些用于連接管理的 QUIC 報文,比如 PING。我們就略過,只看發(fā)送應用層數據的報文。下圖是客戶端發(fā)送的包含 “hello” 文本的報文:

    通過QUIC協議,來看看怎么學習網絡協議

    可以看到,這里 QUIC 報文是個 Short Header packet,除了 ACK frame 外,它還有一個 STREAM frame。這個 stream 的 stream ID 最低兩位是 00,代表是客戶端發(fā)起的,雙向的 stream。由于使用了兩位來表述類型,所以 QUIC 的 stream 有如下類型:

    通過QUIC協議,來看看怎么學習網絡協議

    我們看 STREAM frame 的 length(6) 和 Data(68 65 6c 6c 6f 0a)。Data 里的內容如果用 ASCII 表示,正好是 “hello<LF>”,它的長度是 6 個字節(jié)。

    服務器回復 “hello” 文本

    最后是服務器 echo back:

    通過QUIC協議,來看看怎么學習網絡協議

    這個和上面的報文如出一轍,就不解釋了。

    賢者時刻

    相信通過上面對照著 wireshark 抓包進行的 QUIC 簡介,能讓你對 QUIC 協議有一個初步的認識。上篇文章,我們說 QUIC 支持多路復用,并且解決了傳輸層隊頭阻塞的問題。通過這篇文章的介紹,你能回答以下兩個問題么?

    • QUIC 通過哪個 frame 類型來做多路復用的?

    • QUIC 如何解決傳輸層隊頭阻塞的?

    贊(0)
    分享到: 更多 (0)
    網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號