完成端口

改编自小猪的完成端口教程 添加了发送 和一些错误检查

头文件

#ifndef _IOCPMODULE_H_20161209_
#define _IOCPMODULE_H_20161209_


// winsock 2 的头文件和库
#include <winsock2.h>
#include <MSWSock.h>
#pragma comment(lib,"ws2_32.lib")

#include "VpPublic.h"

// 如果确实客户端发来的每组数据都比较少,那么就设置得小一些,省内存
#define MAX_BUFFER_LEN        1024  
// 默认端口
#define DEFAULT_PORT          12345    
// 默认IP地址
#define DEFAULT_IP            ("127.0.0.1")

#include <assert.h>
#include <vector>
#include <string>
#include <map>
using namespace std;

class CCDAPacket;

//
// 在完成端口上投递的I/O操作的类型
typedef enum _OPERATION_TYPE  
{  
	ACCEPT_POSTED,                     // 标志投递的Accept操作
	SEND_POSTED,                       // 标志投递的是发送操作
	RECV_POSTED,                       // 标志投递的是接收操作
	NULL_POSTED                        // 用于初始化,无意义

}OPERATION_TYPE;

//====================================================================================
//
//				单IO数据结构体定义(用于每一个重叠操作的参数)
//
//====================================================================================

typedef struct _PER_IO_CONTEXT
{
	OVERLAPPED     m_Overlapped;                               // 每一个重叠网络操作的重叠结构(针对每一个Socket的每一个操作,都要有一个)              
	SOCKET         m_sockAccept;                               // 这个网络操作所使用的Socket
	WSABUF         m_wsaBuf;                                   // WSA类型的缓冲区,用于给重叠操作传参数的
	char           m_szBuffer[MAX_BUFFER_LEN];                 // 这个是WSABUF里具体存字符的缓冲区
	OPERATION_TYPE m_OpType;                                   // 标识网络操作的类型(对应上面的枚举)

	// 初始化
	_PER_IO_CONTEXT()
	{
		ZeroMemory(&m_Overlapped, sizeof(m_Overlapped));  
		ZeroMemory( m_szBuffer,MAX_BUFFER_LEN );
		m_sockAccept = INVALID_SOCKET;
		m_wsaBuf.buf = m_szBuffer;
		m_wsaBuf.len = MAX_BUFFER_LEN;
		m_OpType     = NULL_POSTED;
	}
	// 释放掉Socket
	~_PER_IO_CONTEXT()
	{	
/*
		if( m_sockAccept!=INVALID_SOCKET )
		{
			closesocket(m_sockAccept);
			m_sockAccept = INVALID_SOCKET;
		}
*/
	}
	// 重置缓冲区内容
	void ResetBuffer()
	{
		ZeroMemory( m_szBuffer,MAX_BUFFER_LEN );
	}

} PER_IO_CONTEXT, *PPER_IO_CONTEXT;


//====================================================================================
//
//				单句柄数据结构体定义(用于每一个完成端口,也就是每一个Socket的参数)
//
//====================================================================================

typedef struct _PER_SOCKET_CONTEXT
{  
	SOCKET      m_Socket;                                  // 每一个客户端连接的Socket
	SOCKADDR_IN m_ClientAddr;                              // 客户端的地址
	std::vector<_PER_IO_CONTEXT*> m_arrayIoContext;             // 客户端网络操作的上下文数据,
	                                                       // 也就是说对于每一个客户端Socket,是可以在上面同时投递多个IO请求的
	char		m_sBuffer[1024*100];
	int			m_nTotalLen;

	IceUtil::Mutex      m_IOContextMutex;			            // 用于IOcontext管理
	IceUtil::Mutex		m_oDataParseMutex;						//用数据buff

	// 初始化
	_PER_SOCKET_CONTEXT()
	{
		m_Socket = INVALID_SOCKET;
		memset(&m_ClientAddr, 0, sizeof(m_ClientAddr)); 

		memset(m_sBuffer,0,sizeof(m_sBuffer));
		m_nTotalLen = 0;

	}

	// 释放资源
	~_PER_SOCKET_CONTEXT()
	{
		if( m_Socket!=INVALID_SOCKET )
		{
			closesocket( m_Socket );
		    m_Socket = INVALID_SOCKET;
		}

		IceUtil::Mutex::Lock guard(m_IOContextMutex);
		for (int i = 0;i<m_arrayIoContext.size();i++)
		{
			delete m_arrayIoContext[i];
		}
		m_arrayIoContext.clear();

		std::string sip = inet_ntoa(m_ClientAddr.sin_addr);
		int nPort = m_ClientAddr.sin_port;

		OS_LOG_INFO("客户端 资源释放 IP:"<< sip.c_str()<<" Port:"<<nPort );

	}

	_PER_IO_CONTEXT* GetNewIoContext()
	{
		IceUtil::Mutex::Lock guard(m_IOContextMutex);

		_PER_IO_CONTEXT* p = new _PER_IO_CONTEXT;
		m_arrayIoContext.push_back( p );
		
		return p;
	}

	// 从数组中移除一个指定的IoContext
	void RemoveContext( _PER_IO_CONTEXT* pContext )
	{
//		assert( pContext!=NULL && m_arrayIoContext.size()>0 );
		if( pContext ==NULL || m_arrayIoContext.size()==0 )
		{
			OS_LOG_INFO("移除失败: arraysize:"<<m_arrayIoContext.size());
			return ;
		}

		IceUtil::Mutex::Lock guard(m_IOContextMutex);
		std::vector<_PER_IO_CONTEXT*>::iterator it_find = m_arrayIoContext.begin();
		for (; it_find != m_arrayIoContext.end();it_find++)
		{
			if (pContext == *it_find)
			{
				delete pContext;
				pContext = NULL;
				m_arrayIoContext.erase(it_find);

//				OS_LOG_INFO("成功移除!");
				break;
			}
		}

	}

	void ParsePacket(PER_IO_CONTEXT* pIoContext);

} PER_SOCKET_CONTEXT, *PPER_SOCKET_CONTEXT;


