在 .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 = new DnsEndPoint(host, port); if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea))
{
//...
}
else if (saea.SocketError != SocketError.Success)
{
throw new SocketException((int)saea.SocketError);
} Socket socket = saea.ConnectSocket;
socket.NoDelay = true;
return (socket, new NetworkStream(socket, ownsSocket: true));
//...
}

用到了 SocketAsyncEventArgs ,但没有直接使用,而是继承它实现了 ConnectEventArgs :

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

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

// saea = 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 => Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea))
{
await saea.Builder.Task.ConfigureAwait(false);
}
}

上面就是连接 Socket 的代码,Socket.ConnectAsync 虽然方法名以 Async 结尾,但与通常的异步方法不同,它的返回类型不是 Task ,而是 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 ,则表示对应的网络 IO 操作是异步的;返回 false ,则是同步的。

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

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

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

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

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

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

当看明白这个巧妙之处后,不得不发出赞叹:高!实在是高!

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

cancellationWithConnectTimeout.CancelAfter(Settings._connectTimeout);
cancellationToken = cancellationWithConnectTimeout.Token;

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

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

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

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

.NET Core 2.1 源码学习:看 SocketsHttpHandler 如何在异步方法中连接 Socket的更多相关文章

  1. ASP.NET Core 2.1 源码学习之 Options[1]:Configure

    配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 Configur ...

  2. ASP.NET Core 2.1 源码学习之 Options[2]:IOptions

    在 上一章 中,介绍了Options的注册,而在使用时只需要注入 IOption<T> 即可: public ValuesController(IOptions<MyOptions& ...

  3. ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor

    前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...

  4. ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-monitor-in-asp-net-core. ...

  5. ASP.NET Core 2.1 源码学习之 Options[2]:IOptions 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-in-asp-net-core.html 在 上 ...

  6. ASP.NET Core 2.1 源码学习之 Options[1]:Configure 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-configure-in-asp-net-core.html 配置 ...

  7. ASP.NET Core 选项模式源码学习Options Configure(一)

    前言 ASP.NET Core 后我们的配置变得更加轻量级了,在ASP.NET Core中,配置模型得到了显著的扩展和增强,应用程序配置可以存储在多环境变量配置中,appsettings.json用户 ...

  8. TCP/IP协议栈源码图解分析系列10:linux内核协议栈中对于socket相关API的实现

    题记:本系列文章的目的是抛开书本从Linux内核源代码的角度详细分析TCP/IP协议栈内核相关技术 轻松搞定TCP/IP协议栈,原创文章欢迎交流, byhankswang@gmail.com linu ...

  9. ASP.NET Core 选项模式源码学习Options IOptions(二)

    前言 上一篇文章介绍IOptions的注册,本章我们继续往下看 IOptions IOptions是一个接口里面只有一个Values属性,该接口通过OptionsManager实现 public in ...

随机推荐

  1. java 线程间的通信 (wait / notify / notifyAll)

    package waitnotifytest; import java.util.Collections; import java.util.List; import com.google.commo ...

  2. AI创业的技术方案选择

    观察了许多初创公司技术方案的选择,我总结基本遵循8个字:快速灵活,物美价廉.我们也应该根据自身实际情况,跳出束缚与时俱进,选择智能互联网时代最有力的技术和工具. 基础编程语言 候选者:C#/C++/P ...

  3. 【转载】Hadoop官方文档翻译——HDFS Architecture 2.7.3

    HDFS Architecture HDFS Architecture(HDFS 架构) Introduction(简介) Assumptions and Goals(假设和目标) Hardware ...

  4. Oracle sql 统计

    1.按小时统计数量 SELECT TO_CHAR(RECEIVE_TIME,'HH24') HOUR ,COUNT(*) N FROM PH_PRESCRIPTION P WHERE TO_CHAR( ...

  5. 欢迎访问我的最新个人技术博客http://zhangxuefei.site

    博客已经搬家,欢迎访问我的最新个人技术博客:http://zhangxuefei.site

  6. java面试中被问到的问题

     技术面 1) Spring 的注解有哪些? 2) 你如何理解 spring IOC 技术,如果不使用spring,你有哪些方式来加载 bean? 3) spring aop 的原理是什么?它是怎样一 ...

  7. Vue.js之Vue计算属性、侦听器、样式绑定

    前言 上一篇介绍了Vue的基本概念,这一篇介绍一下Vue的基本使用. 一.搭建一个Vue程序 1.1 搭建Vue环境 搭建Vue的开发环境总共有三种方法: 引入CDN <script src=& ...

  8. Jmeter在非GUI环境下传递参数(命令行&Jenkins配置)

    https://www.cnblogs.com/kill0001000/p/8078686.html 通过cmd运行 jmeter -? 可以得到所有命令行选项(本文最后) 其中可以看到下面 -J 的 ...

  9. 服务器tail输出正常,vim打开中文乱码

    修改服务器端, 在/etc/vim/vimrc文件末尾加上 set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936set termencodi ...

  10. STS中如何使用lombok

    Lombok有什么用使用Lombok时需要注意的点Lombok的安装spring boot集成LombokLombok常用注解@NonNull@Cleanup@Getter/@Setter@Gette ...