IOCP的几点开发心得(补充)

   IOCP以其高效的性能受到服务器开发者的青睐,本人有幸在当前的项目中使用了该异步模型,修改调试之余,总结出开发过程中的经验若干,供大家借鉴。

   首先是需要注意的是OVERLAPPED结构。想必该结构大多数人都是自定义新的结构体,将OVERLAPPED成员放置在第一位,然后后置其他成员。
   在函数 WSASend, WSARecv, PostQueuedCompletionStatus 以及GetQueuedCompletionStatus 中都有LPOVERLAPPED的参数,其中在前面三个函数中是输入参数,后面一个函数中是输出参数。对于输入参数可以传入强制转换的自定义结构体指针(不需要取地址),也可以传入自定义结构体中OVERLAPPED成员的地址(需要取地址);对于输出参数,将要传出的是前三个函数中输入参数的地址。在开发过程中,对于该结构地址的操作需要细心。
   形如以下的代码都是正确的:
WSASend(pClientData->IoSocket, &(pPerIoData->WsaSendDataBuff), 1&dwSendBytes, 0, (LPOVERLAPPED)pPerIoData, NULL);
WSASend(pClientData
->IoSocket, &(pPerIoData->WsaSendDataBuff), 1&dwSendBytes, 0&(pPerIoData->overlaped), NULL);
GetQueuedCompletionStatus(pThis
->m_hIOCP, &dwBytesTransferred,(LPDWORD)&pPerHandleData, (LPOVERLAPPED *)&pPerIoData, INFINITE);

   其次需要注意的是PostQueuedCompletionStatus 函数。该函数向IOCP发送三个参数(DWORD dwNumberOfBytesTransferred, ULONG_PTR dwCompletionKey, LPOVERLAPPED lpOverlapped),GetQueuedCompletionStatus 函数将接收到这三个参数。IOCP将不会对这三个参数做任何操作。
   在实际应用中,该函数一般用于控制IOCP接收线程的退出。其实,该函数的用法远不止于此,它还可以作为消息来使用。通过定义特定的dwNumberOfBytesTransferred消息值,然后通过PostQueuedCompletionStatus函数向IOCP中POST该消息,GetQueuedCompletionStatus 函数就可以捕获该消息。自定义的dwNumberOfBytesTransferred消息值一定要大于接收BUFFER和发送BUFFER的最大长度,否则作为消息就没有意义了。

   还有一个需要注意的是WSARecv函数。在IOCP中多次调用该函数是有后果的,严重的会导致接收缓冲区被塞满,就算没有塞满接收缓冲区,如果客户端意外断开连接,GetQueuedCompletionStatus 函数会接到与调用次数一样多次数的返回错误,想必大家一定都不希望这些情况发生。要避免这种问题一定要谨慎的调用WSARecv函数,最好在GetQueuedCompletionStatus 函数接收到数据后再考虑再次调用WSARecv。 



   今天又测出一个潜在的BUG,先贴代码:
   首先是定义:
typedef enum _IO_OPERATION 
{
    IoRecv,            
//WSARecv
    IoSend,            //WSASend
    IoQuit
}
IO_OPERATION, *PIO_OPERATION;

typedef 
struct _PER_IO_CONTEXT
{
    WSAOVERLAPPED       ol;
    WSABUF                WsaRecvDataBuff;
    WSABUF                WsaSendDataBuff;
    
char                strRecvBuffer[DATA_MAX_BUFFERSIZE];   //接收BUFFER
    
char                strSendBuffer[DATA_MAX_BUFFERSIZE];   //发送BUFFER
    IO_OPERATION        IoType;
}
PER_IO_CONTEXT, *LPPER_IO_CONTEXT;
   这里的IO_OPERATION定义了三种类型,分别表示接收、发送和退出,GetQueuedCompletionStatus函数在接收到消息时,可以通过检测IoType的类型来判断IOCP刚刚完成的操作是接收操作还是发送操作亦或是退出操作。
   在一次操作中(比如接收到数据后的操作),先后调用WSASend和WSARecv,来实现发送数据,然后继续Recv的动作,这样做可行吗?答案是否定的。分析:调用WSASend之前,设置IoType为IoSend,标志本次操作是发送操作;然后在调用WSARecv前,设置IoType为IoRecv,标志本次操作是接收操作。IOCP在处理消息队列时,首先应该接收到的是发送操作,由于IoType已经被设置成了IoRecv,在判断时就会将这次操作判断成接收操作,去检测接收BUFFER,这样显然就出错了;然后会接收到接收操作,此时IoType是IoRecv,仍然判断为接收操作,此时检测接收BUFFER,是正确的。这样做的直观表现就是接收事件明显变多了。
   以上的这个例子,说明处理IOCP时一定要细心,要注意那些变量是易变的,那些是不易变的。可能上面的这个看起来很明显,但是如果程序复杂了,这种BUG就不易察觉。

posted on 2007-11-12 12:52 迷宫の未来 阅读(4484) 评论(10)  编辑 收藏 引用

评论

# re: IOCP的几点开发心得 2007-11-12 13:50 <a href=http://minidx.com>minidxer</a>

看来我落伍了……IOCP是什么?  回复  更多评论   

# re: IOCP的几点开发心得 2007-11-12 14:23 周辉

SOCKET五种异步通讯模型中的完成端口模型(Completion Port Model)  回复  更多评论   

# re: IOCP的几点开发心得 2007-11-12 16:42 li_guotao

内容不少,知识面太好,收益丰浅!   回复  更多评论   

# re: IOCP的几点开发心得 2007-11-12 17:00 <a href=http://minidx.com>minidxer</a>

谢谢~  回复  更多评论   

# re: IOCP的几点开发心得[未登录] 2007-11-12 23:43 天下无双

哎,不好用,很容易出错,要搞稳定太难了。  回复  更多评论   

# re: IOCP的几点开发心得(补充) 2007-11-15 10:24 cooelaf

完成端口模型属于IO模型,不光可以用于Socket。  回复  更多评论   

# re: IOCP的几点开发心得(补充)[未登录] 2008-03-15 12:57 Jerry

如果在GetQueuedCompletionStatus 函数接收到数据后再考虑再次调用WSARecv的话,对效率可能会有一定影响的,可以对多次发出的WSARecv加以一定的控制
  回复  更多评论   

# re: IOCP的几点开发心得(补充)[未登录] 2008-03-15 13:06 Jerry

楼主的那个bug是很常见的,其实首先收到的不一定是发送操作,可能是接收操作, 但第二次解析时IoType还是错的  回复  更多评论   

# re: IOCP的几点开发心得(补充) 2008-12-06 18:01 Anny

很好
谢谢 还得学习啊  回复  更多评论   

# re: IOCP的几点开发心得(补充) 2014-05-10 14:50 phanil

在一次操作中(比如接收到数据后的操作),先后调用WSASend和WSARecv,来实现发送数据,然后继续Recv的动作,这样做可行吗?答案是否定的。

这时你代码实现的问题  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2007年12月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

导航

统计

常用链接

留言簿(10)

随笔档案

文章档案

最新随笔

搜索

积分与排名

最新随笔

最新评论

阅读排行榜

评论排行榜