C# TcpListener与SocketAsyncEventArgs
是否有正当理由不使用C# TcpListener与SocketAsyncEventArgs,c#,sockets,tcplistener,tcpserver,socketasynceventargs,C#,Sockets,Tcplistener,Tcpserver,Socketasynceventargs,是否有正当理由不使用TcpListener来实现高性能/高吞吐量TCP服务器而不是SocketAsyncEventArgs 我已经使用SocketAsyncEventArgs实现了这个高性能/高吞吐量TCP服务器,使用一个大的预分配byte数组和SocketAsyncEventArgs池来接受和接收这些固定缓冲区,经历了各种各样的麻烦,将一些低级的东西和闪亮的智能代码与一些TPL数据流和一些Rx结合在一起,它可以完美地工作;在这项工作中,我几乎是一本教科书——事实上,我从别人的代码中学到了80%
TcpListener
来实现高性能/高吞吐量TCP
服务器而不是SocketAsyncEventArgs
我已经使用
SocketAsyncEventArgs
实现了这个高性能/高吞吐量TCP服务器,使用一个大的预分配byte
数组和SocketAsyncEventArgs
池来接受和接收这些固定缓冲区,经历了各种各样的麻烦,将一些低级的东西和闪亮的智能代码与一些TPL数据流和一些Rx结合在一起,它可以完美地工作;在这项工作中,我几乎是一本教科书——事实上,我从别人的代码中学到了80%以上的东西
但也存在一些问题和担忧:
字节
数组):使用SocketAsyncEventArgs
池需要
必须预先分配。所以对于处理100000个并发连接来说
(更糟糕的情况,即使在不同的港口)一大堆RAM无用地悬浮在那里;
预分配(即使在某些时候满足这些条件,
服务器应该能够每天处理1到2个这样的峰值)TcpListener
实际上效果很好:我实际上已经对TcpListener
进行了测试(使用了一些技巧,如
在专用线程上使用acceptcpclient
,而不使用async
版本,然后将接受的连接发送到
ConcurrentQueue
和不创建任务等)
对于最新版本的.NET,它运行得非常好,几乎一样好
作为
SocketAsyncEventArgs
,没有数据丢失和内存不足
这有助于避免在服务器上浪费太多RAM,并且无需预先分配那么为什么我看不到
TcpListener
在任何地方被使用,而每个人(包括我自己)都在使用SocketAsyncEventArgs
?我遗漏了什么吗?我看不到任何证据表明这个问题是关于TcpListener
的。您似乎只关心处理已被接受的连接的代码。这种连接独立于侦听器
SocketAsyncEventArgs
是一种CPU负载优化。我相信你可以用它实现更高的每秒操作率。与普通APM/TAP异步IO的差异有多大?当然不到一个数量级。可能在1.2倍到3倍之间。上次我对环回TCP事务速率进行基准测试时,我发现内核占用了大约一半的CPU使用率。这意味着通过无限优化,你的应用程序最多可以快2倍
请记住,
SocketAsyncEventArgs
是在2000年左右的时候添加到BCL中的,当时CPU的功能远远不够
只有在有证据表明需要时才使用
SocketAsyncEventArgs
。这会使你的工作效率大大降低。更多潜在的bug
以下是套接字处理循环的模板:
while (ConnectionEstablished()) {
var someData = await ReadFromSocketAsync(socket);
await ProcessDataAsync(someData);
}
非常简单的代码。由于等待
,因此没有回调
如果您担心托管堆碎片:在启动时分配一个
新字节[1024*1024]
。当您想要从套接字读取数据时,请将单个字节读入缓冲区的某个空闲部分。当单字节读取完成时,您询问实际有多少字节(Socket.Available
),并同步提取其余字节。这样,您只需固定一个相当小的缓冲区,仍然可以使用异步IO等待数据到达
这种技术不需要轮询。由于Socket.Available
只能在不读取Socket的情况下增加,因此我们不会冒险执行过小的读取
或者,您可以通过分配几个非常大的缓冲区和分发块来对抗托管堆碎片
或者,如果你没有发现这是一个问题,你不需要做任何事情。@usr谢谢,你是对的,正如我在第3点提到的。我完全按照你说的做!接受是在一个简单的循环中发生的,在一个专用的
线程上(使用TaskCreationOptions.longlunning
选项创建的Task
)。然后我不明白你为什么建议在SocketAsyncEventArgs和TcpListener之间进行选择。为什么不两者都用呢?一旦连接被接受,侦听器就停止播放。在我看来,这与处理连接无关。SocketAsyncEventArgs
导致byte
数组的缓冲区被固定,因此无法有效地进行垃圾收集,并导致内存碎片,从而导致更高的CPU和RAM使用率;加上准备和管理SocketAsyncEventArgs
对象池需要克服的所有额外障碍;总而言之,它需要更多的额外工作和维护,更重要的是,这种知识/经验不容易/安全/可靠地转移给其他开发人员?以什么方式?您似乎在问:“我可以简单地用通常的APM/TAP异步IO替换SocketAsyncEventArgs吗?”这与TcpListener没有任何关系。正如我所描述的,TcpListener
提供了一个简单得多的编程模型以及与SocketAsyncEventArgs
相同的性能特征。但是在我研究过的所有项目中(比如fracture(F#)、SocketAwaitable、SuperSocket和许多其他示例以及这里和那里的博客文章),我找不到任何人使用TcpListener
。现在我问,为什么?在你得到使用建议之前,我100%支持你