我最近开始编写一些使用套接字的C++代码,我希望它是异步的.我读过许多关于如何使用poll和select来使我的套接字异步(使用poll或select等待send或recv缓冲区)的帖子,但在我的服务器端我有一个struct pollfd数组,每次都有侦听套接字接受连接,它将它添加到struct pollfd数组,以便它可以监视该套接字的recv(POLLIN).
我的问题是,如果我有5000个套接字连接到我的服务器上的监听套接字,那么struct pollfd的数组大小将为5000,因为它将监视所有连接的套接字但是我知道如何检查是否一个套接字的recv已准备就绪,是通过循环遍历struct pollfd数组中的所有项来查找其revents等于POLLIN的项.这只是效率低下,当连接套接字的数量因为非常大.有一个更好的方法吗?
boost :: asio库如何处理async_accept,async_send等...?我应该怎么处理?
哎呀,我会继续写下答案.
我将忽略"异步"与"非阻塞"术语,因为我认为它与您的问题无关.
在处理数千个网络客户端时,您担心性能,并且您担心是正确的.你重新发现了C10K的问题.回到网络年轻时,人们发现需要少量快速服务器来处理大量(相对)慢客户端.现有的选择/轮询类型接口需要在内核和用户空间中对所有套接字进行线性扫描,以确定哪些是准备好的.如果许多套接字通常处于空闲状态,那么您的服务器可能会花费更多时间来确定要做什么工作而不是实际工作.
快进到今天,我们基本上有两种方法来处理这个问题:
1)每个套接字使用一个线程,只发出阻塞读写.在我看来,这通常是最简单的代码,现代操作系统非常善于让空闲线程安静地睡眠,而不会产生任何显着的性能开销.根据我的经验,这种方法对数百个客户非常有效; 我无法亲自说出它对千人的影响.
2)使用引入的平台特定接口之一来解决C10K问题.这意味着epoll(Linux),kqueue(BSD/Mac)或完成端口(Windows).(如果您认为epoll
是相同的poll
,请再次查看.)所有这些只会通知您的应用程序有关实际就绪的套接字,避免在空闲连接上进行浪费的线性扫描.有几个库使这些特定于平台的接口更易于使用,包括libevent,libev和Boost.Asio.您会发现,只要有这样的接口,所有这些都最终会在Linux上调用epoll,在BSD上调用kqueue等等.