求助:IOCP客户端下载数据服务器崩溃?
我写了一个IOCP发送和接收程序,发现客户端上传文件没有问题,可是下载服务器数据出现问题。我尝试下载服务器30兆的数据,连续下载5-6次后,服务器流量不断降低,开始还有3兆每秒,后面只有1兆多每秒。如果继续下载,服务器可能崩溃。在即将崩溃的时候,我调试运行,发现有时服务器IOCP的SafeArrayCreateVector创建8192个字节,返回空。更为严重的情况是,有时连创建组件对象都返回空。我开始以为是内存泄露的问题,可是在VC++2005调试运行时,关闭程序没有提示内存泄露。会不会是什么资源发生泄露,还是IOCP的内存锁定的问题,给分100分,请各位专家帮忙看下程序!
首先,我定义的PER_IO_OPERATION_DATA和PER_HANDLE_DATA如下:
- C/C++ code
typedef struct{ OVERLAPPED Overlapped; WSABUF DataBuf; char Buffer[MAX_BUFFER_SIZE]; DWORD BytesSend; DWORD BytesRecv; DWORD BytesLength; DWORD BytesPin; DWORD BytesFrom;}PER_IO_OPERATION_DATA,*LPPER_IO_OPERATION_DATA;typedef struct _PER_HANDLE_DATA{ SOCKET Socket; SOCKADDR_IN addressinfo; char parameters[256]; HANDLE g_pUser; HANDLE g_pServer; DOUBLE RecentTime; LPPER_IO_OPERATION_DATA PerIoData; LPPER_IO_OPERATION_DATA wPerIoData;}PER_HANDLE_DATA,*LPPER_HANDLE_DATA; PER_HANDLE_DATA中,PerIoData表示接收的IO,wPerIoData表示发送的IO。MAX_BUFFER_SIZE为8192。我发送的思路是,在我自定义的INetUser接口中,添加了PostMessage方法,该方法一般在服务器接收数据线程里面收到数据包后调用。PostMessage的思路是:在NetUser组件对象中,创建包队列,PostMessage将包按8192字节划分子包,按顺序添加至队列中,如果开始队列为空,将队列第一个包取出,使用WSASend发送。在在服务器接收数据线程,处理发送消息。如果当前包没有发完,继续WSASend投递,否则调用NetUser组件对象的SendNextPackage继续下一个包的投递。 代码如下: void CNetUser::SendNextPackage(){ //DWORD dwRet=WaitForSingleObject(m_hux,INFINITE); //if(dwRet==WAIT_FAILED) return; if(bClosed!=0) return; WaitForSingleObject(m_sendEvent,INFINITE); if(sendingPackage.GetSize()==0) { SetEvent(m_sendEvent); //SetEvent(m_hux); return; } sendingPackage.GetAt(0)->Release(); sendingPackage.RemoveAt(0); if(sendingPackage.GetSize()==0) { SetEvent(m_sendEvent); //SetEvent(m_hux); return; } IDataPackage*subPack=sendingPackage.GetAt(0); LONG length; subPack->get_CurrentPackageSize(&length); length+=PACKAGE_HEADER; OLE_HANDLE pin; subPack->GetStreamPtr(&pin); BYTE*bbuff=(BYTE*)pin; char*buff=(char*)bbuff; memcpy(lpHandleData->wPerIoData->Buffer,buff,length); lpHandleData->wPerIoData->BytesSend=length; lpHandleData->wPerIoData->DataBuf.buf=lpHandleData->wPerIoData->Buffer; lpHandleData->wPerIoData->DataBuf.len=length; DWORD dwSend; DWORD Flags=0; int len=0; ZeroMemory(&(lpHandleData->wPerIoData->Overlapped),sizeof(OVERLAPPED)); WSASend(lpHandleData->Socket, &lpHandleData->wPerIoData->DataBuf, 1, &dwSend, Flags, &lpHandleData->wPerIoData->Overlapped, NULL); SetEvent(m_sendEvent); //SetEvent(m_hux);}STDMETHODIMP CNetUser::PostMessage(IDataPackage*pdr,DataPackageCoderType type,VARIANT_BOOL*pVal){ AFX_MANAGE_STATE(AfxGetStaticModuleState()); *pVal=VARIANT_FALSE; WaitForSingleObject(m_sendEvent,INFINITE); if(lpHandleData==NULL) { SetEvent(m_sendEvent); return S_OK; } IDataPackageSender*pSender; ::CoCreateInstance(CLSID_DataPackageSender,NULL,CLSCTX_INPROC_SERVER,IID_IDataPackageSender,(void**)&pSender); pSender->put_DataPackageEncodeType(type); VARIANT_BOOL IsOk; pSender->SetSendingDataPackage(pdr,&IsOk); if(!IsOk) { pSender->Release(); SetEvent(m_sendEvent); return S_OK; } LONG oldSize=sendingPackage.GetSize(); IDataPackage*subPack; while(true) { pSender->GetNextPackage(&subPack); if(subPack==NULL) break; sendingPackage.Add(subPack); } pSender->Release(); if((oldSize==0)&&(sendingPackage.GetSize()>0)) { subPack=sendingPackage.GetAt(0); LONG length; subPack->get_CurrentPackageSize(&length); length+=PACKAGE_HEADER; OLE_HANDLE pin; subPack->GetStreamPtr(&pin); BYTE*bbuff=(BYTE*)pin; char*buff=(char*)bbuff; memcpy(lpHandleData->wPerIoData->Buffer,buff,length); lpHandleData->wPerIoData->BytesSend=length; lpHandleData->wPerIoData->DataBuf.buf=lpHandleData->wPerIoData->Buffer; lpHandleData->wPerIoData->DataBuf.len=length; DWORD dwSend; DWORD Flags=0; int len=0; ZeroMemory(&(lpHandleData->wPerIoData->Overlapped),sizeof(OVERLAPPED)); WSASend(lpHandleData->Socket, &lpHandleData->wPerIoData->DataBuf, 1, &dwSend, Flags, &lpHandleData->wPerIoData->Overlapped, NULL); } SetEvent(m_sendEvent); *pVal=VARIANT_TRUE; return S_OK;}
[解决办法]
代码发:228330140@qq.com只看上面这些也不能判定是哪个地方出了问题。可能是其他地方
[解决办法]
调试一下啊,看看哪里错误?指针无效?
[解决办法]
检查是不是线程同步问题
举例就WSASend后SetEvent是有问题的
由于WSASend是异步,此时不一定完成发送数据,
最好将SetEvent放到GetQueuedCompletionStatus里
PER_HANDLE_DATA里各自只有一个收发PER_IO_OPERATION_DATA,必然要做很多同步工作
倒不如弄个收发队列,需要收发时,就申请一个新的PER_IO_OPERATION_DATA
如果自己搞不定,上传代码,等大牛解决吧。
[解决办法]
一般来说,malloc返回空,并不是因为内存耗尽,而是堆被破坏了.
你应该首先从堆块越界上找原因。
[解决办法]
你可以在程序的不同位置插入堆检测函数,逐步定位越界的地方。