//====================================================================================
//
//				CIOCPModel类定义
//
//====================================================================================

// 工作者线程的线程参数
class CIOCPModel;
typedef struct _tagThreadParams_WORKER
{
	CIOCPModel* pIOCPModel;                                   // 类指针,用于调用类中的函数
	int         nThreadNo;                                    // 线程编号

} THREADPARAMS_WORKER,*PTHREADPARAM_WORKER; 

// CIOCPModel类
class CIOCPModel
{
public:
	CIOCPModel(void);
	~CIOCPModel(void);

public:

	// 启动服务器
	bool Start();

	//	停止服务器
	void Stop();

	// 加载Socket库
	bool LoadSocketLib();

	// 卸载Socket库,彻底完事
	void UnloadSocketLib() { WSACleanup(); }

	// 获得本机的IP地址
	std::string  GetLocalIP();

	// 设置监听端口
	void SetPort( const int& nPort ) { m_nPort=nPort; }

protected:

	// 初始化IOCP
	bool _InitializeIOCP();

	// 初始化Socket
	bool _InitializeListenSocket();

	// 最后释放资源
	void _DeInitialize();

	// 投递Accept请求
	bool _PostAccept( PER_IO_CONTEXT* pAcceptIoContext ); 

	// 投递接收数据请求
	bool _PostRecv( PER_IO_CONTEXT* pIoContext );

	// 在有客户端连入的时候,进行处理
	bool _DoAccpet( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext );

	// 在有接收的数据到达的时候,进行处理
	bool _DoRecv( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext );

	// 将客户端的相关信息存储到数组中
	void _AddToContextList( PER_SOCKET_CONTEXT *pSocketContext );

	// 将客户端的信息从数组中移除
	void _RemoveContext( PER_SOCKET_CONTEXT *pSocketContext );

	// 清空客户端信息
	void _ClearContextList();

	//判断指定socketcontext 是否还在链表中
	bool IsSocketInContextList(PER_SOCKET_CONTEXT* pSocketContext);

	// 将句柄绑定到完成端口中
	bool _AssociateWithIOCP( PER_SOCKET_CONTEXT *pContext);

	// 处理完成端口上的错误
	bool HandleError( PER_SOCKET_CONTEXT *pContext,const DWORD& dwErr );

	// 线程函数,为IOCP请求服务的工作者线程
	static DWORD WINAPI _WorkerThread(LPVOID lpParam);

	// 获得本机的处理器数量
	int _GetNoOfProcessors();

	// 判断客户端Socket是否已经断开
	bool _IsSocketAlive(SOCKET s);


	bool _PostSend(PER_IO_CONTEXT* pIoContext);
	bool _DoSend(PER_SOCKET_CONTEXT* pSocketContext,PER_IO_CONTEXT* pIoContext);

public:
	//外部接口
//	int SendPacket(unsigned int ip, unsigned short port, CCDAPacket& oPacket);
	int SendPacket(unsigned int ip, unsigned short port, char* pBuff ,int nBufflen);

private:

	HANDLE                       m_hShutdownEvent;              // 用来通知线程系统退出的事件,为了能够更好的退出线程

	HANDLE                       m_hIOCompletionPort;           // 完成端口的句柄

	HANDLE*                      m_phWorkerThreads;             // 工作者线程的句柄指针

	int		                     m_nThreads;                    // 生成的线程数量

	std::string                  m_strIP;                       // 服务器端的IP地址
	int                          m_nPort;                       // 服务器端的监听端口

	IceUtil::Mutex		          m_ContextListMutext;               // 用于Worker线程同步的互斥量

//	std::vector<PER_SOCKET_CONTEXT*>  m_arrayClientContext;          // 客户端Socket的Context信息        
	std::map<SOCKET,PER_SOCKET_CONTEXT*>  m_arrayClientContext;          // 客户端Socket的Context信息        

	PER_SOCKET_CONTEXT*          m_pListenContext;              // 用于监听的Socket的Context信息

	LPFN_ACCEPTEX                m_lpfnAcceptEx;                // AcceptEx 和 GetAcceptExSockaddrs 的函数指针,用于调用这两个扩展函数
	LPFN_GETACCEPTEXSOCKADDRS    m_lpfnGetAcceptExSockAddrs; 

};

