使用IOCP处理客户端的TCP连接时,遇到一个现象,接收到数据(客户端的登录消息)之后,处理完毕,回复了一个应答消息,客户端也收到了,这个消息的长度­假设是100字节。客户端也收到了,之后客户端并没有发送任何信息给服务器。但是服务器端的线程循环到GetQueuedCompletionStatus的地­方,
if (bool bOK = GetQueuedCompletionStatus(m_CompletionPort,
&BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *)&PerIoData,
INFINITE) == 0)
居然又向下执行了,按说应该在此处挂住才对啊?
检查各个参数,发现BytesTransferred的值是我刚才给客户端发送的数据的字节数,但是PerIoData里面也没有任何数据,全部用0xCD填充­的。
给客户端发送数据的函数如下:
void CWorkerThread::SendPacket(char* pBufHeader, char* pBufData, int
nDataLen)
{
PER_IO_OPERATION_DATA * PerIoData = new PER_IO_OPERATION_DATA;
int iRtn = 0;
DWORD dwSendBytes, dwRecvBytes, dwFlags;
char * pBuffer = new char[512];
memset(pBuffer, 0, 512);
memcpy(pBuffer, pBufHeader, MESSAGE_HEADER_LEN);
memcpy(pBuffer + MESSAGE_HEADER_LEN, pBufData, nDataLen);
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.buf = pBuffer;
PerIoData->DataBuf.len = MESSAGE_HEADER_LEN + nDataLen; iRtn = WSASend(m_pPerHandleData->m_hSocket, &(PerIoData->DataBuf), 1,
&dwSendBytes, 0, &(PerIoData->Overlapped), NULL);
if (iRtn == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
TRACE("WSASend() failed with error %d\n", WSAGetLastError());
}
}
}
请问是怎么回事?
if (bool bOK = GetQueuedCompletionStatus(m_CompletionPort,
&BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *)&PerIoData,
INFINITE) == 0)
居然又向下执行了,按说应该在此处挂住才对啊?
检查各个参数,发现BytesTransferred的值是我刚才给客户端发送的数据的字节数,但是PerIoData里面也没有任何数据,全部用0xCD填充­的。
给客户端发送数据的函数如下:
void CWorkerThread::SendPacket(char* pBufHeader, char* pBufData, int
nDataLen)
{
PER_IO_OPERATION_DATA * PerIoData = new PER_IO_OPERATION_DATA;
int iRtn = 0;
DWORD dwSendBytes, dwRecvBytes, dwFlags;
char * pBuffer = new char[512];
memset(pBuffer, 0, 512);
memcpy(pBuffer, pBufHeader, MESSAGE_HEADER_LEN);
memcpy(pBuffer + MESSAGE_HEADER_LEN, pBufData, nDataLen);
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.buf = pBuffer;
PerIoData->DataBuf.len = MESSAGE_HEADER_LEN + nDataLen; iRtn = WSASend(m_pPerHandleData->m_hSocket, &(PerIoData->DataBuf), 1,
&dwSendBytes, 0, &(PerIoData->Overlapped), NULL);
if (iRtn == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
TRACE("WSASend() failed with error %d\n", WSAGetLastError());
}
}
}
请问是怎么回事?
解决方案 »
- MFC如何操作网络虚拟主机(空间)上的MDB数据库
- 显示调用动态链接库
- enum类型的实际应用问题~
- CScrollView打印问题
- 我想在某几个指定的时间段内触发某些操作,比如定时录像(精确到秒),除了用定时器定时触发检查之外还有什么好办法
- 如何实现向64位(8字节)中的36-47位(bit)填充数据,且具有普遍性?
- 关于RichEditCtrl字体设置问题!
- 如何捕捉视频图像
- [马上解决,马上送分]关于EDIT CTRL 问题
- 下拉列表中有复选框的那种组合框是怎么实现的啊?
- 项目中的链接错误问题(VC开发)
- fatal error C1083: Cannot open precompiled header file: 'Debug/myproject6.pch': No such file or directory
INFINITE)调用返回呢?
云彩了!
哪位指点指点俺?先谢谢了
&BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *)&PerIoData,
INFINITE) == 0) 参数里面的这个PerIOData才对吧。
请问到底是应该改还是不应该改?
请问到底是应该改还是不应该改?
{
continue;
}答复楼上说的,肯定关联了,否则在调用GetQueuedCompletionStatus的地方肯定都不会返回了
{
//PER_IO_OPERATION_DATA * PerIoData = new PER_IO_OPERATION_DATA;
int iRtn = 0;
DWORD dwSendBytes, dwFlags;//, dwRecvBytes;
char pBuffer[1024] = {0};
memcpy(pBuffer, pBufHeader, MESSAGE_HEADER_LEN);
memcpy(pBuffer + MESSAGE_HEADER_LEN, pBufData, nDataLen);
ZeroMemory(&(m_pPerIoData->Overlapped), sizeof(OVERLAPPED));
m_pPerIoData->DataBuf.buf = pBuffer;
m_pPerIoData->DataBuf.len = MESSAGE_HEADER_LEN + nDataLen; iRtn = WSASend(m_pPerHandleData->m_hSocket, &(m_pPerIoData->DataBuf), 1, &dwSendBytes, 0, &(m_pPerIoData->Overlapped), NULL);
if (iRtn == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
TRACE("WSASend() failed with error %d\n", WSAGetLastError());
}
}
}
你的操作是send所有的数据
确认send所有的数据后,再保持到read状态.你说发生的现象在我看了是正常的.
{
DWORD BytesTransferred;
DWORD dwFlags;
int iRtn = 0;
char s[1024] = {0}; while(TRUE)
{
if (bool bOK = GetQueuedCompletionStatus(m_CompletionPort, &BytesTransferred, (LPDWORD)&m_pPerHandleData, (LPOVERLAPPED *)&m_pPerIoData, INFINITE) == 0)
{
TRACE("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
//PrepareForRecv(m_pPerHandleData, m_pPerIoData);
continue;
}
else
{
TRACE("sizeof(PER_HANDLE_DATA)=%d, sizeof(PER_IO_OPERATION_DATA)=%d\n", sizeof(PER_HANDLE_DATA), sizeof(PER_IO_OPERATION_DATA));
if (BytesTransferred == 0)
{
TRACE("Closing socket %d\n", m_pPerHandleData->m_hSocket); if (closesocket(m_pPerHandleData->m_hSocket) == SOCKET_ERROR)
{
printf("closesocket() failed with error %d\n", WSAGetLastError());
continue;
}
m_pServerThread->RemoveOnlineUser(m_pPerHandleData);
GlobalFree(m_pPerHandleData);
GlobalFree(m_pPerIoData);
continue;
}
else
{
bool bExists = m_mapOnLineSocket.Lookup(m_pPerHandleData->m_hSocket, m_pPerHandleData);
if(bExists == false)
{
//m_pPerHandleData = PerHandleData;
//m_mapOnLineSocket.SetAt(PerHandleData->m_hSocket, m_pPerHandleData);
}
ASSERT(m_pPerHandleData != NULL);
if (m_pPerHandleData->m_bHeadRecv == false)
{
if(m_pPerHandleData->m_u16DataRead + BytesTransferred < MESSAGE_HEADER_LEN)
{
m_pPerHandleData->m_u16DataRead += BytesTransferred;
PrepareForRecv(m_pPerHandleData, m_pPerIoData);
continue;
}
else if(m_pPerHandleData->m_u16DataRead + BytesTransferred == MESSAGE_HEADER_LEN)
{
memcpy(&m_pPerHandleData->m_pBuffer[m_pPerHandleData->m_u16DataRead], m_pPerIoData->Buffer, MESSAGE_HEADER_LEN - m_pPerHandleData->m_u16DataRead);
if(!AnalysisHeader())
{
ClearSocket(m_pPerHandleData);
continue;
}
else
{
m_pPerHandleData->m_u16DataRead = 0;
m_pPerHandleData->m_bHeadRecv = TRUE;
PrepareForRecv(m_pPerHandleData, m_pPerIoData);
continue;
}
}
else
{
memcpy(&m_pPerHandleData->m_pBuffer[m_pPerHandleData->m_u16DataRead], m_pPerIoData->Buffer, MESSAGE_HEADER_LEN - m_pPerHandleData->m_u16DataRead);
if(!AnalysisHeader())
{
ClearSocket(m_pPerHandleData);
continue;
}
else
{
m_pPerHandleData->m_u16DataRead = BytesTransferred - MESSAGE_HEADER_LEN;
m_pPerHandleData->m_bHeadRecv = TRUE;
}
}
if(m_pPerHandleData->m_u16DataRead < m_pPerHandleData->m_u16DataNum)
{
//PrepareForRecv(m_pPerHandleData, m_pPerIoData);
continue;
}
else if(m_pPerHandleData->m_u16DataRead > m_pPerHandleData->m_u16DataNum)
{
//ASSERT(0);
}
else
{
memcpy(m_pPerHandleData->m_pBuffer, m_pPerIoData->Buffer + MESSAGE_HEADER_LEN, m_pPerHandleData->m_u16DataNum);
//处理收到的数据结构
ProcessPacketData();
m_pPerHandleData->m_u16DataRead = 0;
m_pPerHandleData->m_bHeadRecv = false;
PrepareForRecv(m_pPerHandleData, m_pPerIoData);
}
}
else
{
if(m_pPerHandleData->m_u16DataRead + BytesTransferred < m_pPerHandleData->m_u16DataNum)
{
m_pPerHandleData->m_u16DataRead += BytesTransferred;
//PrepareForRecv(m_pPerHandleData, m_pPerIoData);
continue;
}
else if(m_pPerHandleData->m_u16DataRead + BytesTransferred > m_pPerHandleData->m_u16DataNum)
{
//ASSERT(0);
}
else
{
memcpy(&m_pPerHandleData->m_pBuffer[m_pPerHandleData->m_u16DataRead], m_pPerIoData->Buffer, BytesTransferred);
//处理收到的数据结构
ProcessPacketData();
m_pPerHandleData->m_u16DataRead = 0;
m_pPerHandleData->m_bHeadRecv = false;
PrepareForRecv(m_pPerHandleData, m_pPerIoData);
}
}
}
}
}
return 0;
}
void CWorkerThread::PrepareForRecv(PER_HANDLE_DATA * PerHandleData, PER_IO_OPERATION_DATA * PerIoData)
{
DWORD dwFlags = 0;
int iRtn = 0;
DWORD dwRecvBytes = 0; ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); PerIoData->DataBuf.len = DATA_BUFSIZE;
PerIoData->DataBuf.buf = PerIoData->Buffer;
iRtn = WSARecv(PerHandleData->m_hSocket, &(PerIoData->DataBuf), 1, &dwRecvBytes, &dwFlags, &(PerIoData->Overlapped), NULL);
if (iRtn == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
TRACE("WSARecv() failed with error %d\n", WSAGetLastError());
//return 0;
}
}
}是否每次发送或者接收之后,都要发送一个wsarecv请求?原来没有添加这个操作的时候,线程就挂在调用get的地方了。我的workerthread的任务是接收数据(可能一次接收不到所需要的数据,这时将继续调用get函数,这时候如果不执行一个wsarecv调用,下一次的get就不会返回了),直到收到足够的数据,就调用处理函数,并通过wsasend发送响应数据。到现在我还没搞明白调用wsasend(比如说我发送了400字节的数据)之后,get返回,接收到的数据长度是刚才发送的数据长度,这个到底是怎么回事。按上面各位说的,这是iocp返回的send通知。哪位能告诉我应该怎样修改我的接收线程呢?
send 还是 read 要在lpCompletionKey 结构中标记状态,这样GetQueuedCompletionStatus后不就知道了..这个状态怎么设置呢?
是在调用wsasend或者wsarecv之前设置吗?
LPVOID CompletionPortID)
{
HANDLE CompletionPort = (HANDLE) CompletionPortID;
DWORD BytesTransferred;
LPOVERLAPPED Overlapped;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_DATA PerIoData;
DWORD SendBytes, RecvBytes;
DWORD Flags;
while(TRUE)
{
// Wait for I/O to complete on any socket
// associated with the completion port
ret = GetQueuedCompletionStatus(CompletionPort,
&BytesTransferred,(LPDWORD)&PerHandleData,
(LPOVERLAPPED *) &PerIoData, INFINITE); // First check to see if an error has occurred
// on the socket; if so, close the
// socket and clean up the per-handle data
// and per-I/O operation data associated with
// the socket if (BytesTransferred == 0 &&
(PerIoData->OperationType == RECV_POSTED ││
PerIoData->OperationType == SEND_POSTED))
{
// A zero BytesTransferred indicates that the
// socket has been closed by the peer, so
// you should close the socket. Note:
// Per-handle data was used to reference the
// socket associated with the I/O operation.
closesocket(PerHandleData->Socket); GlobalFree(PerHandleData);
GlobalFree(PerIoData);
continue;
} // Service the completed I/O request. You can
// determine which I/O request has just
// completed by looking at the OperationType
// field contained in the per-I/O operation data.
if (PerIoData->OperationType == RECV_POSTED)
{
// Do something with the received data
// in PerIoData->Buffer
} // Post another WSASend or WSARecv operation.
// As an example, we will post another WSARecv()
// I/O operation. Flags = 0; // Set up the per-I/O operation data for the next
// overlapped call
ZeroMemory(&(PerIoData->Overlapped),
sizeof(OVERLAPPED)); PerIoData->DataBuf.len = DATA_BUFSIZE;
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->OperationType = RECV_POSTED; WSARecv(PerHandleData->Socket,
&(PerIoData->DataBuf), 1, &RecvBytes,
&Flags, &(PerIoData->Overlapped), NULL);
}
}
而是在post 读写操作的时候初始化,对吗?