模型设计与实践---(五)事件选择(EventSelect iO)

时间:2021-08-20 19:16:01

socketEventSelect.h

/********************************************************************
	创建时间:	2013/04/11

	文件名: 	socketEventSelect.h
	描述:
			事件选择I/O模型
	作者:		fengsh
	QQ  :		19985430
	电子邮件:	fengsh998@163.com
	Blog :      http://blog.csdn.net/fengsh998
	@CopyRight  fengsh

*********************************************************************/
#pragma once
#include "socketbase.h"
#include "arrayObj_TPL.h"
#include "socketMutex.h"

class CSocketArrays;

typedef struct tagEventSelect
{
	int total;
	SOCKET sockarr[MAXIMUM_WAIT_OBJECTS];
	WSAEVENT eventHandles[WSA_MAXIMUM_WAIT_EVENTS];
}EVENTSELECT,*PEVENTSELECT;

class CSocketEventSelect :
	public CSocketBase
{
public:
	CSocketEventSelect(void);
	~CSocketEventSelect(void);

	int startServer() ;
	int stopServer() ;
	bool sendtext(const std::string& content) ;

	void add(SOCKET client);
	SOCKET socketitem(int idx);
	WSAEVENT eventhandleitem(int idx);
	void Cleanup(int index);
	void converHandle();
	void clearEventHandle();
	bool terminated();

	void clearall();

	PEVENTSELECT m_Arr;
private:
	void *cid;
	void *eventid;
	CSocketArrays *m_sArr;
	CArrayObj<WSAEVENT> *m_eventArr;
	CSocketMutex m_smlock;
	bool m_isOver;
};


socketEventSelect.cpp

#include "socketEventSelect.h"
#include "socketArrays.h"
#include "socketThread.h"
//对于模板类最好放在一个.h中进行声明并定义。如果拆分后,需要引入.cpp否则出现链接错误。在VS2008中有此现象
#include "arrayObj_TPL.cpp"

//客户端连接等待线程
static void* wait_client_thread(void* param);
//网络事件监听线程
static void* net_event_thread(void* param);

CSocketEventSelect::CSocketEventSelect(void):eventid(0),cid(0),m_isOver(false)
{
	m_sArr = new CSocketArrays();
	m_eventArr = new CArrayObj<WSAEVENT>;
	m_Arr = (EVENTSELECT*)malloc(sizeof(EVENTSELECT));
	memset(m_Arr,0,sizeof(EVENTSELECT));
}

CSocketEventSelect::~CSocketEventSelect(void)
{
	m_scallback = NULL;
	if (m_sArr)
	{
		delete m_sArr;
	}
	
	if (m_eventArr)
	{
		clearEventHandle();
		delete m_eventArr;
	}
	
	delete[] m_Arr;// free error?

	m_isOver = true;
	closesocket(m_listenSocket);
	socket_thread_join(&eventid);

	cid = 0;
	eventid = 0;

}

int CSocketEventSelect::startServer()
{
	if (initSocket())
	{
		m_isOver = false;
		socket_thread_create(&cid,wait_client_thread,(void*)this);

		socket_thread_create(&eventid,net_event_thread,(void*)this);
		return 1;
	}
	return -1;
}

int CSocketEventSelect::stopServer()
{
	dispatchcallback(cbServerClose,NULL);
	m_isOver = true;

	closesocket(m_listenSocket);
	 
	return 1;
}

bool CSocketEventSelect::sendtext( const std::string& content )
{
	for (int i = 0;i < m_sArr->count(); i++)
	{
		sendData(m_sArr->getSocketByIndex(i),content);
	}
	return true;
}

void CSocketEventSelect::add( SOCKET client )
{
	CAutoLock atlock(&m_smlock);
	//创建事件。
	HANDLE hd = WSACreateEvent();
	if (WSA_INVALID_EVENT == hd)
	{
		return;
	}

	m_sArr->addSocket(client);
		
	m_eventArr->addObject(hd);

	WSAEventSelect(client,m_eventArr->getObjectByIndex(m_eventArr->count()-1),
		FD_ACCEPT |FD_READ |FD_WRITE |FD_CONNECT |FD_CLOSE);
	
}

void CSocketEventSelect::converHandle()
{
	CAutoLock atlock(&m_smlock);
	memset(m_Arr,0,sizeof(EVENTSELECT));

	m_Arr->total = m_sArr->count();

	for (int i = 0; i < m_Arr->total; i++)
	{
		m_Arr->sockarr[i] = m_sArr->getSocketByIndex(i);
		m_Arr->eventHandles[i] = m_eventArr->getObjectByIndex(i);
	}
}

SOCKET CSocketEventSelect::socketitem( int idx )
{
	CAutoLock atlock(&m_smlock);
	return m_sArr->getSocketByIndex(idx);
}

