TcpListener与SocketAsyncEventArgs

时间:2015-01-03 13:23:17

标签: c# sockets tcplistener tcpserver socketasynceventargs

是否有正当理由不使用TcpListener来实现高性能/高吞吐量TCP服务器而不是SocketAsyncEventArgs

我已经使用SocketAsyncEventArgs实现了这个高性能/高吞吐量的TCP服务器,经历了各种令人头疼的问题,使用一个大的预分配byte数组和{{{}池来处理这些固定缓冲区1}}用于接受和接收,使用一些低级别的东西和闪亮的智能代码与一些TPL数据流和一些Rx放在一起,它完美地工作;在这项努力中几乎是教科书 - 实际上我从其他人的代码中学到了80%以上的东西。

然而,存在一些问题和担忧:

  1. 复杂性:我无法将此服务器的任何修改委托给另一个服务器 团队成员。这限制了我这种任务,我可以 没有足够重视其他项目的其他部分。
  2. 内存使用(固定SocketAsyncEventArgs阵列):使用byte需要池 预先分配。因此,用于处理100000个并发连接 (更糟糕的是,甚至在不同的端口上)一大堆RAM无用地悬停在那里; 预先分配(即使在某些时候满足这些条件, 服务器应该每天能够处理1个或2个这样的峰值。
  3. SocketAsyncEventArgs实际效果很好:我实际上已经对TcpListener进行了测试(有些技巧就像 在专用主题上使用TcpListener不是 AcceptTcpClient 版本,然后将接受的连接发送到 async并且没有就地创建ConcurrentQueue等等) 并且使用最新版本的.NET,它运行得非常好,几乎一样好 如Task,没有数据丢失和内存占用率低 这有助于不在服务器上浪费太多RAM,也不需要预先分配。
  4. 那么为什么我看不到SocketAsyncEventArgs在任何地方被使用而且每个人(包括我自己)都在使用TcpListener?我错过了什么吗?

1 个答案:

答案 0 :(得分:2)

我认为没有证据表明这个问题与TcpListener完全相同。您似乎只关心处理已经被接受的连接的代码。这种联系独立于听众。

SocketAsyncEventArgs是CPU负载优化。我相信你可以用它来实现更高的每秒操作速度。与普通APM / TAP异步IO的区别有多大?当然不到一个数量级。可能介于1.2x和3x之间。上次我对回送TCP事务率进行基准测试时发现内核占用了大约一半的CPU使用率。这意味着您的应用程序可以通过无限优化获得最多2倍的速度。

请记住,在2000年左右,当CPU的能力差得多时,SocketAsyncEventArgs被添加到BCL中。

仅在您有证据表明需要时才使用SocketAsyncEventArgs。它会使你的工作效率低得多。更容易出错。

这是套接字处理循环应该是这样的模板:

while (ConnectionEstablished()) {
 var someData = await ReadFromSocketAsync(socket);
 await ProcessDataAsync(someData);
}

非常简单的代码。 await没有回调。


如果您担心托管堆碎片:在启动时分配new byte[1024 * 1024]。当你想从套接字读取时,将一个字节读入该缓冲区的一些空闲部分。当单字节读取完成时,您会询问实际存在多少字节(Socket.Available)并同步拉出其余字节。这样你只需要固定一个相当小的缓冲区,并且仍然可以使用异步IO来等待数据到达。

此技术不需要轮询。由于Socket.Available只能在不读取套接字的情况下增加,因此我们不会有意外执行读取太小的风险。

或者,您可以通过分配几个非常大的缓冲区并分发块来对抗托管堆碎片。

或者,如果你没有发现这是一个问题,你不需要做任何事情。