socketEventSelect.h
/********************************************************************
创建时间: 2013/04/11
文件名: socketEventSelect.h
描述:
事件选择I/O模型
作者: fengsh
QQ : 19985430
电子邮件: fengsh998@163.com
Blog : http://blog.youkuaiyun.com/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;
}