热门标签 | 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



推荐阅读
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社区 版权所有