#endif

cplusplus

#include "IOCPModel.h"
#include "VpPublic.h"
#include "base/baseUtil.h"

#include "CDAPacket/CdaPacket.h"

// 每一个处理器上产生多少个线程(为了最大限度的提升服务器性能,详见配套文档)
#define WORKER_THREADS_PER_PROCESSOR 2
// 同时投递的Accept请求的数量(这个要根据实际的情况灵活设置)
#define MAX_POST_ACCEPT              10
// 传递给Worker线程的退出信号
#define EXIT_CODE                    NULL


// 释放指针和句柄资源的宏

// 释放指针宏
#define RELEASE(x)                      {if(x != NULL ){delete x;x=NULL;}}
// 释放句柄宏
#define RELEASE_HANDLE(x)               {if(x != NULL && x!=INVALID_HANDLE_VALUE){ CloseHandle(x);x = NULL;}}
// 释放Socket宏
#define RELEASE_SOCKET(x)               {if(x !=INVALID_SOCKET) { closesocket(x);x=INVALID_SOCKET;}}

void _PER_SOCKET_CONTEXT::ParsePacket(PER_IO_CONTEXT* pIoContext)
{
	if (pIoContext == NULL)
	{
		OS_LOG_INFO("pIoContext FAILED!");
		return ;
	}

	memcpy( m_sBuffer,pIoContext->m_szBuffer,pIoContext->m_Overlapped.InternalHigh);
	m_nTotalLen +=  pIoContext->m_Overlapped.InternalHigh;

	while (1)
	{
		if (m_nTotalLen < CDA_HEAD_LEN)
		{
			//OS_LOG_INFO("长度未够 totallen:"<< m_nTotalLen);
			break;
		}

		int nPacketLen = 0;
		char* pBuff = m_sBuffer;
		DECODE_INT(pBuff,nPacketLen);

		if (nPacketLen < CDA_HEAD_LEN || nPacketLen > m_nTotalLen)
		{
			OS_LOG_INFO("数据包解析出错!");
			break;
		}

		CCDAPacket* pPacket = CCDAPacket::NewPacket(m_sBuffer);
		if (pPacket == NULL)
		{
			OS_LOG_INFO("new packet error");
			break;
		}else
		{
			int nDecodelen = pPacket->DecodePacket(m_sBuffer,nPacketLen);
			if (nPacketLen != nDecodelen)
			{
				OS_LOG_INFO("DECODE PACKET ERROR");
				delete pPacket;
				pPacket = NULL;
				break;
			}
			pPacket->m_pSocketContext = this;

			//为检查心跳超时 打印
			if (pPacket->m_uMsgId == CDA_REPORT_GPS_REQ)
			{
				//直接打印ip 端口用以区别
				std::string sip = inet_ntoa(m_ClientAddr.sin_addr);
				int nPort = m_ClientAddr.sin_port;

				int nSeqNo = pPacket->m_uSeqNo;
				OS_LOG_INFO("收到GPS IP:"<< sip.c_str()<<" Port:"<< nPort<<" nSeqNo:"<<nSeqNo);
			}

			CCDAPacket::OnPacket(pPacket);

			if (m_nTotalLen >= nPacketLen)
			{
				memmove(m_sBuffer,m_sBuffer+nPacketLen,m_nTotalLen-nPacketLen);
				m_nTotalLen -= nPacketLen;
			}else
			{
				OS_LOG_INFO("数据包出错");
				break;
			}
		}

	}

}




CIOCPModel::CIOCPModel(void):
							m_nThreads(0),
							m_hShutdownEvent(NULL),
							m_hIOCompletionPort(NULL),
							m_phWorkerThreads(NULL),
							m_strIP(DEFAULT_IP),
							m_nPort(DEFAULT_PORT),
							m_lpfnAcceptEx( NULL ),
							m_pListenContext( NULL )
{
}


CIOCPModel::~CIOCPModel(void)
{
	// 确保资源彻底释放
	this->Stop();
}




///
// 工作者线程:  为IOCP请求服务的工作者线程
///

