求助:IOCP客户端下载数据服务器崩溃?

gischenjun04 2012-05-26 07:33:07
我写了一个IOCP发送和接收程序,发现客户端上传文件没有问题,可是下载服务器数据出现问题。我尝试下载服务器30兆的数据,连续下载5-6次后,服务器流量不断降低,开始还有3兆每秒,后面只有1兆多每秒。如果继续下载,服务器可能崩溃。在即将崩溃的时候,我调试运行,发现有时服务器IOCP的SafeArrayCreateVector创建8192个字节,返回空。更为严重的情况是,有时连创建组件对象都返回空。我开始以为是内存泄露的问题,可是在VC++2005调试运行时,关闭程序没有提示内存泄露。会不会是什么资源发生泄露,还是IOCP的内存锁定的问题,给分100分,请各位专家帮忙看下程序!
首先,我定义的PER_IO_OPERATION_DATA和PER_HANDLE_DATA如下:
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;
}
...全文
145 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
Eleven 2012-05-26
  • 打赏
  • 举报
回复
调试一下啊,看看哪里错误?指针无效?
e3internet 2012-05-26
  • 打赏
  • 举报
回复
代码发:228330140@qq.com只看上面这些也不能判定是哪个地方出了问题。可能是其他地方
gischenjun04 2012-05-26
  • 打赏
  • 举报
