1. 前言

.NET 6 实现了对 HTTP/3 的预览支持:

  • Kestrel、HTTP.Sys 和 IIS,用于 ASP.NET 的服务器场景
  • HttpClient 中发起请求
  • gRPC

2. 什么是Http/3

HTTP 通过 1.1 版是一个相对简单的协议,打开一个 TCP 连接,通过明文发送一组标头,然后接收响应。请求可以通过同一连接进行流水线处理,但每个请求都必须按顺序处理。TLS 增加了一些额外的复杂性,并增加了几次往返来启动连接,但是一旦建立,HTTP 就会通过安全通道以相同的方式使用。

由于许多站点已经开始要求使用 TLS 加密,并且您只能通过 HTTP/1.1 为每个连接一次提供一个请求,因此通常需要下载多个资源(脚本、图像、CSS、字体等)的网页的性能被降低了由于需要多个连接而受到限制,每个连接的设置成本都很高。

HTTP/2 通过使用成帧概念更改为二进制协议来解决该问题,从而允许在同一连接上同时处理多个请求。TLS 的设置成本可以一次性支付,然后所有请求都可以在该单个连接上交错。

这一切都很好,只是在移动移动时代,现在大部分访问来自使用Wi-Fi和移动网络的手机和平板电脑,这可能不可靠。虽然 HTTP/2 支持多个流,但它们都通过一个经过 TLS 加密的连接,因此如果 TCP 数据包丢失,所有流都会被阻止,直到数据可以恢复。这被称为行首阻塞问题。

HTTP/3 通过使用称为 QUIC 的新底层连接协议解决了这些问题。QUIC 使用 UDP 并内置了 TLS,因此建立连接的速度更快,因为 TLS 握手是连接的一部分。每帧数据都是独立加密的,因此在丢包的情况下不再有行首阻塞。与 TCP 不同,QUIC 连接独立于 IP 地址,因此移动客户端可以在 wifi 和蜂窝网络之间漫游,保持相同的逻辑连接,继续长时间下载等。

3. QUIC

QUIC被设计为 HTTP/3 的基础层,但它也可以被其他协议使用。它设计用于移动设备,具有处理网络变化的能力,并且在发生数据包丢失时具有良好的恢复能力。

.NET 使用MSQuic库来实现 QUIC。这是来自 Windows 网络团队的开源跨平台库。出于打包原因,它包含在 Windows 的 .NET 6 中,并作为单独的包用于 Linux。

与 QUIC 的一个主要区别是内置了 TLS 加密,因此连接建立包括 TLS 握手。这意味着使用的 TLS 库需要提供 API 来启用这种类型的握手。对于 Windows,API 包含在 SChannel/Bcrypt.dll 中。对于 Linux,它有点复杂——由 .NET 和 Linux 上的大多数其他软件使用的 OpenSSL 尚未包含这些 API。OpenSSL 团队一直在致力于 OpenSSL 3.0,它有一个严格的截止日期来提交 FIPS 140-2 认证,因此无法直接在 OpenSSL 3.0 中添加该支持。

在 .NET 6 中,微软并没有公开.NET QUIC API,目标是在 .NET 7 中公开它们。QUIC 可以像 TCP 套接字一样使用,并且不特定于 HTTP/3,因此微软希望其他协议能够随着时间的推移建立在 QUIC 上,例如SMB over QUIC。

4. 如何使用

要使用 HTTP/3,需要安装 MSQuic 的必备版本及其 TLS 依赖项。

4.1 Windows

MsQuic 作为 .NET 6 的一部分安装,但它需要提供 TLS API 的 Schannel SSP 的更新版本,这随操作系统的最新版本一起提供。

Windows 11 Build 22000 或更高版本,或 Server 2022 RTM

4.2 Linux

在 Linux 上,libmsquic 是通过 Microsoft 官方 Linux 包存储库 packages.microsoft.com 发布的。为了消耗它,必须手动添加。请参阅Microsoft 产品的 Linux 软件存储库。配置软件包提要后,可以通过软件包管理器为您的发行版安装它,例如,在 Ubuntu 上:

sudo apt install libmsquic

4.3 Kestrel

Kestrel 中包含服务器支持。需要使用以下项目属性启用预览功能:

<PropertyGroup>
  <EnablePreviewFeatures>True</EnablePreviewFeatures>
</PropertyGroup>

然后在监听器选项中设置,例如:

public static async Task Main(string[] args)
{
  var builder = WebApplication.CreateBuilder(args);
  builder.WebHost.ConfigureKestrel((context, options) =>
  {
    options.Listen(IPAddress.Any, 5001, listenOptions =>
    {
      // Use HTTP/3
      listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
      listenOptions.UseHttps();
    });
  });
}

有关更多详细信息,请参阅将 HTTP/3 与 ASP.NET Core Kestrel Web 服务器一起使用

4.4 HttpClient

HttpClient 已更新为包括对 HTTP/3 的支持,但需要使用运行时标志启用它。在项目文件中包含以下内容以使用 HttpClient 启用 HTTP/3:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
</ItemGroup>

需要将 HTTP/3 指定为请求的版本:

// See https://aka.ms/new-console-template for more information
using System.Net;

var client = new HttpClient();
client.DefaultRequestVersion = HttpVersion.Version30;
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;

var resp = await client.GetAsync("https://localhost:5001/");
var body = await resp.Content.ReadAsStringAsync();

Console.WriteLine($"status: {resp.StatusCode}, version: {resp.Version}, body: {body.Substring(0, Math.Min(100, body.Length))}");

HTTP/3 通过 HTTP.sys 和 IIS