IOCP不可忽视的细节

IOCP构架之所以公认高效,核心就在于异步IO。在网上纷纷为之膜拜之余,很少看到对稳定性或性能优化的观点。仔细想想,任何技术都有有利的一面,自然也有有弊的一面,这才符合辩证法思想。
我把自己在编写IOCP构架时重点处理的几个地方交代一下,如果你想认真写好一个稳定的IOCP服务端,建议重新规划以下几个细节。

1)不要修改套接字默认的收发缓冲区大小,即8192字节=8K=2分页。

2)总所周知,x86页面置换最少需要1分页,所以不管IOCP的重叠IO涉及多少字节,OS至少需要加锁/解锁1分页,在使用异步WSARecv/WSASend时,只要重叠缓冲区WSABuf的长度大于0,那么在投递异步操作时,系统至少会锁定1分页的非页面内存,而系统的非页面缓冲池是有大小限制的,如果你的IOCP消耗了太多非分页内存。轻则同服务器其他网络进程无法使用网络,重则可能会导致系统蓝屏或其他崩溃。而对于你的IOCP服务端通讯,常见的现象是客户端无法与服务端进行连接,或者收发包时出现 10055

3)正是由于情况2的出现可能性,所以在投递WSARecv/WSASend时,绝对不要贪图简单,直接进行投递,正确的做法应该为每个套接字建立至少2个缓冲队列,一收一发。

4)对于同一个客户端套接字,在同一时间最好只有一个WSARecv/WSASend,如果你想提高IOCP网络吞吐量,可能会在同一个套接字上同时投递多个WSARecv/WSASend,那么你将会面临下面几个情况必须处理:

a)IO线程之间的WSARecv/WSASend操作频繁加锁/解锁,因为你无法保证线程1在GQCS时,线程2不会对相同套接字GQCS。

b)收发出现乱序,仔细想一下,正是因为你对同一套接字同时投递多个WSARecv/WSASend导致的,有你忙的了。

c)短时间内大量WSARecv/WSASend异步操作消耗太多非分页内存,也许在你下次投递收发操作时,OS会毫不留情给你一个10055错误。

5)现在你应该明白服务端的稳定是一个前提,需要比高吞吐量更要优先考虑,那么对于情况4建议的缓冲队列,绝对应该是你首先需要考虑的。

6)合理限制客户端套接字的流量(包速),即你需要规划完整合理的Qos,否则一旦恶意连接试图以大流量正常业务包攻击你的IOCP时,你会发现你的IOCP构架是多么脆弱。

7)为了减少情况6的出现,建议你在客户端/服务端通讯时增加压缩(加密)/解压缩(解密)功能,最好加入时间戳或GUID校验,避免出现同样的业务收发相同的包,最终导致攻击者只需要截取一个正常业务包,就能干出情况6的事情。

也许你会觉得如此实现太影响IOCP吞吐量,确实如此,收/发缓冲队列、Qos、加密解密都会降低IO吞吐量。希望你在决定之前,先衡量一下你未来的IOCP服务端菊花是否够紧,否则有一天你会高呼:OMG,是谁又把我那可怜而又脆弱的IOCP爆菊了?

你可能感兴趣的:(IO,iocp,wsarecv,wsasend,wsabuf)