一个简单的完成端口代码,不停发送,就有可能出现异常?(十万火急在线等)
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;
}