DWORD WINAPI CIOCPModel::_WorkerThread(LPVOID lpParam)
{    
	THREADPARAMS_WORKER* pParam = (THREADPARAMS_WORKER*)lpParam;
	CIOCPModel* pIOCPModel = (CIOCPModel*)pParam->pIOCPModel;
	int nThreadNo = (int)pParam->nThreadNo;


	OVERLAPPED           *pOverlapped = NULL;
	PER_SOCKET_CONTEXT   *pSocketContext = NULL;
	DWORD                dwBytesTransfered = 0;

	// 循环处理请求,知道接收到Shutdown信息为止
	while (WAIT_OBJECT_0 != WaitForSingleObject(pIOCPModel->m_hShutdownEvent, 0))
	{
		BOOL bReturn = GetQueuedCompletionStatus(
			pIOCPModel->m_hIOCompletionPort,
			&dwBytesTransfered,
			(PULONG_PTR)&pSocketContext,
			&pOverlapped,
			INFINITE);

		// 如果收到的是退出标志,则直接退出
		if ( EXIT_CODE==(DWORD)pSocketContext )
		{
			OS_LOG_INFO("收到退出标志:EXIT_CODE");
			break;
		}

		// 判断是否出现了错误
		if( !bReturn )  
		{  
			DWORD dwErr = GetLastError();

			// 显示一下提示信息
			if( !pIOCPModel->HandleError( pSocketContext,dwErr ) )
			{
//				break;
			}

			continue;  
		}  
		else  
		{  	
			// 读取传入的参数
			PER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_CONTEXT, m_Overlapped);  

			// 判断是否有客户端断开了
			if((0 == dwBytesTransfered) && ( RECV_POSTED==pIoContext->m_OpType || SEND_POSTED==pIoContext->m_OpType))  
			{  
				// 释放掉对应的资源

				std::string sip = inet_ntoa(pSocketContext->m_ClientAddr.sin_addr);
				int nPort = pSocketContext->m_ClientAddr.sin_port;

				OS_LOG_INFO("客户端主动断开连接 IP:"<< sip.c_str()<<" Port:"<< nPort);
				pIOCPModel->_RemoveContext( pSocketContext );

 				continue;  
			}  
			else
			{
				switch( pIoContext->m_OpType )  
				{  
				case ACCEPT_POSTED:
					{ 
						pIOCPModel->_DoAccpet( pSocketContext, pIoContext );							
					}
					break;
				case RECV_POSTED:
					{
						pIOCPModel->_DoRecv( pSocketContext,pIoContext );
					}
					break;
				case SEND_POSTED:
					{
						pIOCPModel->_DoSend(pSocketContext,pIoContext);
					}
					break;
				default:
					OS_LOG_INFO("_WorkThread中的 pIoContext->m_OpType 参数异常");
					break;
				}
			}
		}
	}

	OS_LOG_INFO("工作者线程 %d 号退出."<< nThreadNo);

	// 释放线程参数
	RELEASE(lpParam);	

	return 0;
}



//====================================================================================
//
//				    系统初始化和终止
//
//====================================================================================


bool CIOCPModel::LoadSocketLib()
{ 
	GetLocalIP();
	return true;
}

//
//	启动服务器
bool CIOCPModel::Start()
{
	// 建立系统退出的事件通知
	m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	// 初始化IOCP
	if (false == _InitializeIOCP())
	{
		OS_LOG_INFO("initiocp failed!");
		return false;
	}


	// 初始化Socket
	if( false==_InitializeListenSocket() )
	{
		OS_LOG_INFO("initlistensocket failed!");
		this->_DeInitialize();
		return false;
	}

	return true;
}



//	开始发送系统退出消息,退出完成端口和线程资源
void CIOCPModel::Stop()
{
	if( m_pListenContext!=NULL && m_pListenContext->m_Socket!=INVALID_SOCKET )
	{
		// 激活关闭消息通知
		SetEvent(m_hShutdownEvent);

		for (int i = 0; i < m_nThreads; i++)
		{
			// 通知所有的完成端口操作退出
			PostQueuedCompletionStatus(m_hIOCompletionPort, 0, (DWORD)EXIT_CODE, NULL);
		}

		// 等待所有的客户端资源退出
		WaitForMultipleObjects(m_nThreads, m_phWorkerThreads, TRUE, INFINITE);

		// 清除客户端列表信息
		this->_ClearContextList();

		// 释放其他资源
		this->_DeInitialize();

	}	
}



// 初始化完成端口
bool CIOCPModel::_InitializeIOCP()
{
	// 建立第一个完成端口
	m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );
	OS_LOG_INFO("IOCP handle:"<<m_hIOCompletionPort);

	if ( NULL == m_hIOCompletionPort)
	{
		OS_LOG_INFO("IOCP Create Failed");
		return false;
	}

	// 根据本机中的处理器数量,建立对应的线程数
	m_nThreads = WORKER_THREADS_PER_PROCESSOR * _GetNoOfProcessors();
	
	// 为工作者线程初始化句柄
	m_phWorkerThreads = new HANDLE[m_nThreads];
	
	// 根据计算出来的数量建立工作者线程
	DWORD nThreadID;
	for (int i = 0; i < m_nThreads; i++)
	{
		THREADPARAMS_WORKER* pThreadParams = new THREADPARAMS_WORKER;
		pThreadParams->pIOCPModel = this;
		pThreadParams->nThreadNo  = i+1;
		m_phWorkerThreads[i] = ::CreateThread(0, 0, _WorkerThread, (void *)pThreadParams, 0, &nThreadID);
	}


	return true;
}


