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; }