在用 BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED* lpOverlapped,
DWORD dwMilliseconds
);
函数等待接收数据返回时,我看到有些程序例子是在lpCompletionKey参数中取得客户发送来的数据,
有些例子又是在lpOverlapped参数中取得数据,这是为什么,又如何设定在哪个参数中取得数据
HANDLE CompletionPort,
LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED* lpOverlapped,
DWORD dwMilliseconds
);
函数等待接收数据返回时,我看到有些程序例子是在lpCompletionKey参数中取得客户发送来的数据,
有些例子又是在lpOverlapped参数中取得数据,这是为什么,又如何设定在哪个参数中取得数据
{
CompletionPortModel* pCP = (CompletionPortModel*)Param;
DWORD dwNumberBytes;
PPER_HANDLE_CONTEXT lpHandleContext = NULL;
LPWSAOVERLAPPED lpOverlapped = NULL;
int nResult;
BOOL bSuccess;
EnterCriticalSection(&pCP->m_CPCriSection);
++ pCP->CPThreadCount;
LeaveCriticalSection(&pCP->m_CPCriSection);
TRACE("第%d个完成端口处理线程启动。\n",pCP->CPThreadCount); while (pCP->CPThreadRun == 1)
{
bSuccess = GetQueuedCompletionStatus(pCP->m_hCOP,&dwNumberBytes,(PULONG_PTR )&lpHandleContext,&lpOverlapped,1000);//INFINITE); PPER_IO_CONTEXT lpPerIoContext = (PPER_IO_CONTEXT)lpOverlapped;
if (FALSE == bSuccess && pCP->CPThreadRun == 1)
{#ifndef _DEBUG
TRACE( "GetQueuedCompletionStatus() failed: %d" , GetLastError());
#endif
if (lpPerIoContext)
{
pCP->ReleaseClient(lpPerIoContext); pCP->InsertToLookaside(lpPerIoContext, NULL);
}
if (lpHandleContext)
{
lpHandleContext->pNext = NULL;
pCP->InsertToLookaside(NULL, lpHandleContext);
} continue;
}
if (pCP->CPThreadRun == 1)
{
if (NULL == lpHandleContext)
{
//
//PostQueuedCompletionStatus发过来一个空的单句柄数据,表示线程要退出了。
//
TRACE("第%d个完成端口处理线程退出:PostQueuedCompletionStatus发过来一个空的单句柄数据,表示线程要退出了\n",pCP->CPThreadCount);
--pCP->CPThreadCount;
return 0;
}
#ifdef _DEBUG
// TRACE( "recv buffer data: %s %d\n", lpPerIoContext->szBuffer,lpPerIoContext->dataLen);
#endif
if(IoAccept != lpPerIoContext->IoOperation && 0 == dwNumberBytes)//非连接消息且收发数据长度为0,则关闭连接
{
pCP->OnClientClose(lpPerIoContext->unId);
pCP->ReleaseClient(lpPerIoContext);
pCP->InsertToLookaside(lpPerIoContext, NULL); lpHandleContext->pNext = NULL;
pCP->InsertToLookaside(NULL, lpHandleContext);
continue;
} HANDLE hResult;
PPER_HANDLE_CONTEXT lpNewperHandleContext = NULL; switch(lpPerIoContext->IoOperation)
{
case IoAccept :
if (dwNumberBytes > 0)
{
//
//第一次连接成功并且收到了数据,将这个结点从链表中解除
//
EnterCriticalSection(&pCP->m_ListCriSection);
pCP->ReleaseNode(lpPerIoContext);
LeaveCriticalSection(&pCP->m_ListCriSection);
}
//将客户连接和服务器监听连接关联,即在客户连接中设置服务器监听连接
nResult = setsockopt(lpPerIoContext->sClient,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(char *)&pCP->m_ListenSocket,
sizeof(pCP->m_ListenSocket)
);
if(SOCKET_ERROR == nResult)
{
TRACE( "SO_UPDATE_ACCEPT_CONTEXT failed to update accept socket\n" );
pCP->ReleaseClient(lpPerIoContext);
pCP->InsertToLookaside(lpPerIoContext, NULL);
continue;
}
//获取一个空闲的节点
lpNewperHandleContext = pCP->GetHandleFromLookaside();//获取一个空闲节点
if (NULL == lpNewperHandleContext)//无且创建失败
{
TRACE( "HeapAlloc() for lpNewperHandlecontext failed\n" );
pCP->ReleaseClient(lpPerIoContext);
pCP->InsertToLookaside(lpPerIoContext, NULL);
continue;
} lpNewperHandleContext->pNext = NULL;
//
//将新建立的套接字关联到完成端口
//
hResult = CreateIoCompletionPort(
(HANDLE)lpPerIoContext->sClient,\
pCP->m_hCOP,
(DWORD_PTR)lpNewperHandleContext,
0
);
if (NULL == hResult)
{
TRACE( "将新建立的套接字关联到完成端口时,CreateIoCompletionPort() failed:%d ", GetLastError()); pCP->ReleaseClient(lpPerIoContext);
pCP->InsertToLookaside(lpPerIoContext, NULL);
lpNewperHandleContext->pNext = NULL;
pCP->InsertToLookaside(NULL, lpNewperHandleContext);
continue;
}
//保留完成键
pCP->AddClient(lpPerIoContext); int flags;
if (dwNumberBytes > 0)//同时收到数据
{
lpPerIoContext->dataLen = dwNumberBytes;
flags = IO_READ_COMPLETION;
}
else//如果连接成功但是没有收到数据
{
lpPerIoContext->dataLen = 0;
flags = IO_ACCEPT_COMPLETION;
}
pCP->IOActionFinish(lpPerIoContext,flags );
bSuccess = pCP->IOAction(lpPerIoContext);
if (FALSE == bSuccess)
{
continue;
}
break;//end of case IoAccept
case IoRead:
pCP->IOActionFinish(lpPerIoContext, IO_READ_COMPLETION);
bSuccess = pCP->IOAction(lpPerIoContext);
if (FALSE == bSuccess)
{
continue;
} break;//end of case IoRead case IoWrite:
pCP->IOActionFinish(lpPerIoContext, IO_WRITE_COMPLETION);
bSuccess = pCP->IOAction(lpPerIoContext);
if (FALSE == bSuccess)
{
continue;
} break; default:
continue;
break;
}
}
} TRACE("第%d个完成端口处理线程退出。\n",pCP->CPThreadCount);
EnterCriticalSection(&pCP->m_CPCriSection);
--pCP->CPThreadCount;
LeaveCriticalSection(&pCP->m_CPCriSection);
return 0;}//end of CompletionRoutine()
//发送接收数据后处理
//
BOOL JhCpServer::IOActionFinish(PPER_IO_CONTEXT lpPerIoContext,int nFlags)
{
//
//nFlags == IO_READ_COMPLETION表示完成的上一次IO操作是WSARecv。
//
if (IO_READ_COMPLETION == nFlags)
{
//
//完成了WSARecv,添加数据到缓冲区
//
if (lpPerIoContext->dataLen > 0)
{
AddRecvData(lpPerIoContext->sClient,lpPerIoContext->wsaBuffer.buf,lpPerIoContext->dataLen);
}
//lpPerIoContext->ContinueAction = ContinueWrite;
//if (server)
//{
// SOCKETDATA sd;
// sd.s = lpPerIoContext->sClient;
// sd.pData = lpPerIoContext->wsaBuffer.buf;
// sd.DataLen = lpPerIoContext->dataLen;
// server->OnReceive(&sd);
//}
// //
//转向发送
//
TrySend(lpPerIoContext);
return TRUE;
} if (IO_WRITE_COMPLETION == nFlags)
{
//
//上一次IO操作WSASend数据发送完成,可以将后续操作标志设置为关闭则关闭连接
//如果不需要关闭而是要继续发送,将lpPerIoContext->IoOperation设置为
//IoWrite,如果要继续接收,将lpPerIoContext->IoOperation设置为
//IoRead,并初始化好BUFFER。
//
if (server && lpPerIoContext->dataLen > 0)//发送成功
{
server->OnSendSuccess(lpPerIoContext->sClient,lpPerIoContext->dataLen);
}
TrySend(lpPerIoContext); return TRUE;
}
if (IO_ACCEPT_COMPLETION == nFlags)
{
//
//刚建立了一个连接,并且没有收发数据,,,,
//
lpPerIoContext->IoOperation = IoRead;
// ZeroMemory(&(lpPerIoContext->ol), sizeof(WSAOVERLAPPED));
// ZeroMemory(lpPerIoContext->szBuffer, BUFFER_SIZE);
// lpPerIoContext->wsaBuffer.len = BUFFER_SIZE;
// lpPerIoContext->wsaBuffer.buf = lpPerIoContext->szBuffer; if (server)
{
server->OnConnect(lpPerIoContext->sClient);
}
return TRUE;
}
return FALSE;
}//
//试图发送
//
void JhCpServer::TrySend(PPER_IO_CONTEXT lpPerIoContext)
{
//取得最近一个 发送数据包
ZeroMemory(&(lpPerIoContext->ol), sizeof(WSAOVERLAPPED));
ZeroMemory(lpPerIoContext->szBuffer, BUFFER_SIZE);
if (lpPerIoContext->sendBuf == NULL)//转向继续接收
{
lpPerIoContext->IoOperation = IoRead;
}
else//转向发送
{
EnterCriticalSection(&lpPerIoContext->sendCriticalSection);
if (lpPerIoContext->sendBuf->SendOffset + lpPerIoContext->dataLen >= lpPerIoContext->sendBuf->GetDataLen())//该数据包是否已经发送完
{
SendData* nextData = lpPerIoContext->sendBuf;
lpPerIoContext->sendBuf = (SendData*)nextData->next;
Del(nextData);
}
else
{
lpPerIoContext->sendBuf->SendOffset += lpPerIoContext->dataLen;
}
if (lpPerIoContext->sendBuf != NULL)
{
lpPerIoContext->dataLen = min(BUFFER_SIZE,lpPerIoContext->sendBuf->GetDataLen() - lpPerIoContext->sendBuf->SendOffset);
memcpy(lpPerIoContext->szBuffer,lpPerIoContext->sendBuf->GetData() + lpPerIoContext->sendBuf->SendOffset,lpPerIoContext->dataLen);
lpPerIoContext->wsaBuffer.len = lpPerIoContext->dataLen;
lpPerIoContext->IoOperation = IoWrite;
}
else//没有了,转向接收
{
lpPerIoContext->IoOperation = IoRead;
lpPerIoContext->dataLen = 0;
lpPerIoContext->wsaBuffer.len = BUFFER_SIZE;
}
LeaveCriticalSection(&lpPerIoContext->sendCriticalSection);
}
}//
//执行发送或者接收数据
//
BOOL JhCpServer::IOAction( PPER_IO_CONTEXT lpPerIoContext)
{
int nResult;
DWORD dwFlags = 0,dwSend = 0;
//
//发送数据且发送数据长度大于0
//
if (IoWrite == lpPerIoContext->IoOperation && lpPerIoContext->dataLen > 0)
{
nResult = WSASend(lpPerIoContext->sClient,
&(lpPerIoContext->wsaBuffer),
1,
&dwSend,
0,
&(lpPerIoContext->ol),
NULL
);
if((SOCKET_ERROR == nResult) && (ERROR_IO_PENDING != WSAGetLastError()))
{
TRACE( "WSASend() failed:%d " ,WSAGetLastError() );
if (server) server->OnSendFail(lpPerIoContext->sClient,WSAGetLastError());
ReleaseClient(lpPerIoContext);
return FALSE;
}
else
{
TRACE("Send data %d %d bytes success.\n ",lpPerIoContext->dataLen,dwSend);
}
}
//
//读取数据
//
if (IoRead == lpPerIoContext->IoOperation)
{
lpPerIoContext->dataLen = 0;
lpPerIoContext->wsaBuffer.len = BUFFER_SIZE;
nResult = WSARecv(lpPerIoContext->sClient,
&(lpPerIoContext->wsaBuffer),
1,
&lpPerIoContext->dataLen,
&dwFlags,
&(lpPerIoContext->ol),
NULL
);
if((SOCKET_ERROR==nResult) && (ERROR_IO_PENDING != WSAGetLastError()))
{
TRACE( "WSARecv() failed:%d " , WSAGetLastError() );
if (server) server->OnReceiveFail( lpPerIoContext->sClient,WSAGetLastError()); ReleaseClient(lpPerIoContext);
return FALSE;
}
else
{
TRACE("Receive data %d bytes.\n",lpPerIoContext->dataLen);
}
}
//
//关闭客户连接
//
if (IoEnd == lpPerIoContext->IoOperation)
{
ReleaseClient(lpPerIoContext);//释放资源
InsertToLookaside(lpPerIoContext,NULL);//插入到空闲链表中备用
} return TRUE;
}
GetQueCompletionPort()只是判断完成端口的状态
lpOverlapped参数:是你在进行数据收发时传递的,记录每次IO数据自定义结构。