/
// 初始化Socket
bool CIOCPModel::_InitializeListenSocket()
{
	// AcceptEx 和 GetAcceptExSockaddrs 的GUID,用于导出函数指针
	GUID GuidAcceptEx = WSAID_ACCEPTEX;  
	GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS; 

	// 服务器地址信息,用于绑定Socket
	struct sockaddr_in ServerAddress;

	// 生成用于监听的Socket的信息
	m_pListenContext = new PER_SOCKET_CONTEXT;

	// 需要使用重叠IO,必须得使用WSASocket来建立Socket,才可以支持重叠IO操作
	m_pListenContext->m_Socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (INVALID_SOCKET == m_pListenContext->m_Socket) 
	{
		OS_LOG_INFO("创建listen socket 失败");
		return false;
	}


	// 将Listen Socket绑定至完成端口中
	HANDLE handle = CreateIoCompletionPort( (HANDLE)m_pListenContext->m_Socket, m_hIOCompletionPort,(DWORD)m_pListenContext, 0);
	if( NULL== handle)  
	{  
		OS_LOG_INFO("完成端口绑定失败!");
		RELEASE_SOCKET( m_pListenContext->m_Socket );
		return false;
	}

	// 填充地址信息
	ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress));
	ServerAddress.sin_family = AF_INET;
                     
	ServerAddress.sin_addr.s_addr = inet_addr(m_strIP.c_str());         
	ServerAddress.sin_port = htons(m_nPort);                          

	// 绑定地址和端口
	if (SOCKET_ERROR == bind(m_pListenContext->m_Socket, (struct sockaddr *) &ServerAddress, sizeof(ServerAddress))) 
	{
		OS_LOG_INFO("listen socket bind failesd!");
		return false;
	}
	

	// 开始进行监听
	if (SOCKET_ERROR == listen(m_pListenContext->m_Socket,SOMAXCONN))
	{
		OS_LOG_INFO("listen failed!");	
		return false;
	}


	// 调用WSAIotl 可使用 重叠IO
	// 使用AcceptEx函数,因为这个是属于WinSock2规范之外的微软另外提供的扩展函数
	// 所以需要额外获取一下函数的指针,
	// 获取AcceptEx函数指针
	DWORD dwBytes = 0;  
	if(SOCKET_ERROR == WSAIoctl(
		m_pListenContext->m_Socket, 
		SIO_GET_EXTENSION_FUNCTION_POINTER, 
		&GuidAcceptEx, 
		sizeof(GuidAcceptEx), 
		&m_lpfnAcceptEx, 
		sizeof(m_lpfnAcceptEx), 
		&dwBytes, 
		NULL, 
		NULL))  
	{  
		this->_DeInitialize();
		return false;  
	}  

	// 获取GetAcceptExSockAddrs函数指针,也是同理
	if(SOCKET_ERROR == WSAIoctl(
		m_pListenContext->m_Socket, 
		SIO_GET_EXTENSION_FUNCTION_POINTER, 
		&GuidGetAcceptExSockAddrs,
		sizeof(GuidGetAcceptExSockAddrs), 
		&m_lpfnGetAcceptExSockAddrs, 
		sizeof(m_lpfnGetAcceptExSockAddrs),   
		&dwBytes, 
		NULL, 
		NULL))  
	{  
		this->_DeInitialize();
		return false; 
	}  


	// 为AcceptEx 准备参数,然后投递AcceptEx I/O请求
	for( int i=0;i<MAX_POST_ACCEPT;i++ )
	{
		// 新建一个IO_CONTEXT
		PER_IO_CONTEXT* pAcceptIoContext = m_pListenContext->GetNewIoContext();

		if( false==this->_PostAccept( pAcceptIoContext ) )
		{
			m_pListenContext->RemoveContext(pAcceptIoContext);
			return false;
		}
	}

	return true;
}


//	最后释放掉所有资源
void CIOCPModel::_DeInitialize()
{
	// 删除客户端列表的互斥量
//	DeleteCriticalSection(&m_csContextList);

	// 关闭系统退出事件句柄
	RELEASE_HANDLE(m_hShutdownEvent);

	// 释放工作者线程句柄指针
	for( int i=0;i<m_nThreads;i++ )
	{
		RELEASE_HANDLE(m_phWorkerThreads[i]);
	}
	
	RELEASE(m_phWorkerThreads);

	// 关闭IOCP句柄
	RELEASE_HANDLE(m_hIOCompletionPort);

	// 关闭监听Socket
	RELEASE(m_pListenContext);
}


//====================================================================================
//
//				    投递完成端口请求
//
//====================================================================================


//
// 投递Accept请求
bool CIOCPModel::_PostAccept( PER_IO_CONTEXT* pAcceptIoContext )
{
	if( INVALID_SOCKET == m_pListenContext->m_Socket )
	{
		OS_LOG_INFO("post accept socket NULL");
		return false;
	}


	// 准备参数
	DWORD dwBytes = 0;  
	pAcceptIoContext->m_OpType = ACCEPT_POSTED;  
	WSABUF *p_wbuf   = &pAcceptIoContext->m_wsaBuf;
	OVERLAPPED *p_ol = &pAcceptIoContext->m_Overlapped;
	
	// 为以后新连入的客户端先准备好Socket( 这个是与传统accept最大的区别 ) 
	pAcceptIoContext->m_sockAccept  = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);  
	if( INVALID_SOCKET==pAcceptIoContext->m_sockAccept )  
	{  
		return false;  
	} 

	// 投递AcceptEx
	if(FALSE == m_lpfnAcceptEx( m_pListenContext->m_Socket, pAcceptIoContext->m_sockAccept, p_wbuf->buf, p_wbuf->len - ((sizeof(SOCKADDR_IN)+16)*2),   
								sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, &dwBytes, p_ol))  
	{  
		if(WSA_IO_PENDING != WSAGetLastError())  
		{  
			return false;  
		}  
	} 

	return true;
}



