一个简单的完成端口代码,不停发送,就有可能出现异常?(十万火急在线等)

rbird 2008-07-28 11:57:36
我写了一个简单的完成端口代码,不停发送,运行一段时间后就会出现异常?一般在new重叠结构时,出现错误。迹象是内存被破坏或者调用了施放的指针。
首先服务端接收到的消息是正常的。
另外我将发送和接收缓冲区设置为0,好像就没有出这个问题了 SO_RCVBUF 和 SO_SNDBUF 设置为0。
为了简单起见,我只做了发送。工作原理是在发送时new了一个重叠结构投递给WSASend。然后在GetQueuedCompletionStatus函数后Delete。
到底是怎么回事呢???
是否是我重叠结构删除错了呢??


1、重叠结构如下:
// 自定义重叠结构
struct OverLapped
{
OverLapped(){ memset(&ol, 0, sizeof(WSAOVERLAPPED)); }
~OverLapped(){}
WSABUF wsaBuf; // 重叠IO数据传输类型,包括char*和长度lenth.
INT OperatorType; // 操作类型
SOCKET ClientSocket; // 客户连接的套接字
CHAR szMB[32];
DWORD buflen;
WSAOVERLAPPED ol; // 标准重叠I/O结构
};

///////////////////////////////////////////////////////////////////////////////

2、工作者线程类如下:(CThread线程类是对线程的简单封装,并且没有问题)
// 工作者线程类
class WorkerThread : public CThread
{
public:
WorkerThread(){ m_hCompletionPort = NULL; }
~WorkerThread(){m_hCompletionPort = NULL;}
virtual void ThreadProc(); // 线程函数
INT Init( HANDLE& hCompletionPort ); // 初始化

private:
HANDLE* m_hCompletionPort; // 绑定的完成端口句柄
};

//////////////////////////////////////////////////////////////////////////////

3、完成端口核心类(初始化和发送)
class IOCP
{
public:
IOCP(void){}
~IOCP(void){}
INT Init();
INT StartConnect( SOCKADDR_IN Address );
INT Send( );
INT End();
//virtual void ThreadProc(); // 线程函数

private:
HANDLE m_hCompletionPort; // 完成端口核心对象句柄
SOCKET m_hConnect;
WorkerThread* m_pWorkerThread; // 完成端口的工作者线程

UINT m_nWorkThreadNum; // 工作者线程数量
ComKey* m_key;
};

//////////////////////////////////////////////////////////////////////////////

4、工作者线程的主要代码:

void WorkerThread::ThreadProc()
{
if( m_hCompletionPort == NULL ){ return; } // 没有初始化

OVERLAPPED *pOL = NULL;
DWORD dwBytesTransferred = 0;
DWORD dwKey = 0;
INT nNum = 0;
OverLapped* pOverLapped = NULL;

while (true)
{
// 如果完成端口有消息来了
nNum = GetQueuedCompletionStatus(*m_hCompletionPort, &dwBytesTransferred, &dwKey, &pOL, INFINITE);

// 首先检验是否是需要退出工作者线程
if (NULL == pOL)
{ break; }

//当得到了指向OVERLAPPED结构的指针以后,
//可以用CONTAINING_RECORD宏取出其中指向扩展结构的指针
//(注意: 接收和发送结果在完成端口应用中要一致)
pOverLapped = CONTAINING_RECORD( pOL, OverLapped, ol );

if(pOverLapped->OperatorType == 1 )
{// 删除重叠结构
SAFE_DELETE(pOverLapped);
continue;
}
}
m_bRun = false;
}

//////////////////////////////////////////////////////////////////////////////


5、发送消息

INT IOCP::Send( )
{
OverLapped* pOverLapped;
pOverLapped = new OverLapped();
//// 初始化
strcpy(pOverLapped->szMB, "hello!");
pOverLapped->wsaBuf.buf = pOverLapped->szMB;
pOverLapped->wsaBuf.len = 32;
pOverLapped->ClientSocket = m_hConnect;
pOverLapped->OperatorType =1;

int nRet = 0;
int len;

nRet = ::WSASend(m_hConnect, (LPWSABUF)&pOverLapped->wsaBuf, 1,(LPDWORD)&len, 0, &(pOverLapped->ol), NULL);

// 如果投递失败, 则针对不同的情况进行不同的处理
if (SOCKET_ERROR == nRet)
{
int nError = ::WSAGetLastError();
switch (nError)
{
// 投递重叠结构成功,数据将稍后收到
case WSA_IO_PENDING:
{
WSASetLastError(0);
return NET_SUCCEED;
}
// 意外原因导致连接需要关闭 (由于较长,就不贴出来了)
//。。。。。。。。
}
}
return NET_SUCCEED;
}


//////////////////////////////////////////////////////////////////////////////


6、初始化和连接服务端(这部分应该没有问题的)。其中我设置了缓冲区
INT IOCP::Init()
{
INT hr = 0;

// 加载通信需要的底层资源
if( FAILED( hr = _OS_WIN32_NET::LoadSystermRes() ) ) return hr;

// 创建完成端口核心对象
if( NULL == ( m_hCompletionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 ) ) ) return -1;

// 初始化完成键
m_key = new ComKey;
m_key->key = 1;

// 开启工作者线程
// 根据CPU个数创建适当数量的工作者线程
SYSTEM_INFO SystemInfo; // 得到计算机的信息
GetSystemInfo(&SystemInfo);
m_nWorkThreadNum = SystemInfo.dwNumberOfProcessors * 2 + 2 <= MAX_WORKTHREAD_NUM ? SystemInfo.dwNumberOfProcessors * 2 + 2 : MAX_WORKTHREAD_NUM;
//m_nWorkThreadNum = 1;
if(NULL == (m_pWorkerThread = new WorkerThread[m_nWorkThreadNum] ) ) return -1;

