随笔记-TCPNetworkEngine分析

it2022-05-05  134

1.send的时候使用的OverLapped,不考虑同步问题,直接往队列投递请求,不考虑上一次是否send成功。

2.recv在投递接受请求的时候给了一个空的buffer,然后在完成在完成回调中再次调用recv方法接受数据。

理解: 如果一个服务器提交了非常多的重叠receive在每个连接上,那么限制会随着连接数的增长而变化。如果一个服务器能够预先估计可能产生的最大并发连接数,服务器可以投递一个使用零缓冲区的receive在每一个连接上。因为当你提交操作没有缓冲区时,该socket底层缓冲区的数据会原封不动的还在其中而没有被读取到receive操作的缓冲区来。此时,服务器可以简单的调用非阻塞式的recv将存在socket缓冲区的数据全部读出来,一直到recv返回WSAEWOULDBLOCK为止。这种设计非常适合那些可以牺牲数据吞吐量而换区巨大并发连接数的服务器。当然,你也需要意识到如何让客户端的行为尽量避免对服务器造成影响。在上一个例子中,当一个零缓冲区的receive操作被返回后使用一个非阻塞的recv去读取socket缓冲区中的数据,如果服务器此时可预计到将会有爆发的数据流,那么可以考虑此时投递一个或者多个receive来取代非阻塞的recv来进行数据接收。(这比你使用1个缺省的8K缓冲区来接收要好的多。)

一个简单实用的解决WSAENOBUF错误的办法。我们执行了一个零字节缓冲的异步WSARead(…)当这个请求完成,我们知道在TCP/IP栈中有数据,然后我们通过执行几个有MAXIMUMPACKAGESIZE缓冲的异步WSARead(…)去读,解决了WSAENOBUFS问题。但是这种解决办法降低了服务器的吞吐量。

总结: 解决方法一: 投递使用空缓冲区的receive操作,当操作返回后,使用非阻塞的recv来进行真实数据的读取。因此在完成端口的每一个连接中需要使用一个循环的操作来不断的提交空缓冲区的receive操作。

解决方法二: 在投递几个普通含有缓冲区的receive操作后,紧接着开始循环投递一个空缓冲区的receive操作。这样保证他们按照投递顺序依次返回,这样我们就总能对被锁定的内存进行解锁。

附: 如果一个服务器同时连接了许多客户端,对每个客户端又调用了许多WSARecv,那么大量的内存将会被锁定到非分页内存池。锁定这些内存时时按照页面边界来锁定的,也就是说即使你WSARecv的缓存大小是1字节,被锁定的内存也将会是4K。非分页内存池是由整个系统共用的,如果用完的话最坏的情况就是系统崩溃。一个解决办法是,使用大小为0的缓冲区调用WSARecv。等到调用成功时在换用非阻塞的recv接受到来的数据,知道它返回WSAEWOULDBLOCK表明数据已经全部读完。在这个过程中没有任何内存需要被锁定,但坏处是效率稍低。


最新回复(0)