回复
这是我的ProcessIO源码:
UINT WINAPI CServerSocket::ProcessIO(LPVOID lpParam)
{
::CoInitializeEx(NULL,COINIT_MULTITHREADED);
//::CoInitialize(NULL);
ServerSocketStruct*ss=(ServerSocketStruct*)lpParam;
CServerSocket*m_pServer=ss->m_pServer;
HANDLE m_Tread=ss->m_Thread;
delete ss;
HANDLE CompletionPort = m_pServer->CompletionPort;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPOVERLAPPED lpOverlapped;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD Flags = 0;
DWORD dwRecv = 0;
VARIANT_BOOL IsOk;
while(true)
{
if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,(PULONG_PTR)&PerHandleData,(LPOVERLAPPED*)&lpOverlapped,INFINITE))
{
if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
{
PerIoData = (LPPER_IO_OPERATION_DATA)CONTAINING_RECORD(lpOverlapped,PER_IO_OPERATION_DATA,Overlapped);
if(PerHandleData==NULL)
{
::GlobalFree(PerIoData);
SetEvent(m_Tread);
::CoUninitialize();
#ifdef _DEBUG
TRACE("接受线程退出\n");
#endif
return 0;
}
IsOk=VARIANT_FALSE;
if(PerHandleData->g_pUser!=NULL)
{
m_pServer->Fire_OnClientQuit((INetUser*)PerHandleData->g_pUser);
CNetUsers*cUsers=(CNetUsers*)m_pServer->pUsers;
cUsers->RemoveUser((INetUser*)PerHandleData->g_pUser,&IsOk);
PerHandleData=NULL;
}
continue;
}
}
PerIoData = (LPPER_IO_OPERATION_DATA)CONTAINING_RECORD(lpOverlapped,PER_IO_OPERATION_DATA,Overlapped);
if(PerHandleData==NULL)
{
::GlobalFree(PerIoData);
SetEvent(m_Tread);
::CoUninitialize();
#ifdef _DEBUG
TRACE("接受线程退出\n");
#endif
return 0;
}
// 说明客户端已经退出
if(BytesTransferred == 0)
{
IsOk=VARIANT_FALSE;
if(PerHandleData->g_pUser!=NULL)
{
CNetUsers*cUsers=(CNetUsers*)m_pServer->pUsers;
m_pServer->Fire_OnClientQuit((INetUser*)PerHandleData->g_pUser);
cUsers->RemoveUser((INetUser*)PerHandleData->g_pUser,&IsOk);
}
continue;
}
if(PerIoData->BytesSend==0)
{
m_pServer->sis.NotifyInBytes(BytesTransferred);
CTime time=CTime::GetCurrentTime();
SYSTEMTIME systime;
time.GetAsSystemTime(systime);//转成systime
double dtime;
SystemTimeToVariantTime(&systime,&dtime);
PerHandleData->RecentTime=dtime;
PerIoData->BytesRecv+=BytesTransferred;
if(PerIoData->BytesLength==0)
{
if(PerIoData->BytesRecv<8)
{
ZeroMemory(&(PerIoData->Overlapped),sizeof(OVERLAPPED));
PerIoData->BytesPin+=BytesTransferred;
PerIoData->DataBuf.buf = PerIoData->Buffer+PerIoData->BytesPin;
PerIoData->DataBuf.len = MAX_BUFFER_SIZE-PerIoData->BytesPin;
WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
continue;
}
char*buffer=(char*)PerIoData->Buffer+PerIoData->BytesFrom; //操作同一块内存区域
LONG BufferSize;
char*ca=(char*)&BufferSize;
memcpy(ca,buffer+4,4);
PerIoData->BytesLength=BufferSize+PACKAGE_HEADER;
}
bool IsValid=true;
if(PerIoData->BytesRecv>=PerIoData->BytesLength)
{
int ValidPin=PerIoData->BytesPin+BytesTransferred;
while(TRUE)
{
char*buffer=(char*)PerIoData->Buffer+PerIoData->BytesFrom; //操作同一块内存区域
LONG BufferSize;
char*ca=(char*)&BufferSize;
memcpy(ca,buffer+4,4);
BufferSize+=PACKAGE_HEADER;
if(PerIoData->BytesFrom+BufferSize>ValidPin)
{
for(int k=PerIoData->BytesFrom;k<ValidPin;k++)
{
PerIoData->Buffer[k-PerIoData->BytesFrom]=PerIoData->Buffer[k];
}
PerIoData->BytesLength=BufferSize;
PerIoData->BytesRecv=ValidPin-PerIoData->BytesFrom;
PerIoData->BytesPin=PerIoData->BytesRecv;
PerIoData->BytesFrom=0;
break;
}
IDataPackage*pdr;
CDataPackage::_CreatorClass::CreateInstance(NULL, __uuidof(IDataPackage), (void**)&pdr);
BYTE*data=new BYTE[BufferSize];
if(data!=NULL)
{
memcpy(data,buffer,BufferSize);
CDataPackage*cPack=(CDataPackage*)pdr;
cPack->buffer=data;
IsValid=m_pServer->NotifyAcceptDataPackage(PerHandleData,pdr);
if(!IsValid)
{
pdr->Release();
break;
}
}
pdr->Release();
PerIoData->BytesFrom+=BufferSize;
BufferSize=PerIoData->BytesPin+BytesTransferred-PerIoData->BytesFrom;
if(BufferSize<PACKAGE_HEADER)
{
for(int k=0;k<BufferSize;k++)
{
PerIoData->Buffer[k]=PerIoData->Buffer[PerIoData->BytesFrom+k];
}
PerIoData->BytesLength=0;
PerIoData->BytesRecv=BufferSize;
PerIoData->BytesPin=BufferSize;
PerIoData->BytesFrom=0;
break;
}
}
}
else
{
PerIoData->BytesPin+=BytesTransferred;
}
if(!IsValid) continue;
// 取得数据并处理
// 继续向 socket 投递WSARecv操作
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.buf = PerIoData->Buffer+PerIoData->BytesPin;
PerIoData->DataBuf.len = MAX_BUFFER_SIZE-PerIoData->BytesPin;
WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}
else
{
m_pServer->sis.NotifyOutBytes(BytesTransferred);
if(PerHandleData->g_pServer==m_pServer)
{
if(BytesTransferred+PerIoData->BytesFrom<PerIoData->BytesSend)
{
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.buf=PerIoData->Buffer+BytesTransferred+PerIoData->BytesFrom;
PerIoData->DataBuf.len=PerIoData->BytesSend-(BytesTransferred+PerIoData->BytesFrom);
PerIoData->BytesSend=PerIoData->BytesSend-(BytesTransferred+PerIoData->BytesFrom);
PerIoData->BytesFrom+=BytesTransferred;
DWORD SendBytes;
WSASend(PerHandleData->Socket,&(PerIoData->DataBuf),1,&SendBytes,0,&(PerIoData->Overlapped),NULL);
}
else
{
INetUser*pUser=(INetUser*)PerHandleData->g_pUser;
CNetUser*cUser=(CNetUser*)pUser;
cUser->SendNextPackage();
}
}
}
}
SetEvent(m_Tread);
::CoUninitialize();
#ifdef _DEBUG
TRACE("接受线程退出\n");
#endif
return 0;
}
gischenjun04 2012-05-26
  • 打赏
  • 举报
