IOCP新手求指点

Co_priest 2015-09-30 04:51:16
void CIOCPServer::HandleIO(DWORD dwKey, CIOCPBuffer *pBuffer, DWORD dwTrans, int nError)
{
CIOCPContext *pContext = (CIOCPContext *)dwKey;

// 1)首先减少套节字上的未决I/O计数
if(pContext != NULL)
{
::EnterCriticalSection(&pContext->Lock);

if(pBuffer->nOperation == OP_READ)
pContext->nOutstandingRecv --;
else if(pBuffer->nOperation == OP_WRITE)
pContext->nOutstandingSend --;

::LeaveCriticalSection(&pContext->Lock);

// 2)检查套节字是否已经被我们关闭 [2009.8.9 bak Lostyears][如果关闭则释放剩下的未决IO]
if(pContext->bClosing)
{

if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
{
ReleaseContext(pContext);
}
// 释放已关闭套节字的未决I/O
ReleaseBuffer(pBuffer);
return;
}
}
else
{
RemovePendingAccept(pBuffer); // [2009.8.9 bak Lostyears][sListen关联了iocp, 关联时dwKey为0, 所以当有新连接发送数据时会执行到此]
}

// 3)检查套节字上发生的错误,如果有的话,通知用户,然后关闭套节字
if(nError != NO_ERROR)
{
if(pBuffer->nOperation != OP_ACCEPT)
{
NotifyConnectionError(pContext, pBuffer, nError);
CloseAConnection(pContext);
if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
{
ReleaseContext(pContext);
}

}
else // 在监听套节字上发生错误,也就是监听套节字处理的客户出错了
{
// 客户端出错,释放I/O缓冲区
if(pBuffer->sClient != INVALID_SOCKET)
{
::closesocket(pBuffer->sClient);
pBuffer->sClient = INVALID_SOCKET;
}
}

ReleaseBuffer(pBuffer);
return;
}
// 开始处理
if(pBuffer->nOperation == OP_ACCEPT)
{
if(dwTrans == 0) // [2010.5.16 bak Lostyears]如果AcceptEx的数据接收缓冲区设为0, 一连接上就会执行到这
{

if(pBuffer->sClient != INVALID_SOCKET)
{
::closesocket(pBuffer->sClient);
pBuffer->sClient = INVALID_SOCKET;
}
}
else
{
// 为新接受的连接申请客户上下文对象
CIOCPContext *pClient = AllocateContext(pBuffer->sClient);
if(pClient != NULL)
{
if(AddAConnection(pClient))
{
// 取得客户地址
int nLocalLen, nRmoteLen;
LPSOCKADDR pLocalAddr, pRemoteAddr;
m_lpfnGetAcceptExSockaddrs(
pBuffer->buff,
pBuffer->nLen - ((sizeof(sockaddr_in) + 16) * 2), // [2010.5.16 bak Lostyears]和AcceptEx相应参数对应
sizeof(sockaddr_in) + 16,
sizeof(sockaddr_in) + 16,
(SOCKADDR **)&pLocalAddr,
&nLocalLen,
(SOCKADDR **)&pRemoteAddr,
&nRmoteLen);
memcpy(&pClient->addrLocal, pLocalAddr, nLocalLen);
memcpy(&pClient->addrRemote, pRemoteAddr, nRmoteLen);

// [2010.1.15 add Lostyears][加入KeepAlive机制]
BOOL bKeepAlive = TRUE;
int nRet = ::setsockopt(pClient->s, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));
if (nRet == SOCKET_ERROR)
{
CloseAConnection(pClient);
}
else
{
// 设置KeepAlive参数
tcp_keepalive alive_in = {0};
tcp_keepalive alive_out = {0};
alive_in.keepalivetime = 5000; // 开始首次KeepAlive探测前的TCP空闲时间
alive_in.keepaliveinterval = 1000; // 两次KeepAlive探测间的时间间隔
alive_in.onoff = TRUE;
unsigned long ulBytesReturn = 0;
nRet = ::WSAIoctl(pClient->s, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),
&alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);
if (nRet == SOCKET_ERROR)
{
CloseAConnection(pClient);
}
else
{
// 关联新连接到完成端口对象
::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletion, (DWORD)pClient, 2);

// 通知用户
pBuffer->nLen = dwTrans;
OnConnectionEstablished(pClient, pBuffer);

// 向新连接投递几个Read请求,这些空间在套节字关闭或出错时释放
for(int i=0; i<m_nInitialReads; i++) // [2009.8.21 mod Lostyears][将常量值改为m_nInitialReads]
{
CIOCPBuffer *p = AllocateBuffer(BUFFER_SIZE);
if(p != NULL)
{
if(!PostRecv(pClient, p))
{
CloseAConnection(pClient);
break;
}
}
}
}
}
}
else // 连接数量已满,关闭连接
{
CloseAConnection(pClient);
ReleaseContext(pClient);
}
}
else
{
// 资源不足,关闭与客户的连接即可
::closesocket(pBuffer->sClient);
pBuffer->sClient = INVALID_SOCKET;
}
}

