热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

.NETCore2.1源码学习:看SocketsHttpHandler如何在异步方法中连接Socket

在.NETCore2.1中,System.Net.Sockets的性能有了很大的提升,最好的证明是Kestrel与HttpClient都改为使用Syst

在 .NET Core 2.1 中,System.Net.Sockets 的性能有了很大的提升,最好的证明是 Kestrel 与 HttpClient 都改为使用 System.Net.Sockets ,stackoverflow 上也有人提到了,详见 libuv vs sockets in asp.net core 2.1 。

这两天阅读了 corefx 中 HttpClient 的 SocketsHttpHandler 部分实现代码,学习了一下它如何在异步方法中连接 Socket 。

连接 Socket 是在 ConnectHelper 的 ConnectAsync 异步方法中实现的:

public static async ValueTask<(Socket, Stream)> ConnectAsync(string host, int port, CancellationToken cancellationToken)
{ConnectEventArgs saea;
//..
saea.Initialize(cancellationToken);saea.RemoteEndPoint &#61; new DnsEndPoint(host, port);if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea)){//...
}else if (saea.SocketError !&#61; SocketError.Success){throw new SocketException((int)saea.SocketError);}Socket socket &#61; saea.ConnectSocket;socket.NoDelay &#61; true;return (socket, new NetworkStream(socket, ownsSocket: true));//...
}

用到了 SocketAsyncEventArgs &#xff0c;但没有直接使用&#xff0c;而是继承它实现了 ConnectEventArgs &#xff1a;

private sealed class ConnectEventArgs : SocketAsyncEventArgs
{
public AsyncTaskMethodBuilder Builder { get; private set; }public CancellationToken CancellationToken { get; private set; }public void Initialize(CancellationToken cancellationToken){CancellationToken &#61; cancellationToken;var b &#61; new AsyncTaskMethodBuilder();var ignored &#61; b.Task; // force initializationBuilder &#61; b;}public void Clear() &#61;> CancellationToken &#61; default;protected override void OnCompleted(SocketAsyncEventArgs _){ /* ... */ }
}

ConnectEventArgs 中出现了一个之前从未见过的身影 —— AsyncTaskMethodBuilder &#xff0c;而且它存在的目的让人费解 —— 为了让无法进行 await 的 SocketAsyncEventArgs 可以 await&#xff08;见下面代码中的 await 部分&#xff09;&#xff0c;为什么要这样&#xff1f;带着这个疑问继续看代码。

// saea &#61; new ConnectEventArgs();
if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea))
{
// Connect completing asynchronously. Enable it to be canceled and wait for it.using (cancellationToken.Register(s &#61;> Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea)){await saea.Builder.Task.ConfigureAwait(false);}
}

上面就是连接 Socket 的代码&#xff0c;Socket.ConnectAsync 虽然方法名以 Async 结尾&#xff0c;但与通常的异步方法不同&#xff0c;它的返回类型不是 Task &#xff0c;而是 bool 。

// Returns true if the I/O operation is pending. The System.Net.Sockets.SocketAsyncEventArgs.Completed
// event on the e parameter will be raised upon completion of the operation.
// Returns false if the I/O operation completed synchronously.

如果返回 true &#xff0c;则表示对应的网络 IO 操作是异步的&#xff1b;返回 false &#xff0c;则是同步的。

如果是异步 IO 操作&#xff0c;完成后会通过 Completed 事件通知 SocketAsyncEventArgs &#xff0c;但这里是在 async 异步方法中调用的&#xff0c; SocketAsyncEventArgs 没有提供可以 await 的异步方法&#xff0c;那如何 await ?

答案就是之前让人疑惑的在 ConnectEventArgs 中引入的 AsyncTaskMethodBuilder &#xff0c;用它的 Task 进行 await 。

await saea.Builder.Task.ConfigureAwait(false);

但仅仅 await 这个什么也不干的 Task &#xff0c;即使等到天荒地老&#xff0c;也等不到。而我们希望在连接 Socket 的异步 IO 操作完成后&#xff0c;就立即唤醒继续执行。

