TcpListener与SocketAsyncEventArgs [英] TcpListener vs SocketAsyncEventArgs

查看:89
本文介绍了TcpListener与SocketAsyncEventArgs的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我已经使用 SocketAsyncEventArgs 实现了这种高性能/高吞吐量的TCP服务器,它经历了种种麻烦,不得不使用很大的预分配的 byte 来处理那些固定的缓冲区用于接收和接收的 SocketAsyncEventArgs 的数组和池,使用一些低级的东西和闪亮的智能代码以及一些TPL数据流和一些Rx来组合在一起,并且效果很好;在这方面几乎是教科书-实际上,我已经从别人的代码中学到了80%以上的这些东西.

但是仍然存在一些问题和担忧:

  1. 复杂度:我无法将对此服务器的任何形式的修改委派给另一台服务器团队成员.这使我无法完成此类任务,我可以对其他项目的其他部分没有足够的重视.
  2. 内存使用情况(固定的 byte 数组):使用 SocketAsyncEventArgs 池需要被预先分配.因此,用于处理100000个并发连接(更糟糕的情况,甚至在不同的端口上)一大堆RAM毫无用处地徘徊;预先分配的(即使某些时候满足了这些条件,服务器应该每天能够处理1或2个这样的高峰).
  3. TcpListener 实际上很好用:我实际上已经将 TcpListener 进行了测试(有一些技巧在专用线程上使用 AcceptTcpClient ,并 异步版本,然后将接受的连接发送到 ConcurrentQueue 而不是就地创建 Task 等等)并使用最新版本的.NET,效果很好,几乎一样好作为 SocketAsyncEventArgs ,没有数据丢失并且内存占用少这有助于避免在服务器上浪费过多的RAM,并且不需要预先分配.

那么为什么我看不到在任何地方都使用 TcpListener ,并且每个人(包括我自己)都在使用 SocketAsyncEventArgs ?我想念什么吗?

解决方案

我看不到任何证据表明这个问题与 TcpListener 完全有关.看来您只关心处理已被接受的连接的代码.这样的连接独立于侦听器.

SocketAsyncEventArgs 是CPU负载优化.我相信您可以通过它获得更高的每秒操作率.与普通APM/TAP异步IO的区别有多大?当然小于一个数量级.大概在1.2倍至3倍之间.上次我对环回TCP事务速率进行基准测试时,发现内核占用了大约一半的CPU使用率.这意味着通过无限优化,您的应用最多可以快2倍.

请记住, SocketAsyncEventArgs 是在2000年左右(当CPU的功能远远不足时)添加到BCL中的.

仅在有证据表明需要时,才使用 SocketAsyncEventArgs .它使您的工作效率大大降低.错误的可能性更大.

这是您的套接字处理循环应为的模板:

  while(Connection Establishmented()){var someData =等待ReadFromSocketAsync(socket);等待ProcessDataAsync(someData);} 

非常简单的代码.由于 await ,没有回调.


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

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

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

或者,如果您不认为这是个问题,则无需执行任何操作.

Is there a valid reason to not use TcpListener for implementing a high performance/high throughput TCP server instead of SocketAsyncEventArgs?

I've already implemented this high performance/high throughput TCP server using SocketAsyncEventArgs went through all sort of headaches to handling those pinned buffers using a big pre-allocated byte array and pools of SocketAsyncEventArgs for accepting and receiving, putting together using some low level stuff and shiny smart code with some TPL Data Flow and some Rx and it works perfectly; almost text book in this endeavor - actually I've learnt more than 80% of these stuff from other-one's code.

However there are some problems and concerns:

  1. Complexity: I can not delegate any sort of modifications to this server to another member of the team. That bounds me to this sort of tasks and I can not pay enough attention to other parts of other projects.
  2. Memory Usage (pinned byte arrays): Using SocketAsyncEventArgs the pools are needed to be pre-allocated. So for handling 100000 concurrent connections (worse condition, even on different ports) a big pile of RAM is uselessly hovers there; pre-allocated (even if these conditions are met just at some times, server should be able to handle 1 or 2 such peaks everyday).
  3. TcpListener actually works good: I actually had put TcpListener into test (with some tricks like using AcceptTcpClient on a dedicated thread, and not the async version and then sending the accepted connections to a ConcurrentQueue and not creating Tasks in-place and the like) and with latest version of .NET, it worked very well, almost as good as SocketAsyncEventArgs, no data-loss and a low memory foot-print which helps with not wasting too much RAM on server and no pre-allocation is needed.

So why I do not see TcpListener being used anywhere and everybody (including myself) is using SocketAsyncEventArgs? Am I missing something?

解决方案

I see no evidence that this question is about TcpListener at all. It seems you are only concerned with the code that deals with a connection that already has been accepted. Such a connection is independent of the listener.

SocketAsyncEventArgs is a CPU-load optimization. I'm convinced you can achieve a higher rate of operations per second with it. How significant is the difference to normal APM/TAP async IO? Certainly less than an order of magnitude. Probably between 1.2x and 3x. Last time I benchmarked loopback TCP transaction rate I found that the kernel took about half of the CPU usage. That means your app can get at most 2x faster by being infinitely optimized.

Remember that SocketAsyncEventArgs was added to the BCL in the year 2000 or so when CPUs were far less capable.

Use SocketAsyncEventArgs only when you have evidence that you need it. It causes you to be far less productive. More potential for bugs.

Here's the template that your socket processing loop should look like:

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

Very simple code. No callbacks thanks to await.


In case you are concerned about managed heap fragmentation: Allocate a new byte[1024 * 1024] on startup. When you want to read from a socket read a single byte into some free portion of this buffer. When that single-byte read completes you ask how many bytes are actually there (Socket.Available) and synchronously pull the rest. That way you only pin a single rather small buffer and still can use async IO to wait for data to arrive.

This technique does not require polling. Since Socket.Available can only increase without reading from the socket we do not risk to perform a read that is too small accidentally.

Alternatively, you can combat managed heap fragmentation by allocating few very big buffers and handing out chunks.

Or, if you don't find this to be a problem you don't need to do anything.

这篇关于TcpListener与SocketAsyncEventArgs的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