この記事では、QUIC プロトコルを理解し、QUIC プロトコルを例としてネットワーク プロトコルの学習方法について説明します。皆様のお役に立てれば幸いです。
s2n-quic に関する以前の記事で、読者から QUIC のようなネットワーク プロトコルを学ぶ方法について質問がありました。ほとんどのインターネット実務者にとって、毎日インターネットを扱っていますが、HTTP のネットワーク プロトコルの詳細を気にする人はほとんどおらず、ほとんどの場合、一般的な理解と使用方法を知っていれば十分です。 QUIC についてよく知らない場合は、次の図を参照すると、HTTP/3 エコシステムにおける QUIC の位置をよく理解できます。 QUIC について詳しく知りたいのですが、どうやって始めればよいですか?
ネットワーク プロトコルとネットワーク機器の元開発者としての私自身の経験では、RFC から始めて、Wireshark パケット キャプチャで補足すると、ターゲット プロトコルをすぐにマスターできます。
QUIC の場合、最初に読む必要があるのは RFC9000 です。契約書を読むのは非常に退屈で、ある程度の忍耐力が必要ですが、英語があまり得意でない場合は、Google 翻訳を使用して中国語に翻訳し、すぐに閲覧することができます (多読)。最初に読むのは主に、主要な概念と主要なプロセスを理解することです。 その後、QUIC プロトコルを使用してプログラムを作成し、Wireshark でパケットをキャプチャし、実際のメッセージを調べて RFC プロトコルの内容と比較する (精読) ことで、次のことが得られます。プロトコルの本質をより深く理解します。 前回の記事のコードに基づいて、引き続きエコー クライアントとサーバーを構築します。誰でも読みやすいように、コードも掲載しました。興味のある学生は、私のリポジトリを複製して、クライアント/サーバー コードを実行できます。 クライアント コード (github: tyrchen/rust-training/live_coding/quic-test/examples/client.rs
を参照):サーバーコード (
github: tyrchen/rust-training/live_coding/quic-test/examples/server.rsを参照):
これら 2 つコードの一部は、最も単純なエコー サーバーを構築します。 Wireshark を使用すると、ローカル ループバック インターフェイスで UDP パケットを監視し、パケットをキャプチャできます。 QUIC などの TLS プロトコルを使用するトラフィックの場合、パケットがキャプチャされたとしても、最初の数パケットのみが読み取れる可能性があり、後続のパケットは暗号化されて読み取れないことに注意してください。したがって、クライアント/サーバーを構築するときは、Wireshark が復号化できるように、サーバーとクライアントの間でネゴシエートされたセッション キーをキャプチャする方法を見つける必要があります。通常、SSL/TLS ライブラリはこの機能を提供します。たとえば、Rustls の場合、tls config で key_log を使用できます。上記のサーバー コードを注意深く見ると、次の文が表示されます:
let config = Builder::new() .with_certificate(CERT_PEM, KEY_PEM)? .with_key_logging()? # 使能 keylogging .build()?;
key_log を使用した後、サーバーを起動するとき、SSLKEYLOGFILE:
SSLKEYLOGFILE=session.log cargo run --example server
以下は、クライアントとサーバー間の完全な対話です。パケットを確認すると、すべての「保護されたペイロード」が正常に表示されていることがわかります。
エコー クライアントは最も単純なアクション (双方向ストリームを開くだけ) のみを実行するため、このパケットを通じてキャプチャすると、QUIC プロトコルによる接続を確立するプロセスの学習に集中できます。
#クライアントによって送信された最初のパケット
クライアントによって送信された最初のパケットを見てみましょう:
このメッセージには非常に豊富な情報が含まれています。まず、TCP ハンドシェイクとは異なり、QUIC の最初のパケットは非常に大きく、QUIC ヘッダー、255 バイトの CRYPTO フレーム、および 890 バイトを含む 1200 バイトにもなります (プロトコルには少なくとも 1200 バイトの UDP ペイロードが必要です)。バイトの PADDING フレーム。ヘッダーからわかるように、この QUIC パッケージのタイプは Initial です。QUIC メッセージ タイプ
QUIC パケットの場合、ヘッダーはプレーン テキストであり、後続のすべてのフレーム ペイロードは暗号文です (ヘッダーの一部を除く)。パッケージ)。この最初のパケットは長いヘッダー メッセージであることがわかります。RFC9000 のセクション 17.2 では、長いヘッダー パケットが定義されています: 既然有 Long Header packet,那么就有 Short Header packet,Short Header packet 目前的版本只有一种: 为什么需要 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)的变化,并不会引发 QUIC 连接的中断,也就是说,你从家里开车出门,即便手机的网络从 WIFI(固网运营商分配给你的 IP)切换到蜂窝网络(移动运营商分配给你的 IP),整个 UDP/IP 网络变化了,但你的 QUIC 应用只会感受到细微的延迟,并不需要重新建立 QUIC 连接。 从这个使用场景来看,QUIC 底层使用无连接的 UDP 是非常必要的。 首包中就包含了 TLS hello? 我们接下来看看 CRYPTO frame: 可以看到,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: 服务器的回包 我们来看 server 的回包: 这里有一些新东西。首先,一个 UDP 包内部可以包含若干个 QUIC payload,我们看到 server 回复了一个 QUIC Initial 报文和一个 QUIC Handshake 报文。在 Initial 报文中,我们看到了一个 ACK frame,可见 QUIC 虽然构建于 UDP,但在 QUIC 协议内部构建了类似 TCP 的确认机制。 我们之前看到,在 Initial 报文的 CRYPTO frame 中,客户端发送了 TLS Client Hello,同样的,服务器在 Initial 报文的 CRYPTO frame 中发送了 TLS Server Hello。这个我们就略过不看。 在 Handshake 报文中: 服务器发送了自己的证书,并结束了 TLS handshake。 客户端结束 Handshake 我们再看第三个包,客户端发送给服务器结束 TLS 握手: 这个包依旧包含两个 QUIC 报文,其中第一个就是一个 ACK frame,来确认收到了服务器的 Server Hello 那个 QUIC 报文;第二个包含一个 ACK frame,确认服务器的 Handshake,随后有一个 CRYPTO frame 结束客户端的 TLS handshake。 TLS 握手结束之后,客户端和服务器就开始应用层的数据交换,此刻,所有数据都是加密的。 客户端发送一个 “hello” 文本 在我们的 echo client/server 代码中,客户端连接到服务器后,就可以等待用户在 stdin 的输入,然后将其发送到服务器。服务器收到客户端数据,原封不动发回,客户端再将其显示到 stdout 上。在这个过程的前后,客户端和服务器间有一些用于连接管理的 QUIC 报文,比如 PING。我们就略过,只看发送应用层数据的报文。下图是客户端发送的包含 “hello” 文本的报文: ご覧のとおり、ここでの QUIC メッセージは Short Header パケットであり、ACK フレームに加えて STREAM フレームもあります。このストリームのストリーム ID の下 2 桁は 00 で、クライアントによって開始された双方向ストリームを表します。型を表すのに 2 ビットが使用されるため、QUIC のストリームには次の型があります。 length(6) と Data(68 65 6c 6c 6f 0a of STREAM フレーム)。 Dataの内容をASCIIで表現すると、まさに「hello #サーバーは「hello」というテキストを返信します#最終的にサーバーは次のようにエコー バックします: これは上記のメッセージと全く同じなので説明は省略します。 Sage Moment Wireshark パケット キャプチャに基づいた上記の QUIC の紹介は、QUIC プロトコルの予備的な理解に役立つと思います。 。 知る。前回の記事では、QUIC が多重化をサポートし、トランスポート層でのキューヘッドのブロッキングの問題を解決すると述べました。この記事の導入を通して、次の 2 つの質問に答えることができますか? 以上がQUIC プロトコルを通じてネットワーク プロトコルを学習する方法を見てみましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。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 (..),
}
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..),
}
QUIC は多重化にどのフレーム タイプを使用しますか?
Web サーバーのセキュリティ