corefx 源码学习:NetworkStream.ReadAsync 是如何从 Socket 异步读取数据的
最近遇到 NetworkStream.ReadAsync 在 Linux 上高并发读取数据的问题,由此激发了阅读 corefx 中 System.Net.Sockets 实现源码(基于 corefx 2.2)的兴趣。
这篇随笔是阅读 NetworkStream.ReadAsync 相关源码的简单笔记,基于在 Linux 上运行的场景。
NetworkStream 继承自 System.IO.Stream ,System.IO.Stream.ReadAsync 方法签名是
public Task<int> ReadAsync(byte[] buffer, int offset, int count);
实际调用的是
public virtual Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
上面的的方法被 NetworkStream 重写(override),调用的是 Socket 的 ReceiveAsync 方法
return _streamSocket.ReceiveAsync(
new Memory<byte>(buffer, offset, size),
SocketFlags.None,
fromNetworkStream: true,
cancellationToken).AsTask();
Socket.ReceiveAsync 的方法签名
internal ValueTask<int> ReceiveAsync(Memory<byte> buffer, SocketFlags socketFlags, bool fromNetworkStream, CancellationToken cancellationToken)
主要实现代码
AwaitableSocketAsyncEventArgs saea = LazyInitializer.EnsureInitialized(ref LazyInitializer.EnsureInitialized(ref _cachedTaskEventArgs).ValueTaskReceive);
if (saea.Reserve())
{
saea.SetBuffer(buffer);
saea.SocketFlags = socketFlags;
saea.WrapExceptionsInIOExceptions = fromNetworkStream;
var result = saea.ReceiveAsync(this);
return result;
}
else
{
// We couldn't get a cached instance, due to a concurrent receive operation on the socket.
// Fall back to wrapping APM.
return new ValueTask<int>(ReceiveAsyncApm(buffer, socketFlags));
}
通常情况下都会使用 AwaitableSocketAsyncEventArgs 异步读取数据,所以我们这里只从 saea.ReceiveAsync 往下看。
saea.ReceiveAsync 调用的是 Socket.ReceiveAsync(SocketAsyncEventArgs e) 方法,而后者调用的是 SocketAsyncEventArgs.DoOperationReceive(SafeCloseSocket handle) 。
在 Linux 上 DoOperationReceive 的实现在 SocketAsyncEventArgs.Unix.cs 中,主要代码如下
internal unsafe SocketError DoOperationReceive(SafeCloseSocket handle)
{
//...
if (_bufferList == null)
{
errorCode = handle.AsyncContext.ReceiveAsync(_buffer.Slice(_offset, _count), _socketFlags, out bytesReceived, out flags, TransferCompletionCallback);
}
else
{
errorCode = handle.AsyncContext.ReceiveAsync(_bufferListInternal, _socketFlags, out bytesReceived, out flags, TransferCompletionCallback);
} if (errorCode != SocketError.IOPending)
{
CompleteTransferOperation(bytesReceived, null, , flags, errorCode);
FinishOperationSync(errorCode, bytesReceived, flags);
} return errorCode;
}
handle.AsyncContext.ReceiveAsync 对应的 Linux 实现在 SocketAsyncContext.Unix.cs 中,调用的是 SocketAsyncContext 的 ReceiveFrom 方法,ReceiveFrom 的主要实现代码如下
public SocketError ReceiveFromAsync(Memory<byte> buffer, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
SetNonBlocking(); SocketError errorCode;
int observedSequenceNumber;
if (_receiveQueue.IsReady(this, out observedSequenceNumber) &&
SocketPal.TryCompleteReceiveFrom(_socket, buffer.Span, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode))
{
return errorCode;
} BufferMemoryReceiveOperation operation = RentBufferMemoryReceiveOperation();
operation.Callback = callback;
operation.Buffer = buffer;
operation.Flags = flags;
operation.SocketAddress = socketAddress;
operation.SocketAddressLen = socketAddressLen; if (!_receiveQueue.StartAsyncOperation(this, operation, observedSequenceNumber))
{
receivedFlags = operation.ReceivedFlags;
bytesReceived = operation.BytesTransferred;
errorCode = operation.ErrorCode; ReturnOperation(operation);
return errorCode;
} bytesReceived = ;
receivedFlags = SocketFlags.None;
return SocketError.IOPending;
}
SocketPal.TryCompleteReceiveFrom 的实现代码在 SocketPal.Unix.cs 中,所调用的另一个 TryCompleteReceiveFrom 方法的签名是
public static unsafe bool TryCompleteReceiveFrom(SafeCloseSocket socket, Span<byte> buffer, IList<ArraySegment<byte>> buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, out SocketError errorCode)
该方法调用的是 Receive 方法
private static unsafe int Receive(SafeCloseSocket socket, SocketFlags flags, IList<ArraySegment<byte>> buffers, byte[] socketAddress, ref int socketAddressLen, out SocketFlags receivedFlags, out Interop.Error errno)
在 Receive 方法中调用了
errno = Interop.Sys.ReceiveMessage(
socket.DangerousGetHandle(),
&messageHeader,
flags,
&received);
Interop.Sys.ReceiveMessage 对应的是 Linux 本地库中的方法
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReceiveMessage")]
internal static extern unsafe Error ReceiveMessage(IntPtr socket, MessageHeader* messageHeader, SocketFlags flags, long* received);
}
Libraries.SystemNative 对应的是哪个库呢?
它就是 System.Native.so
$ find /usr/share/dotnet/ -name System.Native.so
/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.0/System.Native.so
接下来根据 SocketError.IOPending 的情况阅读源码。
SocketAsyncEventArgs 在 DoOperationReceive 方法中调用 SocketAsyncContext.ReceiveFrom 方法时(handle.AsyncContext.ReceiveAsync)传递了 TransferCompletionCallback 参数值,在异步操作时是通过这个 callback 读取 socket 数据的,对应的方法是 TransferCompletionCallbackCore 。
private void TransferCompletionCallbackCore(int bytesTransferred, byte[] socketAddress, int socketAddressSize, SocketFlags receivedFlags, SocketError socketError)
{
CompleteTransferOperation(bytesTransferred, socketAddress, socketAddressSize, receivedFlags, socketError); CompletionCallback(bytesTransferred, receivedFlags, socketError);
}
TransferCompletionCallbackCore 中进一步调用 CompletionCallback
private void CompletionCallback(int bytesTransferred, SocketFlags flags, SocketError socketError)
{
if (socketError == SocketError.Success)
{
FinishOperationAsyncSuccess(bytesTransferred, flags);
}
else
{
if (_currentSocket.CleanedUp)
{
socketError = SocketError.OperationAborted;
} FinishOperationAsyncFailure(socketError, bytesTransferred, flags);
}
}
在 CompletionCallback 中当 SocketError.Success 时进一步调用 FinishOperationAsyncSuccess
internal void FinishOperationAsyncSuccess(int bytesTransferred, SocketFlags flags)
{
FinishOperationSyncSuccess(bytesTransferred, flags); // Raise completion event.
if (_context == null)
{
OnCompleted(this);
}
else
{
ExecutionContext.Run(_context, s_executionCallback, this);
}
}
从上面的代码可以看出实际调用的也是 FinishOperationSyncSuccess ,异步与同步读取数据最终调用的是同一个方法。
corefx 源码学习:NetworkStream.ReadAsync 是如何从 Socket 异步读取数据的的更多相关文章
- corefx 源码学习:SqlClient 是如何同步建立 Socket 连接的
在昨天的技术周会上发现 EnyimMemcached 中建立 Socket 连接的代码有问题,今天坐车的时候在手机上阅读 .net core 2.2 的 SqlClient 中同步建立 Socket ...
- netty源码解解析(4.0)-14 Channel NIO实现:读取数据
本章分析Nio Channel的数据读取功能的实现. Channel读取数据需要Channel和ChannelHandler配合使用,netty设计数据读取功能包括三个要素:Channel, Eve ...
- .NET Core 2.1 源码学习:看 SocketsHttpHandler 如何在异步方法中连接 Socket
在 .NET Core 2.1 中,System.Net.Sockets 的性能有了很大的提升,最好的证明是 Kestrel 与 HttpClient 都改为使用 System.Net.Sockets ...
- [算法2-数组与字符串的查找与匹配] (.NET源码学习)
[算法2-数组与字符串的查找与匹配] (.NET源码学习) 关键词:1. 数组查找(算法) 2. 字符串查找(算法) 3. C#中的String(源码) 4. 特性Attribute 与内 ...
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- jQuery源码学习感想
还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
随机推荐
- 【u215】河床
问题描述 小明是一个地理学家,经常要对一段河流进行测量分析.他从上游开始向下游方向等距离地选择了N个点测量水位深度.得到一组数据d1,d2,--,dn,回到实验室后数据分析员根据需要对数据进行分析,发 ...
- OpenGL核心之视差映射
笔者介绍:姜雪伟,IT公司技术合伙人.IT高级讲师,CSDN社区专家,特邀编辑.畅销书作者;已出版书籍:<手把手教你¯的纹理坐标偏移T3来对fragment的纹理坐标进行位移.你能够看到随着深度 ...
- alloc init初始化后对象依然还在父视图
self.TableView=[[UITableView alloc]init]; ........2个cell //下面但方法和addsubviews方法不一样 [self.view insertS ...
- [Angular] @ContentChildren and QueryList
We have looked at @ContentChild in article(http://www.cnblogs.com/Answer1215/p/6414657.html). Now le ...
- 我的Java开发学习之旅------>Java经典排序算法之归并排序
一.归并排序 归并排序是建立在归并操作上的一种有效的排序算法,该算法是採用分治法(Divide and Conquer)的一个很典型的应用.将已有序的子序列合并,得到全然有序的序列.即先使每一个子序列 ...
- Web开发标配--开发人员工具-F12
喜欢从业的专注,七分学习的态度. 360浏览器-开发工具 谷歌-开发工具 IE-开发工具 Web开发中最最烦琐的莫过于调整CSS和JS,而最方便最高效的方式就是利用浏览器的开发工具调整.CSS可以实时 ...
- 【严肃脸】使用caffe实现色情图片的识别
前言 前几天看到了雅虎开源了一个色情图片的识别模型新闻,上Github一看,是基于caffe的.试了试,模型效果很赞.Github地址:https://github.com/yahoo/open_ns ...
- Android Studio 报错Guest isn't online after 7 seconds 解决方案
最近使用真机模拟之后,再使用虚拟机就频繁出现这个问题; 解决步骤如下: 1.打开Android虚拟设备管理器, 2.查看Actoins栏下拉图标, 3.选择冷启动模式即可, 4.重启AVD正常;
- <meta http-equiv="refresh" content="0; url=">什么意思?
原文:<meta http-equiv="refresh" content="0; url=">什么意思? 页面定期刷新,如果加url的,则会重新定 ...
- dotnet core 通过修改文件头的方式隐藏控制台窗口
原文:dotnet core 通过修改文件头的方式隐藏控制台窗口 在带界面的 dotnet core 程序运行的时候就会出现一个控制台窗口,本文告诉大家使用最简单方法去隐藏控制台窗口. 最近在使用 A ...