回复
我的代码是ATL代码,在我的IOCP的ProcessIO里面,我对PerIOData添加了是否越界的判断,没有找到越界的问题,还有就是SetEvent是保证包是按顺序发送的,即使是弄个收发队列,还是要使用SetEvent类似的方法的!会不会是组件对象不能跨线程呢,我的组件对象都是Both模型呀,支持多线程中的调用的!
「已注销」 2012-05-26
  • 打赏
  • 举报
回复
你可以在程序的不同位置插入堆检测函数,逐步定位越界的地方。
「已注销」 2012-05-26
  • 打赏
  • 举报
回复
一般来说,malloc返回空,并不是因为内存耗尽,而是堆被破坏了.

你应该首先从堆块越界上找原因。
stjay 2012-05-26
  • 打赏
  • 举报
回复
检查是不是线程同步问题

举例就WSASend后SetEvent是有问题的
由于WSASend是异步,此时不一定完成发送数据,
最好将SetEvent放到GetQueuedCompletionStatus里

PER_HANDLE_DATA里各自只有一个收发PER_IO_OPERATION_DATA,必然要做很多同步工作
倒不如弄个收发队列,需要收发时,就申请一个新的PER_IO_OPERATION_DATA

如果自己搞不定,上传代码,等大牛解决吧。
gischenjun04 2012-05-26
  • 打赏
  • 举报
回复
现在发现上传数据也是这个问题呀!!
gischenjun04 2012-05-26
  • 打赏
  • 举报
回复
这个问题已经折磨我好久了,内存问题都检查过很多遍了,感觉上没有!!
1、本课程是一个干货课程,主要讲解如何封装服务器底层,使用Tcp/ip长连接,IDE使用vs2019 c++开发以及使用c++11的一些标准,跨平台windows和linux,服务器性能高效,单服务器压力测试上万无压力,服务器框架是经历过上线产品的验证,框架简单明了,不熟悉底层封装的人,半个小时就能完全掌握服务器框架上手写业务逻辑。2、本课程是一个底层服务器框架教程,主要是教会学员在windows或linux下如何封装一个高效的,避免踩坑的商业级框架,服务器底层使用初始化即开辟内存的技术,使用内存池,服务器运行期间内存不会溢出,非常稳定,同时服务器使用自定义哈希hashContainer,在处理新的连接,新的数据,新的封包,以及解包,发包,粘包的过程,哈希容器性能非常高效,增、删、查、改永远不会随着连接人数的上升而降低性能,增、删、查、改的复杂度永远都是恒定的O(1)。3、服务器底层封装没有使用任何第三方网络库以及任何第三方插件,自由度非常的高,出了任何BUG,你都有办法去修改,查找问题也非常方便,在windows下使用iocp,linux下使用epoll.4、讲解c++纯客户端,主要用于服务器之间通信,也就是说你想搭建多层结构的服务器服务器服务器之间使用socket通信。还可以使用c++客户端做压力测试,开辟多线程连接服务器,教程提供了压力测试,学员可以自己做压力测试服务器性能。5、赠送ue4和unity3d通信底层框架以及多人交互demo,登录,注册,玩家离开,同步主要是教会学员服务器客户端如何交互。6、赠送c++连接mysql数据库框架demo,登录,注册,玩家离开数据持久化.7、服务器教程使用自定义通信协议,同时也支持protobuf,选择权在开发者自己手里,想用什么协议都可以,自由度高。8、服务器教程使用手动敲代码逐句讲解的方式开展教学课程。非喜勿喷,谢谢大家。9、服务器教程提供源码,大家可以在平台提供的地址下载或者联系我,服务器使用c++11部分标准,std::thread,条件变量,线程锁,智能指针等,需要学员具备一定c++知识,购买前请慎重考虑。

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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