.NET Core 2.1 源码学习:看 SocketsHttpHandler 如何在异步方法中连接 Socket
在 .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的更多相关文章
- ASP.NET Core 2.1 源码学习之 Options[1]:Configure
配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 Configur ...
- ASP.NET Core 2.1 源码学习之 Options[2]:IOptions
在 上一章 中,介绍了Options的注册,而在使用时只需要注入 IOption<T> 即可: public ValuesController(IOptions<MyOptions& ...
- ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor
前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...
- ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor 【转】
原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-monitor-in-asp-net-core. ...
- ASP.NET Core 2.1 源码学习之 Options[2]:IOptions 【转】
原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-in-asp-net-core.html 在 上 ...
- ASP.NET Core 2.1 源码学习之 Options[1]:Configure 【转】
原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-configure-in-asp-net-core.html 配置 ...
- ASP.NET Core 选项模式源码学习Options Configure(一)
前言 ASP.NET Core 后我们的配置变得更加轻量级了,在ASP.NET Core中,配置模型得到了显著的扩展和增强,应用程序配置可以存储在多环境变量配置中,appsettings.json用户 ...
- TCP/IP协议栈源码图解分析系列10:linux内核协议栈中对于socket相关API的实现
题记:本系列文章的目的是抛开书本从Linux内核源代码的角度详细分析TCP/IP协议栈内核相关技术 轻松搞定TCP/IP协议栈,原创文章欢迎交流, byhankswang@gmail.com linu ...
- ASP.NET Core 选项模式源码学习Options IOptions(二)
前言 上一篇文章介绍IOptions的注册,本章我们继续往下看 IOptions IOptions是一个接口里面只有一个Values属性,该接口通过OptionsManager实现 public in ...
随机推荐
- logback配置异步日志
<appender name="FILE" class= "ch.qos.logback.core.rolling.RollingFileAppender" ...
- CMD 命令2
cd %~dp0 切换到当前脚本所有目录 批处理常用命令总结 - 批处理命令简介 目录 echo 打开回显或关闭请求回显功能,或显示消息.如果没有任何参数,echo 命令将显示当前回显设置. ech ...
- IOS应用内购(一)内购的种类
Glossary IAP - In App Purchase, 应用内购. 内购种类 consumable - 可消费的,比如游戏中的金币,金币可以购买游戏道具或者装备,这个金币是可以消费的,用完之后 ...
- window.print控制打印样式
我们可能会去使用window.print()方法来打印页面,但是当我们遇到需要改变打印时候的字体大小等css样式的时候你可能会懵逼. 所以搜索成了我们的必经之路,我相信在网上搜索出来的最好的答案就是使 ...
- 突破拐点:企业成长的S曲线
1.企业成长的不同时期 初创期:初创前几年,最重要的创业激情,靠“蓝色小药丸”,断层期切入,不断试错玩命干(产品定位.商业模式.融资源.找关系.辅渠道),并最终完成赢利模式,得以生存. 成长期:通过快 ...
- fastcgi php-cgi与php-fpm区别和之间的关系
关于FastCGI.php-cgi.php-fpm的区别是什么,各自有什么用途,以及相互间的关系是什么,查阅相关资料,可谓是众说纷纭,莫衷一是: 说法一:fastcgi是一个协议,php-fpm实现了 ...
- delphi 导出到excel的7种方法
本文来自 爱好者8888 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/kpc2000/article/details/17066823?utm_source=cop ...
- [MySQL Status] Queries,Questions,read/s区别,Com_Commit和handle_commit
Queries: 这个状态变量表示,mysql系统接收的查询的次数,包括存储过程内部的查询 Questions: 这个状态变量表示,mysql系统接收查询的次数,但是不包括存储过程内部的查询 ...
- 【java】[文件上传jar包]commons-fileUpload组件解决文件上传(文件名)乱码问题
response.setContentType("text/html; charset=UTF-8"); Boolean isMultipart = ServletFileUpl ...
- Jquery EasyUI Combotree只能选择叶子节点且叶子节点有多选框
Jquery EasyUI Combotree只能选择叶子节点且叶子节点有多选框 Jquery EasyUI Combotree单选框,Jquery EasyUI Combotree只能选择叶子节点 ...