이 기사에서는 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):
이 두 코드 조각은 가장 간단한 에코 서버를 구축합니다. Wireshark를 사용하여 로컬 루프백 인터페이스에서 UDP 패킷을 모니터링하고 패킷을 캡처할 수 있습니다. QUIC과 같은 TLS 프로토콜을 사용하는 트래픽의 경우 패킷이 캡처되더라도 처음 몇 개의 패킷만 읽을 수 있으며 이후 패킷은 암호화되어 읽을 수 없습니다. 따라서 클라이언트/서버를 구축할 때 Wireshark가 해독할 수 있도록 서버와 클라이언트 간에 협상된 세션 키를 캡처하는 방법을 찾아야 합니다. 일반적으로 SSL/TLS 라이브러리가 이 기능을 제공합니다. 예를 들어 Rustls의 경우 tls 구성에서 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
패킷 캡처가 완료된 후 Wireshark 기본 설정을 열고 선택
다음은 클라이언트와 서버 간의 상호 작용에 대한 전체 패킷 캡처입니다. :
에코 클라이언트는 가장 간단한 작업(양방향 스트림만 열기)만 수행하므로 이 패킷 캡처를 통해 QUIC 프로토콜을 통해 연결을 설정하는 프로세스를 연구하는 데 집중할 수 있습니다.
클라이언트가 보낸 첫 번째 패킷
클라이언트가 보낸 첫 번째 메시지를 살펴보겠습니다.
이 메시지에는 매우 풍부한 정보가 포함되어 있습니다. 우선, TCP 핸드셰이크와 달리 QUIC의 첫 번째 패킷은 QUIC 헤더, 255바이트 CRYPTO 프레임, 바이트 PADDING 프레임. 헤더에서 볼 수 있듯이 이 QUIC 패키지의 유형은 초기입니다.
QUIC 메시지 유형
QUIC 패킷의 경우 헤더는 일반 텍스트이고 모든 후속 프레임 페이로드는 암호 텍스트입니다(처음 몇 개의 패킷 제외). 이 첫 번째 패킷은 긴 헤더 메시지입니다. RFC9000의 섹션 17.2에는 긴 헤더 패킷이 정의되어 있습니다.
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의 해당 장을 직접 읽어 보세요. 긴 헤더 메시지의 경우 다음 유형이 있습니다.
既然有 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)的变化,并不会引发 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 메시지는 짧은 헤더 패킷이며 ACK 프레임 외에도 STREAM 프레임도 있습니다. 이 스트림의 스트림 ID 중 가장 낮은 두 자리는 00입니다. 이는 클라이언트가 시작한 양방향 스트림임을 의미합니다. 2비트를 사용하여 유형을 표현하므로 QUIC의 스트림에는 다음과 같은 유형이 있습니다.
STREAM 프레임의 길이(6)와 데이터(68 65 6c 6c 6f 0a)를 살펴보겠습니다. Data의 내용을 ASCII로 표현하면 정확히 "hello
서버가 "hello" 문자로 응답합니다
마지막으로 서버가 다음과 같이 반향합니다.
위 메시지와 완전히 동일하므로 설명하지 않겠습니다.
Sage Moment
위의 Wireshark 패킷 캡처 기반 QUIC 소개를 통해 QUIC 프로토콜에 대한 예비 이해를 얻을 수 있다고 믿습니다. 지난 기사에서 우리는 QUIC가 멀티플렉싱을 지원하고 전송 계층에서 대기열 선두 차단 문제를 해결한다고 말했습니다. 이 기사의 서론을 통해 다음 두 가지 질문에 답할 수 있습니까?
QUIC은 멀티플렉싱에 어떤 프레임 유형을 사용하나요?
QUIC 전송 계층에서 대기열 선두 차단을 해결하는 방법은 무엇입니까?
관련 권장 사항: 웹 서버 보안
위 내용은 QUIC 프로토콜을 통해 네트워크 프로토콜을 학습하는 방법을 살펴보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!