1,如何处理乱序。
按照投递Read的次序,收到ReadComplete时,看收到的buffer中序号跟连接中的序号是否相等。
不如用例子表示:
b1,b2表示两个Read请求用的buffer。连接Context中序列号为0,b1保存的序号为0,b2保存的序号为1.
p2,p1表示先收到第二个Read的反馈,p2对应的buffer为b2,里面的序号为1,而连接Context中的序号还是0,所以暂时不处理,将buffer缓冲起来。
等p1到达,p1对应的buffer为b1,里面的序号为0,而连接中的序号是0,处理,再将连接序号+1,由于处理是在一个while循环中,继续查找序号为1的buffer,找到后,处理。
如此这般就能实现对buffer的乱序处理。
if(m_bReadInOrder)
pOverlapBuff=GetNextReadBuffer(pContext,pOverlapBuff);
while(pOverlapBuff!=NULL)
{
//TRACE("R> %i\r\n",pOverlapBuff->GetSequenceNumber());
// Mark that we are Using the buffer..
pOverlapBuff->Use(dwIoSize);
ProcessPackage(pContext,dwIoSize,pOverlapBuff);
IncreaseReadSequenceNumber(pContext);
pOverlapBuff=NULL;
if(m_bReadInOrder)
pOverlapBuff=GetNextReadBuffer(pContext);
}
}
2,如何提升读取性能。
一个新的客户端连上后,先通过ARead()投递0字节IOInitialize事件给IOCP端口。
当IOCP端口上收到该IOInitialize事件后(自产自销?),又干了这个:
AZeroByteRead(pContext,pOverlapBuff); // 投递(PostQueuedCompletionStatus)一个0字节IOZeroByteRead事件。
// m_iNumberOfPendlingReads=1 by default.
for(int i=0;i<m_iNumberOfPendlingReads;i++)
{
EnterIOLoop(pContext); // One for each Read Loop
ARead(pContext); // 投递(PostQueuedCompletionStatus)n个0字节IORead事件。
}
当IOCP端口上收到IOZeroByteRead事件后,会将buffer类型设置成IOZeroReadCompleted,触发读取(WSARecv)缓冲区长度设为0的包。
当IOCP端口上收到IOZeroReadCompleted事件后,会重新调用AZeroByteRead(),如此循环往复。
在IOCP端口上收到IORead事件后,将buffer类型设置成IOReadCompleted,然后动真格的,触发读取(WSARecv)缓冲设为真正buffer长度。
当IOCP端口上收到IOReadCompleted事件后,继续调用ARead(pContext),如此循环往复,保持m_iNumberOfPendlingReads个读请求。
1,上面连续投递n个,是为了提高吞吐率,当正在处理一个数据包时,还可以同时接收数据。
2,没有直接投递IOReadCompleted,而是先Post IORead,马上再WSARecv IOReadCompleted,是为了将大任务分解成小任务,符合IOCP的特点。
3,循环投递IOZeroByteRead,是为了将投递的IOReadCompleted缓冲区解锁。看下面链接:
http://topic.csdn.net/u/20100209/10/0271d94f-0785-427b-85f5-51fe7417d22d.html
3,如何处理分包。
看ProcessPackage代码即可。
4,如何处理Context非法访问。
在一个连接中,你知道要关闭该连接,并关闭,但同时有几个IORead请求已经发出,不可避免的,它们马上返回IOCP消息,但你已经不能再访问消息中的Context对象了。
解决办法,给Context增加计数器,当最后一个IORead返回(不论成功或失败)时,计数器肯定等于0,这时才能释放资源。
但我的疑问是:IOCP请求和IOCP事件是一一对应的吗?客户端主动关闭时,会不会发生多余的IOCP事件。看代码,看TriggerRecv的时机对不对。
5,有害的大包设计。
6,转发服务器只要读取时处理乱序、分包,发送不必关心。