// 传入的是ListenSocket的Context,我们需要复制一份出来给新连入的Socket用
// 原来的Context还是要在上面继续投递下一个Accept请求
//
bool CIOCPModel::_DoAccpet( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext )
{
	SOCKADDR_IN* ClientAddr = NULL;
	SOCKADDR_IN* LocalAddr = NULL;  
	int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);  

	///
	// 1. 首先取得连入客户端的地址信息
	// 这个 m_lpfnGetAcceptExSockAddrs 不得了啊~~~~~~
	// 不但可以取得客户端和本地端的地址信息,还能顺便取出客户端发来的第一组数据,老强大了...
	this->m_lpfnGetAcceptExSockAddrs(pIoContext->m_wsaBuf.buf, pIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN)+16)*2),  
		sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);  



	//
	// 2. 这里需要注意,这里传入的这个是ListenSocket上的Context,这个Context我们还需要用于监听下一个连接
	// 所以我还得要将ListenSocket上的Context复制出来一份为新连入的Socket新建一个SocketContext

	PER_SOCKET_CONTEXT* pNewSocketContext = new PER_SOCKET_CONTEXT;
	pNewSocketContext->m_Socket           = pIoContext->m_sockAccept;
	memcpy(&(pNewSocketContext->m_ClientAddr), ClientAddr, sizeof(SOCKADDR_IN));

	// 参数设置完毕,将这个Socket和完成端口绑定(这也是一个关键步骤)
	if( false==this->_AssociateWithIOCP( pNewSocketContext ) )
	{
		RELEASE( pNewSocketContext );
		return false;
	}

	std::string sip =  inet_ntoa(ClientAddr->sin_addr);
	OS_LOG_INFO("Client connected from:IP "<<sip.c_str()<<" PORT:"<<ClientAddr->sin_port);


	///
	// 3. 继续,建立其下的IoContext,用于在这个Socket上投递第一个Recv数据请求
	PER_IO_CONTEXT* pNewIoContext = pNewSocketContext->GetNewIoContext();
	pNewIoContext->m_OpType       = RECV_POSTED;
	pNewIoContext->m_sockAccept   = pNewSocketContext->m_Socket;

	// 如果Buffer需要保留,就自己拷贝一份出来
	//处理第一个包
	pNewSocketContext->ParsePacket(pIoContext);

	// 绑定完毕之后,就可以开始在这个Socket上投递完成请求了
	if( false==this->_PostRecv( pNewIoContext) )
	{
		pNewSocketContext->RemoveContext( pNewIoContext );
		return false;
	}

	/
	// 4. 如果投递成功,那么就把这个有效的客户端信息,加入到ContextList中去(需要统一管理,方便释放资源)
	this->_AddToContextList( pNewSocketContext );

	
	// 5. 使用完毕之后,把Listen Socket的那个IoContext重置,然后准备投递新的AcceptEx
	pIoContext->ResetBuffer();
	return this->_PostAccept( pIoContext ); 	
}


// 投递接收数据请求
bool CIOCPModel::_PostRecv( PER_IO_CONTEXT* pIoContext )
{
	// 初始化变量
	DWORD dwFlags = 0;
	DWORD dwBytes = 0;
	WSABUF *p_wbuf   = &pIoContext->m_wsaBuf;
	OVERLAPPED *p_ol = &pIoContext->m_Overlapped;

	pIoContext->ResetBuffer();
	pIoContext->m_OpType = RECV_POSTED;

	// 初始化完成后,,投递WSARecv请求
	int nBytesRecv = WSARecv( pIoContext->m_sockAccept, p_wbuf, 1, &dwBytes, &dwFlags, p_ol, NULL );

	// 如果返回值错误,并且错误的代码并非是Pending的话,那就说明这个重叠请求失败了
	if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
	{
		return false;
	}
	return true;
}

/
// 在有接收的数据到达的时候,进行处理
bool CIOCPModel::_DoRecv( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext )
{
	// 先把上一次的数据显示出现,然后就重置状态,发出下一个Recv请求
	SOCKADDR_IN* ClientAddr = &pSocketContext->m_ClientAddr;

	if (IsSocketInContextList(pSocketContext))
	{
		//打解包
		pSocketContext->ParsePacket(pIoContext);
	}

	// 然后开始投递下一个WSARecv请求
	return _PostRecv( pIoContext );
}



/
// 将句柄(Socket)绑定到完成端口中
bool CIOCPModel::_AssociateWithIOCP( PER_SOCKET_CONTEXT *pContext )
{
	// 将用于和客户端通信的SOCKET绑定到完成端口中
	HANDLE hTemp = CreateIoCompletionPort((HANDLE)pContext->m_Socket, m_hIOCompletionPort, (DWORD)pContext, 0);

	if (NULL == hTemp)
	{
		return false;
	}

	return true;
}




