昨晚通宵没睡,搞定的一个IOCP 问题,分享一下,不看的绝对会后悔!
昨晚通宵没睡,搞定了这个问题,先看一段代码(下面的代码都做了简化):
上面的这段代码看上去没什么问题,但事实上会引发服务崩溃。接着看:
上面这段代码看上去也没有问题(其实也确实没问题),但死了我无数的脑细胞。
问题描述:
服务器在接受几个连接之后,要么无故退出,要么就是程序出错Windows要关闭它。
通过调试器运行,提示堆破坏,难道有使用了已释放的内存?检查了3个小时,没找出有可能出现内存错误的地方。
后来实在没办法了,把所有的分配内存的地方地改成GlobalAlloc与GlobalFree,但问题仍旧存在。
最后,干脆把所有的内存释放的地方全去掉,然后逐个调试(加上一个,运行一遍),看看到底是哪个内存出错了,昨晚大部会时间花在这上面了(调试堆破坏我只有这一个办法,不知道高手有其它方法没有?望赐教!)。
最后定位到上面的B处 GlobalFree。
但究竟是什么原因造成的呢?
在网上也找不出所以然,最后几乎快要放弃了,但偶然间改了A处的两个地方,服务器安全运行了(激动啊)!!
改变如下:
UINT nRetVal =::WSARecv((SOCKET)clientSocket,&(pIoContext->wsaBuffer),1,&(pIoContext->dwIoSize),&(pIoContext->dwlFlags),&(pIoContext->overlapped), NULL);
变为
DWORD dwRecvbytes=0;
DWORD dwlFlags=0;
UINT nRetVal =::WSARecv((SOCKET)clientSocket,&(pIoContext->wsaBuffer),1,&dwIoSize,&dwlFlags,&(pIoContext->overlapped), NULL);
也就是说,把WSARecv 第4、第5个参数改成使用局部变量。
现在问题是解决了,但具体原因还只是猜测:
如果不使用局部变量作为参数,那么在IO 等待中系统将会一直引用这两个变量,就算是一个IO完成了,系统内部仍然在使用所以当释放内存后,系统再次使用变量时就破坏了堆数据。
上面的废话比较多,希望大家能看懂,同时也希望看明白的说说自己的看法。做服务器我是新手,希望高手多多指点!
//监听线程
DWORD WINAPI CService::ClientListen(void* p)
{
while(TRUE)
{
SOCKET clientSocket=::accept(m_pInstance->m_hServiceSocket,NULL,NULL);
if(clientSocket==SOCKET_ERROR)
continue;
::CreateIoCompletionPort((HANDLE)clientSocket,m_pInstance->m_hServiceIocp,0,0);
LPIOCONTEXT pIoContext=(LPIOCONTEXT)GlobalAlloc(GPTR, sizeof( IOCONTEXT));
pIoContext->wsaBuffer.buf=pIoContext->pWorkBuffer;
pIoContext->wsaBuffer.len=10;
//下面的代码会引发非常诡异的问题,后面会讲到
UINT nRetVal =::WSARecv((SOCKET)clientSocket,&(pIoContext->wsaBuffer),1,&(pIoContext->dwIoSize),&(pIoContext->dwlFlags),&(pIoContext->overlapped), NULL);/*关键位置A*/
}
return 0;
}
上面的这段代码看上去没什么问题,但事实上会引发服务崩溃。接着看:
//完成处理线程
DWORD WINAPI CService::DoService(void* p)
{
DWORD dwBytesXfered=0;
DWORD dwKey=0;
LPIOCONTEXT pIoContext=NULL;
while(TRUE)
{
GetQueuedCompletionStatus(m_pInstance->m_ServiceIocp,&dwBytesXfered,&dwKey,(LPOVERLAPPED*)(&pIoContext),INFINITE);
GlobalFree(pIoContext);/*位置B*/
}
return 0;
}
上面这段代码看上去也没有问题(其实也确实没问题),但死了我无数的脑细胞。
问题描述:
服务器在接受几个连接之后,要么无故退出,要么就是程序出错Windows要关闭它。
通过调试器运行,提示堆破坏,难道有使用了已释放的内存?检查了3个小时,没找出有可能出现内存错误的地方。
后来实在没办法了,把所有的分配内存的地方地改成GlobalAlloc与GlobalFree,但问题仍旧存在。
最后,干脆把所有的内存释放的地方全去掉,然后逐个调试(加上一个,运行一遍),看看到底是哪个内存出错了,昨晚大部会时间花在这上面了(调试堆破坏我只有这一个办法,不知道高手有其它方法没有?望赐教!)。
最后定位到上面的B处 GlobalFree。
但究竟是什么原因造成的呢?
在网上也找不出所以然,最后几乎快要放弃了,但偶然间改了A处的两个地方,服务器安全运行了(激动啊)!!
改变如下:
UINT nRetVal =::WSARecv((SOCKET)clientSocket,&(pIoContext->wsaBuffer),1,&(pIoContext->dwIoSize),&(pIoContext->dwlFlags),&(pIoContext->overlapped), NULL);
变为
DWORD dwRecvbytes=0;
DWORD dwlFlags=0;
UINT nRetVal =::WSARecv((SOCKET)clientSocket,&(pIoContext->wsaBuffer),1,&dwIoSize,&dwlFlags,&(pIoContext->overlapped), NULL);
也就是说,把WSARecv 第4、第5个参数改成使用局部变量。
现在问题是解决了,但具体原因还只是猜测:
如果不使用局部变量作为参数,那么在IO 等待中系统将会一直引用这两个变量,就算是一个IO完成了,系统内部仍然在使用所以当释放内存后,系统再次使用变量时就破坏了堆数据。
上面的废话比较多,希望大家能看懂,同时也希望看明白的说说自己的看法。做服务器我是新手,希望高手多多指点!
...全文
相关推荐
完整的IOCP实例
在传统的I/O模型中,如同步阻塞I/O,当一个I/O请求发起后,调用线程会等待该操作完成,这可能导致线程资源的浪费。而异步I/O模型虽然解决了阻塞问题,但回调函数的执行线程不可控,可能影响性能。
一个自己调试和测试过的IOCP库
作者提到“没办法换积分,出个小的家底”,意味着这是作者分享出来的一个个人项目,可能作为学习交流之用。【标签】中的“IOCP Socket”是指这个库是基于IOCP模型实现的Socket通信库。
iocp 实例分享
该项目实现了一个基于IOCP模型的高性能网络通信框架,支持海量并发连接。核心组件包括缓冲区管理、上下文控制和完成端口线程调度,利用重叠I/O与内存池技术提升效率,适用于Windows平台高负载服务器开
IOCP.zip_IOCP_IOCP 通信_IOCP类 c++_IOCp pudn_pudn IOCP
在"PUDN"社区中,可能有一些关于IOCP的讨论和示例代码。PUDN是一个分享编程资源和技术讨论的平台,这里的“pudn_iocp”标签可能指的是在该社区中有关IOCP的资源或问题解答。
一个很好的完成端口(IOCP)例子
- 注意同步问题,避免数据竞争和死锁。综上所述,这个IOCP的例子是一个学习和实践网络编程,尤其是高性能服务器端设计的重要资源。


网络编程