// 开启工作者线程
for (UINT nIndex = 0; nIndex < m_nWorkThreadNum; nIndex++)
{
m_pWorkerThread[nIndex].Init(m_hCompletionPort);
m_pWorkerThread[nIndex].StartThread();
}
return 0;
}


INT IOCP::StartConnect( SOCKADDR_IN Address )
{
INT hr = 0;
// 创建一个SOCKET, 失败则返回
if( INVALID_SOCKET == ( m_hConnect = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED ) ) ) return -1;

// 设置sock缓冲区(自己封装了一个函数,简单的调用SetSockOpt,应该没有问题的。
if( FAILED( hr = _OSNET::SetSockOpt( m_hConnect, SOCK_RCVBUF, SOCK_SNDBUF ) ) ) return hr;

// 连接服务器
int nRet = 0;
if (::connect( m_hConnect, (sockaddr*)&Address, sizeof(Address)) == SOCKET_ERROR)
{
nRet = ::WSAGetLastError();
::closesocket(m_hConnect);
return 0;
}
::CreateIoCompletionPort((HANDLE)m_hConnect, m_hCompletionPort,(ULONG_PTR)m_key, 0);
return 0;
}
...全文
238 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
z_kris 2008-08-01
  • 打赏
  • 举报
回复
new异常一般只有两个原因
一是可用内存不够,二是new的对象构造函数里抛出异常
主要从这两方面考虑吧

另外,修改编译选项为多线程库试试
rbird 2008-07-31
  • 打赏
  • 举报
回复

vc6 debug版对new有一个问题,次数超过DWORD后会报错,用release版就不会有问题
不知道LZ是不是这个问题

我用的是VC2003.
--------------------------------------------------------------------

send是很频繁的操作吧?在send里面new,容易有内存碎片的
建议用内存池,不知道是不是这个原因,但这样做不太好

我感觉不像这个问题。因为当我把wsa缓存设置为0时,好像就就没有出现这个问题了?当然,最后我会加入内存池的。

Angleyuhj 2008-07-30
  • 打赏
  • 举报
回复
mark
如果我知道 2008-07-30
  • 打赏
  • 举报
回复
mark
hurry281 2008-07-30
  • 打赏
  • 举报
回复
OverLapped 结构体最好定义构造函数对里面的变量全部给上初始值
z_kris 2008-07-30
  • 打赏
  • 举报
回复
你send的次数越多,越有可能是内存碎片导致可用内存变少
new出现异常
z_kris 2008-07-30
  • 打赏
  • 举报
回复
send是很频繁的操作吧?在send里面new,容易有内存碎片的
建议用内存池,不知道是不是这个原因,但这样做不太好
gaomingok 2008-07-29
  • 打赏
  • 举报
回复
vc6 debug版对new有一个问题,次数超过DWORD后会报错,用release版就不会有问题
不知道LZ是不是这个问题
rbird 2008-07-29
  • 打赏
  • 举报
回复
回 coverallwangp :
你的重叠结构如果没有发送成功,你有没有delete你new出来的重叠结构呢?
我看你的代码好像只是在发送成功了,在GetQueuedCompletionStatus函数Delete。

答:我在没有发送成功时是做了Delete的。我认为问题应该与此无关,所以因为篇幅原因,没有粘贴过来。
----------------------------------------------------------------------------------

回lala_benben :
可能是多线程的问题。。我没看到LZ有同步在里面

答:在什么地方需要做多线程互斥,请指示?因为我写的程序非常简单,自我感觉没有需要做互斥的变量。
-----------------------------------------------------------------------------------

回homesos:
应该是内存使用的问题
用windbg定位一下异常原因。

答:我也认为是内存使用的问题。但一直没找到问题所在?问题断在我new重叠结构的地方,但是因为是多线程,所以不敢确定问题。非常希望你能指出问题所在,
是否需要将全部源码发出?
------------------------------------------------------------------------------------

回elovenana :
strcpy(pOverLapped->szMB, "hello!");
pOverLapped->wsaBuf.buf = pOverLapped->szMB;
pOverLapped->wsaBuf.len = 32;
看着这句像是有问题的;
你直接指定len是32,szMB里面,肯定会有没有初始化掉的东西;

答:这个长度我是任意填写的,但我认为与此无关。因为即便是我多发送了没有初始化的东西,也是在服务端接收到乱码而已。而与发送端不应该出错。
------------------------------------------------------------------------------------


再次感谢大家的帮助,希望大家能再帮我看看。谢谢!!
长安宁 2008-07-29
  • 打赏
  • 举报
回复
strcpy(pOverLapped->szMB, "hello!");
pOverLapped->wsaBuf.buf = pOverLapped->szMB;
pOverLapped->wsaBuf.len = 32;
看着这句像是有问题的;
你直接指定len是32,szMB里面,肯定会有没有初始化掉的东西;
homesos 2008-07-29
  • 打赏
  • 举报
回复
应该是内存使用的问题
用windbg定位一下异常原因
lala_benben 2008-07-29
  • 打赏
  • 举报
回复
可能是多线程的问题。。我没看到LZ有同步在里面
coverallwangp 2008-07-29
  • 打赏
  • 举报
回复
你的重叠结构如果没有发送成功,你有没有delete你new出来的重叠结构呢?
我看你的代码好像只是在发送成功了,在GetQueuedCompletionStatus函数Delete。


如果没有发送成功,这时候你也已经new了,应该也delete
xkyx_cn 2008-07-29
  • 打赏
  • 举报
回复
先mark

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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