WSAEVENT CSocketEventSelect::eventhandleitem( int idx )
{
	CAutoLock atlock(&m_smlock);
	return m_eventArr->getObjectByIndex(idx);
}

void CSocketEventSelect::Cleanup(int index)
{
	CAutoLock atlock(&m_smlock);
	closesocket(m_sArr->getSocketByIndex(index));

	WSACloseEvent(m_eventArr->getObjectByIndex(index));

	m_sArr->deleteByIndex(index);
	m_eventArr->deleteByIndex(index);
}

bool CSocketEventSelect::terminated()
{
	return m_isOver;
}

void CSocketEventSelect::clearEventHandle()
{
	CAutoLock atlock(&m_smlock);
	int icount = m_eventArr->count();
	for (int i = 0; i < icount; i++)
	{
		WSACloseEvent(m_eventArr->getObjectByIndex(i));
	}
}

void CSocketEventSelect::clearall()
{
	m_sArr->clear();
	clearEventHandle();
	m_eventArr->clear();
}

//阻塞等待客户端的连接。
static void* wait_client_thread(void *param)
{
	CSocketEventSelect *es = (CSocketEventSelect*)param;

	DISPATCHPARAM dp;
	memset(&dp,0,sizeof(DISPATCHPARAM));
	SOCKET socketClient;

	while(TRUE)
	{

		SOCKADDR_IN addrClient;
		int addrClientSize=sizeof(SOCKADDR_IN);
		socketClient=accept(es->m_listenSocket,(struct sockaddr*)&addrClient,&addrClientSize);
		if (socketClient==INVALID_SOCKET)
		{
			socketClient = NULL;
			if (es->checkSocketError(WSAGetLastError()))
			{
				break;
			}
			continue;
		}
		else
		{
			//to do limit FD_SETSIZE,rang to out szie.
			es->add(socketClient);

			strcpy(dp.info.ip,inet_ntoa(addrClient.sin_addr));
			dp.info.port = addrClient.sin_port;
			es->dispatchcallback(cbHasConnect,&dp);
		}
	}

	return 0;
}

static void* net_event_thread(void* param)
{
	
	CSocketEventSelect *es = (CSocketEventSelect*)param;

	DISPATCHPARAM dp;
	memset(&dp,0,sizeof(DISPATCHPARAM));

	int nRet,index;
	WSANETWORKEVENTS NetWorkEvents;

	char bufmsg[BUFFERMAX]={0};

	while(TRUE)
	{
		if (es->terminated())
		{
			break;
		}
		//本想直接用数组的,但写了个模板,就用来试下效果。
		//此方法是将vector中的对象初始化到数组中。这效率上有开销的。不建议这样搞
		es->converHandle();

		nRet = WSAWaitForMultipleEvents(es->m_Arr->total,es->m_Arr->eventHandles,FALSE,1000,FALSE);

		
		if (nRet == WSA_WAIT_FAILED || nRet == WSA_WAIT_TIMEOUT)
		{
			continue;
		}

		index = nRet - WAIT_OBJECT_0;

		WSAEnumNetworkEvents(es->socketitem(index),es->eventhandleitem(index),&NetWorkEvents);

		if(NetWorkEvents.lNetworkEvents & FD_READ)
		{
			
			nRet = recv(es->socketitem(index),bufmsg,BUFFERMAX,0);

			if (nRet == 0 || (nRet == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
			{
				es->Cleanup(index);
				dp.errcode = WSAGetLastError();
				es->dispatchcallback(cbDisconnect,&dp);
			}
			else
			{
				strcpy(dp.msg,bufmsg);
				//回调到界面
				es->dispatchcallback(cbCommunication,&dp);
				//回调到接收完成
				es->dispatchcallback(cbRecviced,NULL);
			}
		}

		if(NetWorkEvents.lNetworkEvents & FD_CONNECT)
		{
			strcpy(dp.msg,"connect go.");
			es->dispatchcallback(cbRunTrace,&dp);
		}

		if(NetWorkEvents.lNetworkEvents & FD_WRITE)
		{
			strcpy(dp.msg,"write go.");
			es->dispatchcallback(cbRunTrace,&dp);
		}

		if(NetWorkEvents.lNetworkEvents & FD_ACCEPT)
		{
			strcpy(dp.msg,"accept go.");
			es->dispatchcallback(cbRunTrace,&dp);
		}

		if(NetWorkEvents.lNetworkEvents & FD_CLOSE)
		{
			es->Cleanup(index);
			strcpy(dp.msg,"close go.");
			es->dispatchcallback(cbRunTrace,&dp);
		}
		
	}

	es->clearall();

	return 0;
}