在 .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. XCode各种问题

    2018.07.10 1.clang: warning: libstdc++ is deprecated; move to libc++ [-Wdeprecated] 2.2018.07.29  海康 ...

  2. 每天一个linux命令(9):touch

    1.命令简介 touch命令将每个文件的访问时间和修改时间改为当前时间. 2.用法 touch [选项]... 文件... 3.选项 -a 只更改访问时间 -c, --no-create 不创建任何文 ...

  3. webview调起浏览器

    调起浏览器 url = "intent://" + url +"#Intent;scheme=http;action=android.intent.action.VIEW ...

  4. 【转载】Linux 命令行快捷键 - 移动光标

    Linux 命令行快捷键 - 移动光标 涉及在linux命令行下进行快速移动光标.命令编辑.编辑后执行历史命令.Bang(!)命令.控制命令等.让basher更有效率. 常用 ctrl+左右键:在单词 ...

  5. (8) MySQL主从复制架构使用方法

    一. 单个数据库服务器的缺点 数据库服务器存在单点问题 数据库服务器资源无法满足增长的读写请求 高峰时数据库连接数经常超过上限 二. 如何解决单点问题 增加额外的数据库服务器,组建数据库集群 同一集群 ...

  6. 10个基本的HTML5动画工具设计

    HTML5已经成为最流行的编程语言在web开发者.强大的编程语言有很大的能力,生产更好的万维网内容.HTML5的兴起已经在过去三年增长迅速.介绍了HTML5的新技术是更好的.HTML5技术是由像Chr ...

  7. 60cms Cookies欺骗漏洞审计

    源码地址:https://files.cnblogs.com/files/ssooking/60cms.zip 运行60cms目录下的Netbox.exe即可开启Asp Web服务,默认端口80 环境 ...

  8. count(*)、count(1)和count(列名)的区别

    count(*).count(1)和count(列名)的区别 1.执行效果上:   l  count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL l  count(1)包 ...

  9. nohup.out文件过大解决方法 定时任务清空

    0.1和2分别表示标准输入.标准输出和标准错误信息输出,可以用来指定需要重定向的标准输入或输出. 在一般使用时,默认的是标准输出,既1.当我们需要特殊用途时,可以使用其他标号.例如,将某个程序的错误信 ...

  10. R par yaxp xaxp 显示x轴和y轴的刻度线

    R语言会自动根据数据的范围,在X轴和Y轴上标记合适的刻度 > options(scipen = ) > plot(sample(:, )) 生成的图片如下 通过par("yaxp ...