完成端口--笔记
1. GetQueuedCompletionStatus 返回值问题
参考:http://wenku.baidu.com/view/7e205c2ced630b1c59eeb5c8.html
|
Return value |
CompletionPort |
lpNumberOfBytes |
lpCompletionKey |
lpOverlapped |
WSAGetLastError() |
|
连接成功 |
1 == TRUE |
1 == NOT NULL |
0(值为0 ) |
1 == NOT NULL |
1(NOT NULL) |
0 |
|
连接超时 |
0 |
1 |
0 |
1 |
1 |
121 |
|
对方拒绝 |
0 |
1 |
0 |
1 |
1 |
1225 |
|
对方关闭 |
1 |
1 |
0 |
1 |
1 |
0 |
|
断线 |
0 |
1 |
0 |
1 |
1 |
|
|
数据 |
1 |
1 |
1 == NOT NULL |
1 |
1 |
0 |
|
未知情况 |
1 |
1 |
0 |
1 |
0 |
|
|
本地关闭 |
0 |
1 |
0 |
1 |
1 |
64 |
|
|
|
|
|
|
|
|
说明:
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,否则得到的值全部为空。
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 ,代码略 // (2)提交接收请求,代码略 } break; case OP_SEND: { //ASSERT(dwByteRecv == lpOverlapped->wsabuf.len); if(dwByteRecv != lpOverlapped->wsabuf.len) { ASSERT(FALSE); // 没实际用途,只做测试 // .... } // TRACE0("send completely!\n"); delete lpOverlapped; // 发送操作成功了,释放OVERLAPPED 结构 } break; case OP_RECV: { TRACE0("recv completely!\n"); if(0 == dwByteRecv ) // 由表1.1知道对方已经关闭 { TRACE0("Peer have been closed , me clsoe too."); closesocket(socket); // 关闭socket delete lpOverlapped ; continue; } // 处理数据,数据在lpOverlapped->wsabuf->buf中 // … // 接下来,继续提交接受请求,且再次利用原来的OVERLAPPED结构 lpOverlapped->operateType = OP_RECV; 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"); } } // end if(WSARecv… } // case OP_RECV break; default: { TRACE(TEXT("default...error happen\n")); } } } // end if (bResult == 0) } // end while(1) TRACE(_T("exit iocp work thread \n")); }