// Accept请求完成,释放I/O缓冲区
ReleaseBuffer(pBuffer);

// 通知监听线程继续再投递一个Accept请求
::InterlockedIncrement(&m_nRepostCount);
::SetEvent(m_hRepostEvent);
}
else if(pBuffer->nOperation == OP_READ)
{
if(dwTrans == 0) // 对方关闭套节字
{
// 先通知用户
pBuffer->nLen = 0;
NotifyConnectionClosing(pContext, pBuffer);

// 再关闭连接
CloseAConnection(pContext);
// 释放客户上下文和缓冲区对象
if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
{
ReleaseContext(pContext);
}
ReleaseBuffer(pBuffer);
}
else
{
pBuffer->nLen = dwTrans;
// 按照I/O投递的顺序读取接收到的数据
CIOCPBuffer *p = GetNextReadBuffer(pContext, pBuffer);
while(p != NULL)
{
// 通知用户
OnReadCompleted(pContext, p);
// 增加要读的序列号的值
::InterlockedIncrement((LONG*)&pContext->nCurrentReadSequence);
// 释放这个已完成的I/O
ReleaseBuffer(p);
p = GetNextReadBuffer(pContext, NULL);
}

// 继续投递一个新的接收请求
pBuffer = AllocateBuffer(BUFFER_SIZE);
if(pBuffer == NULL || !PostRecv(pContext, pBuffer))
{
CloseAConnection(pContext);
}
}
}
else if(pBuffer->nOperation == OP_WRITE)
{
if(dwTrans == 0) // 对方关闭套节字
{
// 先通知用户
pBuffer->nLen = 0;
NotifyConnectionClosing(pContext, pBuffer);

// 再关闭连接
CloseAConnection(pContext);

// 释放客户上下文和缓冲区对象
if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
{
ReleaseContext(pContext);
}
ReleaseBuffer(pBuffer);
}
else
{
// 写操作完成,通知用户
pBuffer->nLen = dwTrans;
OnWriteCompleted(pContext, pBuffer);
// 释放SendText函数申请的缓冲区
ReleaseBuffer(pBuffer);
}
}
}

// 当套件字关闭或出错时通知
void CIOCPServer::NotifyConnectionClosing(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
::EnterCriticalSection(&m_CloseOrErrLock);
if (!pContext->bNotifyCloseOrError)
{
pContext->bNotifyCloseOrError = true;
OnConnectionClosing(pContext, pBuffer);
}
::LeaveCriticalSection(&m_CloseOrErrLock);
}

BOOL CIOCPServer::SendText(CIOCPContext *pContext, char *pszText, int nLen)
{
CIOCPBuffer *pBuffer = AllocateBuffer(nLen);
if(pBuffer != NULL)
{
memcpy(pBuffer->buff, pszText, nLen);
return PostSend(pContext, pBuffer);
}
return FALSE;
}

void CIOCPServer::CloseAConnection(CIOCPContext *pContext)
{
// 首先从列表中移除要关闭的连接
::EnterCriticalSection(&m_ConnectionListLock);

CIOCPContext* pTest = m_pConnectionList;
if(pTest == pContext)
{
m_pConnectionList = pTest->pNext; // [2009.8.9 mod Lostyears][old: m_pConnectionList = pContext->pNext]
m_nCurrentConnection --;
}
else
{
while(pTest != NULL && pTest->pNext != pContext)
pTest = pTest->pNext;
if(pTest != NULL)
{
pTest->pNext = pContext->pNext;
m_nCurrentConnection --;
}
}

::LeaveCriticalSection(&m_ConnectionListLock);

// 然后关闭客户套节字
::EnterCriticalSection(&pContext->Lock);

if(pContext->s != INVALID_SOCKET)
{
::closesocket(pContext->s);
pContext->s = INVALID_SOCKET;
}
pContext->bClosing = TRUE;

::LeaveCriticalSection(&pContext->Lock);
}
...全文
84 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
Co_priest 2015-09-30
  • 打赏
  • 举报
回复
http://blog.csdn.net/yangzhenping/article/details/5353858 这是我使用的服务器端的iocp代码
Co_priest 2015-09-30
  • 打赏
  • 举报
回复
以上是我在网上拷的源码,然后直接放到我的项目里,自己没有用过IOCP,原理之类的也不清楚,只了解简单的socket通讯。现在遇到的问题是,在客户端退出后,会出现服务器与客户之间的通讯断开(我不知道原因,求大神帮忙),服务器端程序不用重启,客户端再登录时可以发数据不能收到数据,服务端能收到数据,但是不知道为什么没有消息处理。我的感觉问题应该是出在关闭套接字时出现的BUG,求各路大神指点一二,公司着急需要东西,自己也没时间去深入研究IOCP的原理

4,356

社区成员

发帖
与我相关
我的任务
社区描述
通信技术相关讨论
社区管理员
  • 网络通信
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