原文 | 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. P5723 注意特殊情况

    https://www.luogu.com.cn/problem/P5723 不是难题,但是倘若忽略L<2的情况就无法AC,Lougu得分只有80.因此写完题后一定要把各种边界性质的数据想出并用 ...

  2. sipp3.6 on centos7安装部署

    概述 在VOIP软交换的开发过程中,必然需要对软交换进行批量压测. SIP压测工具一般都是sipp,免费,开源,功能足够强大,配置灵活,优点多. 环境 centos7.9 cmake3.6 sipp ...

  3. java基础(16)--super与this

    一.this简介 1.this.  this() 2.静态方法无法使用 3.不省略的情况:区分局部变量与实例变量,比如set方法中用到   二.super简介 1.只能出现在实例方法或构造方法中 2. ...

  4. NLP复习之神经网络

    NLP复习之神经网络 前言 tips: 设计神经网络时,输入层与输出层节点数往往固定,中间层可以自由指定: 神经网络中的拓扑与箭头代表预测过程数据流向,与训练的数据流有一定区别: 我们不妨重点关注连接 ...

  5. Https 原理与工作流程及证书链校验

    本文为博主原创,未经允许不得转载: 目录 HTTP传输三大风险 安全通信原则 HTTPS定义 TLS/SSL 协议及加密算法 HTTPS工作流程 HTTPS协议和HTTP协议的区别 CA机构 证书链校 ...

  6. SV 字符串类型

    概述 常见使用方式 string b; string b=""; // 拼接字符串 string a = {"hi",b}; // 将字符串a赋值给[15:0] ...

  7. android应用申请加入电池优化白名单

    首先,在 AndroidManifest.xml 文件中配置一下权限: 1 <uses-permission android:name="android.permission.REQU ...

  8. [转帖]ORACLE恢复神器之ODU/AUL/DUL

    https://www.cnblogs.com/oracle-dba/p/3873870.html 分享ORACLE数据库恢复神器之ODU.DUL和AUL工具. ODU:ORACLE DATABASE ...

  9. [转帖]badboy与jmeter的结合使用

    https://blog.csdn.net/weixin_41754309/article/details/107106855 欢迎关注[无量测试之道]公众号,回复[领取资源], Python编程学习 ...

  10. 时间片 线程切换 指令周期 流水线 TPS的初步了解

    时间片 线程切换 指令周期 流水线 TPS的初步了解 情况说明 Redis 单线程提供服务, 可以支撑十万级别的TPS 通过以个非常简单的测试 redis-benchmark -c 50 -n 500 ...