//====================================================================================
//
//				    ContextList 相关操作
//
//====================================================================================


//
// 将客户端的相关信息存储到数组中
void CIOCPModel::_AddToContextList( PER_SOCKET_CONTEXT *pHandleData )
{
	IceUtil::Mutex::Lock guard(m_ContextListMutext);
//	m_arrayClientContext.push_back(pHandleData);
	m_arrayClientContext.insert(std::make_pair(pHandleData->m_Socket,pHandleData));
}


//	移除某个特定的Context
void CIOCPModel::_RemoveContext( PER_SOCKET_CONTEXT *pSocketContext )
{
	IceUtil::Mutex::Lock guard(m_ContextListMutext);

	//vector<PER_SOCKET_CONTEXT*>::iterator it_find = m_arrayClientContext.begin();
	//for (;it_find != m_arrayClientContext.end();it_find++)
	//{
	//	if (*it_find == pSocketContext)
	//	{
	//		RELEASE(pSocketContext);
	//		m_arrayClientContext.erase(it_find);
	//		break;
	//	}
	//}

	std::map<SOCKET,PER_SOCKET_CONTEXT*>::iterator it_find = m_arrayClientContext.find(pSocketContext->m_Socket);
	if (it_find != m_arrayClientContext.end())
	{
		RELEASE(pSocketContext);
		m_arrayClientContext.erase(it_find);
	}

	if (m_arrayClientContext.size() == 0)
	{
		OS_LOG_INFO("客户端已经完全丢失连接");
	}

}


// 清空客户端信息
void CIOCPModel::_ClearContextList()
{
	IceUtil::Mutex::Lock guard(m_ContextListMutext);
	//for(int i = 0;i<m_arrayClientContext.size();i++)
	//{
	//	delete m_arrayClientContext[i];
	//}
	//m_arrayClientContext.clear();

	std::map<SOCKET,PER_SOCKET_CONTEXT*>::iterator it_find = m_arrayClientContext.begin();
	for (;it_find != m_arrayClientContext.end();it_find++)
	{
		delete it_find->second;
	}
	m_arrayClientContext.clear();

}


/
//判断指定的socket 是否在维护链表中
bool CIOCPModel::IsSocketInContextList(PER_SOCKET_CONTEXT* pSocketContext)
{
	IceUtil::Mutex::Lock guard(m_ContextListMutext);
	//vector<PER_SOCKET_CONTEXT*>::iterator it_find = m_arrayClientContext.begin();
	//for (;it_find != m_arrayClientContext.end(); it_find++)
	//{
	//	if (*it_find == pSocketContext)
	//	{
	//		return true;
	//	}
	//}

	std::map<SOCKET,PER_SOCKET_CONTEXT*>::iterator it_find = m_arrayClientContext.find(pSocketContext->m_Socket);
	if (it_find != m_arrayClientContext.end())
	{
		return true;
	}

	return false;
}


//====================================================================================
//
//				       其他辅助函数定义
//
//====================================================================================




// 获得本机的IP地址
std::string CIOCPModel::GetLocalIP()
{
	// 获得本机主机名
	char hostname[MAX_PATH] = {0};
	gethostname(hostname,MAX_PATH);                
	struct hostent FAR* lpHostEnt = gethostbyname(hostname);
	if(lpHostEnt == NULL)
	{
		return DEFAULT_IP;
	}

	// 取得IP地址列表中的第一个为返回的IP(因为一台主机可能会绑定多个IP)
	LPSTR lpAddr = lpHostEnt->h_addr_list[0];      

	// 将IP地址转化成字符串形式
	struct in_addr inAddr;
	memmove(&inAddr,lpAddr,4);
	m_strIP =  inet_ntoa(inAddr) ;
	

	return m_strIP;
}

///
// 获得本机中处理器的数量
int CIOCPModel::_GetNoOfProcessors()
{
	SYSTEM_INFO si;

	GetSystemInfo(&si);

	return si.dwNumberOfProcessors;
}


/
// 判断客户端Socket是否已经断开,否则在一个无效的Socket上投递WSARecv操作会出现异常
// 使用的方法是尝试向这个socket发送数据,判断这个socket调用的返回值
// 因为如果客户端网络异常断开(例如客户端崩溃或者拔掉网线等)的时候,服务器端是无法收到客户端断开的通知的

bool CIOCPModel::_IsSocketAlive(SOCKET s)
{
	int nByteSent=send(s,"",0,0);
	if (-1 == nByteSent) return false;
	return true;
}

