原文 | Máňa,Natalia Kondratyeva

翻译 | 郑子铭

简化的 SocketsHttpHandler 配置

.NET 8 添加了更方便、更流畅的方式来使用 SocketsHttpHandler 作为 HttpClientFactory 中的主处理程序 (dotnet/runtime#84075)。

您可以使用 UseSocketsHttpHandler 方法设置和配置 SocketsHttpHandler。您可以使用 IConfiguration 从配置文件设置 SocketsHttpHandler 属性,也可以从代码中配置它,或者可以结合使用这两种方法。

请注意,将 IConfiguration 应用于 SocketsHttpHandler 时,仅解析 bool、int、Enum 或 TimeSpan 类型的 SocketsHttpHandler 属性。 IConfiguration 中所有不匹配的属性都将被忽略。配置仅在注册时解析一次并且不会重新加载,因此处理程序在应用程序重新启动之前不会反映任何配置文件更改。

// sets up properties on the handler directly
services.AddHttpClient("foo")
.UseSocketsHttpHandler((h, _) => h.UseCookies = false); // uses a builder to combine approaches
services.AddHttpClient("bar")
.UseSocketsHttpHandler(b =>
b.Configure(config.GetSection($"HttpClient:bar")) // loads simple properties from config
.Configure((h, _) => // sets up SslOptions in code
{
h.SslOptions.RemoteCertificateValidationCallback = delegate { return true; };
});
);
{
"HttpClient": {
"bar": {
"AllowAutoRedirect": true,
"UseCookies": false,
"ConnectTimeout": "00:00:05"
}
}
}

QUIC

OpenSSL 3 支持

当前大多数 Linux 发行版在其最新版本中都采用了 OpenSSL 3:

.NET 8 的 QUIC 支持已为此做好准备 (dotnet/runtime#81801)。

实现这一目标的第一步是确保 System.Net.Quic 下使用的 QUIC 实现 MsQuic 可以与 OpenSSL 3+ 一起使用。这项工作发生在 MsQuic 存储库 microsoft/msquic#2039 中。下一步是确保 libmsquic 包的构建和发布具有对特定发行版和版本的默认 OpenSSL 版本的相应依赖性。例如 Debian 发行版:

最后一步是确保正在测试正确版本的 MsQuic 和 OpenSSL,并且测试覆盖了所有 .NET 支持的发行版。

例外情况

在 .NET 7 中发布 QUIC API(作为预览功能)后,我们收到了几个有关异常的问题:

在 .NET 8 中,System.Net.Quic 异常行为在 dotnet/runtime#82262 中进行了彻底修改,并解决了上述问题。

修订的主要目标之一是确保 System.Net.Quic 中的异常行为在整个命名空间中尽可能一致。总的来说,当前的行为可以总结如下:

  • QuicException:特定于 QUIC 协议或与其处理相关的所有错误。

    • 连接在本地或由对等方关闭。
    • 连接因不活动而闲置。
    • 流在本地或由对等方中止。
    • QuicError 中描述的其他错误
  • SocketException:用于网络问题,例如网络状况、名称解析或用户错误。
    • 地址已被使用。
    • 无法到达目标主机。
    • 指定的地址无效。
    • 无法解析主机名。
  • AuthenticationException:适用于所有 TLS 相关问题。目标是具有与 SslStream 类似的行为。
    • 证书相关错误。
    • ALPN 协商错误。
    • 握手期间用户取消。
  • ArgumentException:当提供的 QuicConnectionOptionsQuicListenerOptions 无效时。
  • OperationCanceledException:每当 CancellationToken 被触发取消时。
  • ObjectDisposeException:每当在已释放的对象上调用方法时。

请注意,上述示例并不详尽。

除了改变行为之外,QuicException 也发生了改变。其中一项更改是调整 QuicError 枚举值。现在 SocketException 涵盖的项目已被删除,并添加了用户回调错误的新值 (dotnet/runtime#87259)。新添加的 CallbackError 用于区分 QuicListenerOptions.ConnectionOptionsCallback 引发的异常与 System.Net.Quic 引发的异常 (dotnet/runtime#88614)。因此,如果用户代码抛出 ArgumentException,QuicListener.AcceptConnectionAsync 会将其包装在 QuicException 中,并将 QuicError 设置为 CallbackError,并且内部异常将包含原始用户抛出的异常。它可以这样使用:

await using var listener = await QuicListener.ListenAsync(new QuicListenerOptions
{
// ...
ConnectionOptionsCallback = (con, hello, token) =>
{
if (blockedServers.Contains(hello.ServerName))
{
throw new ArgumentException($"Connection attempt from forbidden server: '{hello.ServerName}'.", nameof(hello));
} return ValueTask.FromResult(new QuicServerConnectionOptions
{
// ...
});
},
});
// ...
try
{
await listener.AcceptConnectionAsync();
}
catch (QuicException ex) when (ex.QuicError == QuicError.CallbackError && ex.InnerException is ArgumentException)
{
Console.WriteLine($"Blocked connection attempt from forbidden server: {ex.InnerException.Message}");
}

异常空间的最后一个更改是将传输错误代码添加到 QuicException 中 (dotnet/runtime#88550)。传输错误代码由 RFC 9000 传输错误代码定义,并且 MsQuic 的 System.Net.Quic 已经可以使用它们,只是没有公开公开。因此,QuicException 中添加了一个新的可为 null 的属性:TransportErrorCode。我们要感谢社区贡献者 AlexRach,他在 dotnet/runtime#88614 中实现了这一更改。

Sockets

套接字空间中最有影响力的更改是显着减少无连接 (UDP) 套接字的分配 (dotnet/runtime#30797)。使用 UDP 套接字时,分配的最大贡献者之一是在每次调用 Socket.ReceiveFrom 时分配一个新的 EndPoint 对象(并支持 IPAddress 等分配)。为了缓解这个问题,引入了一组使用 SocketAddress 的新 API (dotnet/runtime#87397)。 SocketAddress 在内部将 IP 地址保存为平台相关形式的字节数组,以便可以将其直接传递给操作系统调用。因此,在调用本机套接字函数之前不需要复制 IP 地址数据。

此外,新添加的 ReceiveFromReceiveFromAsync 重载不会实例化每次调用时都会有一个新的 IPEndPoint,而是在适当的位置改变提供的 receiveAddress 参数。所有这些一起可以用来提高 UDP 套接字代码的效率:

// Same initialization code as before, no change here.
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
byte[] message = Encoding.UTF8.GetBytes("Hello world!");
byte[] buffer = new byte[1024];
IPEndPoint endpoint = new IPEndPoint(IPAddress.Loopback, 12345);
server.Bind(endpoint); // --------
// Original code that would allocate IPEndPoint for each ReceiveFromAsync:
Task<SocketReceiveFromResult> receiveTaskOrig = server.ReceiveFromAsync(buffer, SocketFlags.None, endpoint);
await client.SendToAsync(message, SocketFlags.None, endpoint);
SocketReceiveFromResult resultOrig = await receiveTaskOrig; Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, result.ReceivedBytes) + " from " + result.RemoteEndPoint);
// Prints:
// Hello world! from 127.0.0.1:59769 // --------
// New variables that can be re-used for subsequent calls:
SocketAddress receivedAddress = endpoint.Serialize();
SocketAddress targetAddress = endpoint.Serialize(); // New code that will mutate provided SocketAddress for each ReceiveFromAsync:
ValueTask<int> receiveTaskNew = server.ReceiveFromAsync(buffer, SocketFlags.None, receivedAddress);
await client.SendToAsync(message, SocketFlags.None, targetAddress);
var length = await receiveTaskNew; Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, length) + " from " + receivedAddress);
// Prints:
// Hello world! from InterNetwork:16:{233,121,127,0,0,1,0,0,0,0,0,0,0,0}

最重要的是,在 dotnet/runtime#86872 中改进了 SocketAddress 的使用。 SocketAddress 现在有几个额外的成员,使其本身更有用:

  • getter Buffer:访问整个底层地址缓冲区。
  • setter Size:能够调整上述缓冲区大小(只能调整到较小的大小)。
  • static GetMaximumAddressSize:根据地址类型获取必要的缓冲区大小。
  • 接口 IEquatable:SocketAddress 可用于区分套接字与之通信的对等点,例如作为字典中的键(这不是新功能,它只是使其可通过接口调用)。

最后,删除了一些内部制作的 IP 地址数据副本,以提高性能。

网络原语

MIME 类型

添加缺失的 MIME 类型是网络空间中投票最多的问题之一 (dotnet/runtime#1489)。这是一个主要由社区驱动的更改,导致了 dotnet/runtime#85807 API 提案。由于此添加需要经过 API 审核流程,因此有必要确保添加的类型是相关的并遵循规范(IANA 媒体类型)。对于这项准备工作,我们要感谢社区贡献者 Bilal-iommarinchenko

IP网络

.NET 8 中添加的另一个新 API 是新类型 IPNetwork (dotnet/runtime#79946)。该结构允许指定 RFC 4632 中定义的无类 IP 子网。例如:

  • 127.0.0.0/8 用于对应于 A 类子网的无类定义。
  • 42.42.128.0/17 用于 215 个地址的无类别子网。
  • 2a01:110:8012::/100 用于 228 个地址的 IPv6 子网。

新的 API 可以使用构造函数从 IP 地址和前缀长度进行构造,也可以通过 TryParseParse 从字符串进行解析。最重要的是,它允许使用 Contains 方法检查 IP 地址是否属于子网。示例用法如下:

// IPv4 with manual construction.
IPNetwork ipNet = new IPNetwork(new IPAddress(new byte[] { 127, 0, 0, 0 }), 8);
IPAddress ip1 = new IPAddress(new byte[] { 255, 0, 0, 1 });
IPAddress ip2 = new IPAddress(new byte[] { 127, 0, 0, 10 });
Console.WriteLine($"{ip1} {(ipNet.Contains(ip1) ? "belongs" : "doesn't belong")} to {ipNet}");
Console.WriteLine($"{ip2} {(ipNet.Contains(ip2) ? "belongs" : "doesn't belong")} to {ipNet}");
// Prints:
// 255.0.0.1 doesn't belong to 127.0.0.0/8
// 127.0.0.10 belongs to 127.0.0.0/8 // IPv6 with parsing.
IPNetwork ipNet = IPNetwork.Parse("2a01:110:8012::/96");
IPAddress ip1 = IPAddress.Parse("2a01:110:8012::1742:4244");
IPAddress ip2 = IPAddress.Parse("2a01:110:8012:1010:914e:2451:16ff:ffff");
Console.WriteLine($"{ip1} {(ipNet.Contains(ip1) ? "belongs" : "doesn't belong")} to {ipNet}");
Console.WriteLine($"{ip2} {(ipNet.Contains(ip2) ? "belongs" : "doesn't belong")} to {ipNet}");
// Prints:
// 2a01:110:8012::1742:4244 belongs to 2a01:110:8012::/96
// 2a01:110:8012:1010:914e:2451:16ff:ffff doesn't belong to 2a01:110:8012::/96

请注意,不应将此类型与自 1.0 以来 ASP.NET Core 中存在的 Microsoft.AspNetCore.HttpOverrides.IPNetwork 类混淆。我们预计 ASP.NET API 最终将迁移到新的 System.Net.IPNetwork 类型 (dotnet/aspnetcore#46157)。

最后的注释

本博文选择的主题并不是 .NET 8 中所做的所有更改的详尽列表,只是我们认为可能最有趣的主题。如果您对性能改进更感兴趣,您应该查看 Stephen 的大型性能博客文章中的网络部分。如果您有任何疑问或发现任何错误,您可以在 dotnet/runtime 存储库中与我们联系。

最后,我要感谢我的合著者:

原文链接

.NET 8 Networking Improvements

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com)

【译】.NET 8 网络改进(三)的更多相关文章

  1. 【译】.NET 6 网络改进

    原文 | Máňa Píchová 翻译 | 郑子铭 对于 .NET 的每个新版本,我们都希望发布一篇博客文章,重点介绍网络的一些变化和改进.在这篇文章中,我很高兴谈论 .NET 6 中的变化. 这篇 ...

  2. 35 网络相关函数(三)——live555源码阅读(四)网络

    35 网络相关函数(三)——live555源码阅读(四)网络 35 网络相关函数(三)——live555源码阅读(四)网络 简介 5)NoReuse不重用地址类 6)initializeWinsock ...

  3. Android系列之网络(三)----使用HttpClient发送HTTP请求(分别通过GET和POST方法发送数据)

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  4. Linux网络编程(三)

    Linux网络编程(三) wait()还是waitpid() Linux网络编程(二)存在客户端断开连接后,服务器端存在大量僵尸进程.这是由于服务器子进程终止后,发送SIGCHLD信号给父进程,而父进 ...

  5. PLSQL连接ORACLE配置字符串简介 oracle网络配置 三个配置文件 listener.ora、sqlnet.ora、tnsnames.ora原理解释

    PLSQL连接ORACLE配置字符串简介 oracle网络配置 三个配置文件 listener.ora.sqlnet.ora.tnsnames.ora原理解释 oracle网络配置三个配置文件 lis ...

  6. LwIP协议栈开发嵌入式网络的三种方法分析

    LwIP协议栈开发嵌入式网络的三种方法分析   摘要  轻量级的TCP/IP协议栈LwIP,提供了三种应用程序设计方法,且很容易被移植到多任务的操作系统中.本文结合μC/OS-II这一实时操作系统,以 ...

  7. VMware网络配置三种网络模式(桥接、NAT、Host-only)

    VMware网络配置三种网络模式(桥接.NAT.Host-only) 一.虚拟安装后三种网络模式显示 当安装好后,的“虚拟网络编辑器”中也存在三种模式,分别对应:桥接-VMnet0.Host-only ...

  8. Kubernetes 网络改进的三项实践分享

    自研CNI IPAM插件 解决K8s功能问题 首先,在功能方面,Kubernetes 网络模型由于IP不固定,无法对IP资源进行精细管控,无法使用基于IP的监控和基于IP的安全策略,此外,一些IP发现 ...

  9. Python for Infomatics 第12章 网络编程三(译)

    注:文章原文为Dr. Charles Severance 的 <Python for Informatics>.文中代码用3.4版改写,并在本机测试通过. 12.5 HTML分析和网页抓取 ...

  10. Java高并发网络编程(三)NIO

    从Java 1.4开始,Java提供了新的非阻塞IO操作API,用意是替代Java IO和Java Networking相关的API. NIO中有三个核心组件: Buffer缓冲区 Channel通道 ...

随机推荐

  1. Python数据可视化-地图可视化

    Python数据可视化-地图可视化 一.基础地图使用 基础地图演示 二.疫情地图-国内疫情地图 具体代码如下 """ 演示全国疫情可视化地图开发 "" ...

  2. TOEFL | 202307 改革 · 新版题型总结

    目录 Listening(36min) Reading(35min) Speaking(16min) Writing(29min) Listening(36min) 2 conversation,3 ...

  3. Could not get a resource from the pool 异常定位和解决

    最近在服务中经常看到以下错误,进行下定位和问题解决分析: 2023-12-08 00:10:58.248 WARN [terra-sr-server,a9006fd27ccb81d0,a9006fd2 ...

  4. APB Slave设计

    APB Slave位置 实现通过CPU对于APB Slave读写模块进行读写操作 规格说明 不支持反压,即它反馈给APB的pready信号始终为1 不支持错误传输,就是说他反馈给APB总线的PSLVE ...

  5. QT启动问题--找不到python36.dll-cnblog

    1.报错:找不到python36.dll 2.解决 通过该查询CSDN下载相应的python36.dll放到C:\Windows\System32目录下即可 https://blog.csdn.net ...

  6. MyBatis05——一对多和多对一处理

    多对一处理 1.数据库表的设计 CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRI ...

  7. 使用JavaStream将List转为Map

    有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 系列文章地址 使用Java Stream将List转换为Map ...

  8. 达梦数据库varchar和nvarchar的验证

    达梦数据库varchar和nvarchar的验证 测试SQL create tablespace zhaobsh datafile '/opt/dmdbms/data/DAMENG/zhaobsh.d ...

  9. [转帖]PyCharm无法安装第三方模块,一直提示 updating list:time out 解决办法

    Pycharm无法安装第三方模块解决办法: 1.打开pycharm的项目的venv文件夹 2.打开文件夹目录中的pyvenv文件 3.将文件中的include-system-site-packages ...

  10. [转帖]SQL Server JDBC – Set sendStringParametersAsUnicode to false

    https://vladmihalcea.com/sql-server-jdbc-sendstringparametersasunicode/ https://learn.microsoft.com/ ...