iocp_4_如何取得IO返回的操作结果,并作处理
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
<四>如何取得IO返回的操作结果,并作处理
在这一章中,我们的目标就集中在GetQueuedCompletionStatus。GetQueuedCompletionStatus 的作用就是从I/O出口队列中取得I/O操作结果。IOCP可以说是多入口(多种类型的函数可以提交I/O操作请求),单出口(一个函数就可以取得所有I/O操作的结果)。因此GetQueuedCompletionStatus 取得的结果将是复杂多样的。
BOOL(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED*lpOverlapped,
DWORD dwMilliseconds
);
在GetQueuedCompletionStatus中要重点关注以下几点:
(1)返回的布尔值// 操作成功与否的标志
(2)lpCompletionKey参数// 标志那个socket的I/O操作结果
(3)lpOverlapped参数// I/O操作得到的数据所在,看看I/O提交操作就知道了。
接下来,看看一份测试数据,会明白很多.
各种情况下,返回值和其它参数的值得情况,如下表:
说明:
1.ConnectEx 对方之后,如果成功连接,返回值为TRUE,lpNumberOfBytes为零,lpOverlapped不为0,lpCompletionKey 为绑定值
2.对方关闭之后,返回值为TRUE,lpNumberOfBytes 为零,lpOverlapped不为0。3.如果对方关闭后,socket没有关闭,而且还投递WSARecv请求,那么GetQueuedCompletionStatus会立刻返回,回到步骤2
4.ConnectEx 对方之后,如果对方拒绝连接,返回值为FALSE,lpOverlapped不为0,lpNumberOfBytes为零。
5.假如ConnectEx的对方不存在(超时),那么返回值为FALSE,lpOverlapped 不为0,lpCompletionKey 为绑定的值,lpNumberOfBytes为0,
6.任何情况下lpCompletionKey的值都是原先绑定的值
7.CompletionPort肯定不能为NULL,否则得到的值全部为空。
有了以上的表,是不是比MSDN上说的明白多了呢。下面看看代码如何实现:
void CIOCP_ClientDlg::IocpWorkerThread()
{
MYOVERLAPPED *lpOverlapped = NULL;
DWORD dwByteRecv = 0;
ULONG_PTR *PerHandleKey = NULL;
while (1)
{
lpOverlapped = NULL;
if(m_hIocp == NULL)
{
break;
}
//如果I/O 出口队列有结果,GetQueuedCompletionStatus则取得结果返回,否则等待 BOOL bResult = GetQueuedCompletionStatus(
m_hIocp, // 从这个IOCP中取得I/O操作结果
&dwByteRecv, // 发送或是接收了多少字节
(PULONG_PTR)&PerHandleKey,
(LPWSAOVERLAPPED*)&lpOverlapped,
INFINITE);
if (bResult == FALSE) // 对照表1.1,可能是连接超时、对方拒绝连接、断线
{
SOCKET socket = (SOCKET)PerHandleKey) ; // 转换,得到当初关联的socket
closesocket((SOCKET)PerHandleKey); // 出问题了,关闭socket
delete lpOverlapped ; // 释放OVERLAPPED
if (lpOverlapped == NULL) // 根据MSDN的说法,会出现这种情况
{
// 根据MSDN的说法,会出现这种情况,但测试时都没发生过
TRACE(TEXT("did not dequeue a pack from iocp queue.error:%d\n"), GetLastError());
continue;
}
else
{
// ERROR
TRACE(TEXT(" dequeued a pack from a failure iocp queue.error:%d\n"), GetLastError());
continue;
} // end if(lpOverlapped == NULL)
}
else
{
ASSERT(lpOverlapped != NULL); //
SOCKET socket = (SOCKET)PerHandleKey; // 取得关联的socket
switch (lpOverlapped->operateType) // 操作类型判别
{
case OP_CONNECT: // 连接操作,是成功的。
{
TRACE0("connected successfully!\n");
lpOverlapped->operateType = OP_RECV;//再次利用原有的OVERLAPPED
DWORD Flags = 0;
// 连接上之后,就要提交接收请求了,否则将收不到对方发过来的数据
if (WSARecv(socket, &lpOverlapped->wsabuf, 1,
&lpOverlapped->dwByteRecvSend, &Flags,
( LPWSAOVERLAPPED )lpOverlapped, NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() == WSA_IO_PENDING)
{
TRACE0("Error == WSA_IO_PENDING\n");
}
else
{
TRACE0("Error occured at WSARecv()\n");
}
}
}
break;
case OP_ACCEPT:
{
// 得到一已连接的socket
SOCKET socketAccept = (SOCKET)lpOverlapped->pVoid;
//接下来就应该:
// (1)将socketAccept关联到IOCP ,代码略