///
// 显示并处理完成端口上的错误
bool CIOCPModel::HandleError( PER_SOCKET_CONTEXT *pContext,const DWORD& dwErr )
{
	// 如果是超时了,就再继续等吧  
	if(WAIT_TIMEOUT == dwErr)  
	{  	
		// 确认客户端是否还活着...
		if( !_IsSocketAlive( pContext->m_Socket) )
		{

			std::string sip = inet_ntoa(pContext->m_ClientAddr.sin_addr);
			int nPort = pContext->m_ClientAddr.sin_port;
			OS_LOG_INFO("客户端异常退出1 IP:"<< sip.c_str()<<" Port:"<< nPort);
			this->_RemoveContext( pContext );
			return true;
		}
		else
		{
			return true;
		}
	}  
	// 可能是客户端异常退出了
	else if( ERROR_NETNAME_DELETED==dwErr )
	{
		std::string sip = inet_ntoa(pContext->m_ClientAddr.sin_addr);
		int nPort = pContext->m_ClientAddr.sin_port;
		OS_LOG_INFO("客户端异常退出2 IP:"<< sip.c_str()<<" Port:"<< nPort);

		this->_RemoveContext( pContext );
		return true;
	}
	else
	{
		if (dwErr == 995)
		{
			OS_LOG_INFO("遗留堵塞任务,该socketcontext 因超时已被删除:"<<dwErr);
		} 
		else
		{
			OS_LOG_INFO("other error!:"<<dwErr);
		}
		return false;
	}
}


bool CIOCPModel::_PostSend(PER_IO_CONTEXT* pIoContext)
{
	//send
	WSABUF *p_wsbuf = &pIoContext->m_wsaBuf;
	OVERLAPPED* p_ol = &pIoContext->m_Overlapped;
	int nBufferlen = pIoContext->m_wsaBuf.len;

	DWORD dwBytes,dwFlags = 0;
	int nBytesSend = WSASend(pIoContext->m_sockAccept,p_wsbuf,1,&dwBytes,dwFlags,p_ol,NULL);

	if ((SOCKET_ERROR == nBytesSend) && (WSA_IO_PENDING != WSAGetLastError()))
	{
		//投递Send 失败
		int nRet = WSAGetLastError();
		OS_LOG_INFO("PostSend failed,nRet:"<<nRet);

		return false;
	}

	return true;
}


bool CIOCPModel::_DoSend(PER_SOCKET_CONTEXT* pSocketContext,PER_IO_CONTEXT* pIoContext)
{
	if (pSocketContext != NULL && pIoContext != NULL && IsSocketInContextList(pSocketContext))
	{
		pSocketContext->RemoveContext(pIoContext);
		return true;
	}else
	{
		OS_LOG_INFO("客户端已经断开连接:");
	}

	return false;
} 

/*
int CIOCPModel::SendPacket(unsigned int ip, unsigned short port, CCDAPacket& oPacket)
{
	EnterCriticalSection(&m_csContextList);
	int nRet = 0;
	vector<PER_SOCKET_CONTEXT*>::iterator it_find = m_arrayClientContext.begin();
	for (;it_find != m_arrayClientContext.end();it_find++)
	{
		if ((*it_find)->m_ClientAddr.sin_addr.s_addr == ip && (*it_find)->m_ClientAddr.sin_port == port)
		{
			PER_IO_CONTEXT* pIoContext = (*it_find)->GetNewIoContext(); 

			pIoContext->m_sockAccept = (*it_find)->m_Socket;
			pIoContext->m_OpType = SEND_POSTED;
			oPacket.EncodePacket(pIoContext->m_szBuffer,oPacket.len());

			pIoContext->m_wsaBuf.buf = pIoContext->m_szBuffer;
			pIoContext->m_wsaBuf.len = oPacket.len();
			memset(&(pIoContext->m_Overlapped), 0, sizeof( OVERLAPPED ) );

			nRet = _PostSend(pIoContext);
			break;
		}
	}
	LeaveCriticalSection(&m_csContextList);
	return nRet;
}
*/


int CIOCPModel::SendPacket(unsigned int ip, unsigned short port, char* pBuff ,int nBufflen)
{
	IceUtil::Mutex::Lock guard(m_ContextListMutext);

	int nRet = 0;
	//vector<PER_SOCKET_CONTEXT*>::iterator it_find = m_arrayClientContext.begin();
	//for (;it_find != m_arrayClientContext.end();it_find++)
	//{
	std::map<SOCKET,PER_SOCKET_CONTEXT*>::iterator it_find = m_arrayClientContext.begin();
	for (;it_find != m_arrayClientContext.end();it_find++)
	{
		if (it_find->second->m_ClientAddr.sin_addr.s_addr == ip && it_find->second->m_ClientAddr.sin_port == port)
		{
			PER_IO_CONTEXT* pIoContext = it_find->second->GetNewIoContext(); 

			pIoContext->m_sockAccept = it_find->second->m_Socket;
			pIoContext->m_OpType = SEND_POSTED;
//			oPacket.EncodePacket(pIoContext->m_szBuffer,oPacket.len());
			memcpy(pIoContext->m_szBuffer,pBuff,nBufflen);

			pIoContext->m_wsaBuf.buf = pIoContext->m_szBuffer;
//			pIoContext->m_wsaBuf.len = oPacket.len();
			pIoContext->m_wsaBuf.len = nBufflen;

			memset(&(pIoContext->m_Overlapped), 0, sizeof( OVERLAPPED ) );

			nRet = _PostSend(pIoContext);
			delete []pBuff;

			break;
		}
	}

	return nRet;
}





问题是 :在客户端失去连接后 感觉有内存泄漏


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值