ConnectEventArgs 通过在 OnCompleted 事件处理方法中将所等待的 Task 的状态设置为 completed &#xff0c;巧妙地解决了这个问题。

protected override void OnCompleted(SocketAsyncEventArgs _)
{
switch (SocketError){case SocketError.Success:Builder.SetResult();break;//....
}
}

当看明白这个巧妙之处后&#xff0c;不得不发出赞叹&#xff1a;高&#xff01;实在是高&#xff01;

连接 Socket 除了异步等待的问题&#xff0c;还有一个连接超时的问题&#xff0c;这里是通过 CancellationToken 解决的&#xff0c;根据超时时间设置&#xff0c;创建一个 CancellationToken &#xff1a;

cancellationWithConnectTimeout.CancelAfter(Settings._connectTimeout);
cancellationToken
&#61; cancellationWithConnectTimeout.Token;

然后在 await 时用它来处理连接超时

using (cancellationToken.Register(s &#61;> Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea))
{
await saea.Builder.Task.ConfigureAwait(false);
}

这次 .NET Core 源码学习就到这里&#xff0c;最大收获就是见识了高手是怎么玩转 C# 异步编程的。

.NET Core 开源给 .NET 开发者带来的福音之一就是可以从世界顶尖高手写的 C# 代码中学习。

转:https://www.cnblogs.com/dudu/p/9160729.html



推荐阅读
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • 本文介绍了如何在Spring框架中使用AspectJ实现AOP编程,重点讲解了通过注解配置切面的方法,包括方法执行前和方法执行后的增强处理。阅读本文前,请确保已安装并配置好AspectJ。 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 短视频app源码,Android开发底部滑出菜单首先依赖三方库implementationandroidx.appcompat:appcompat:1.2.0im ... [详细]
  • PBO(PixelBufferObject),将像素数据存储在显存中。优点:1、快速的像素数据传递,它采用了一种叫DMA(DirectM ... [详细]
  • vue引入echarts地图的四种方式
    一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom&amp;quot;echarts&amp;quot;;4、如果用到map(地图),还 ... [详细]
  • WCF类型共享的最佳实践
    在使用WCF服务时,经常会遇到同一个实体类型在不同服务中被生成为不同版本的问题。本文将介绍几种有效的类型共享方法,以解决这一常见问题。 ... [详细]
  • PHP 5.5.31 和 PHP 5.6.17 安全更新发布
    PHP 5.5.31 和 PHP 5.6.17 已正式发布,主要包含多个安全修复。强烈建议所有用户尽快升级至最新版本以确保系统安全。 ... [详细]
  • 本文将深入探讨 iOS 中的 Grand Central Dispatch (GCD),并介绍如何利用 GCD 进行高效多线程编程。如果你对线程的基本概念还不熟悉,建议先阅读相关基础资料。 ... [详细]
  • C#实现文件的压缩与解压
    2019独角兽企业重金招聘Python工程师标准一、准备工作1、下载ICSharpCode.SharpZipLib.dll文件2、项目中引用这个dll二、文件压缩与解压共用类 ... [详细]
  • Spring Data JdbcTemplate 入门指南
    本文将介绍如何使用 Spring JdbcTemplate 进行数据库操作,包括查询和插入数据。我们将通过一个学生表的示例来演示具体步骤。 ... [详细]
  • 我有一个从C项目编译的.o文件,该文件引用了名为init_static_pool ... [详细]
  • 为什么多数程序员难以成为架构师?
    探讨80%的程序员为何难以晋升为架构师,涉及技术深度、经验积累和综合能力等方面。本文将详细解析Tomcat的配置和服务组件,帮助读者理解其内部机制。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • JUC(三):深入解析AQS
    本文详细介绍了Java并发工具包中的核心类AQS(AbstractQueuedSynchronizer),包括其基本概念、数据结构、源码分析及核心方法的实现。 ... [详细]
author-avatar
捕鱼达人2602914975
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有