sokcetServerBySelect.h
/******************************************************************** 创建时间: 2013/04/05 文件名: sokcetServerBySelect.h 描述: select I/O 主要采用FD_SET,FD_CLR,FD_ISSET,FD_ZERO 宏的运用 来实现I/O 模型 作者: fengsh QQ : 19985430 电子邮件: [email protected] Blog : http://blog.csdn.net/fengsh998 @CopyRight fengsh *********************************************************************/ #pragma once #include "socketBase.h" #define GET_ARRAY_LEN(array,len) {len = (sizeof(array) / sizeof(array[0]));} typedef enum SelectResult {srNone,srError,srHasRead,srHasWrite}; class CSokcetServerBySelect : public CSocketBase { private: void doWaitClient(); void doSelect(); void waitThreadExit(); void closeClientSockets(); public: CSokcetServerBySelect(void); ~CSokcetServerBySelect(void); int startServer(); int stopServer(); bool sendtext(const std::string& content); //检查是否有数据可读/写 SelectResult listenSelectResult(); void setStop(bool flag); bool getStop(); SOCKET m_clientArr[FD_SETSIZE]; int m_icount ; private: void *waitid; void *selectid; bool isStop; };
sokcetServerBySelect.cpp
#include "sokcetServerBySelect.h" #include "socketThread.h" #include "SocketConst.h" //等待连接线程 static void* wait_client_thread(void *param); //负责进行查询线程。 static void* select_thread(void* param); CSokcetServerBySelect::CSokcetServerBySelect(void):waitid(0),selectid(0),m_icount(0),isStop(true) { memset(&m_clientArr,0,64*4);//每个数组的指针长度为4,共有64个空间,占有64*4 } CSokcetServerBySelect::~CSokcetServerBySelect(void) { closeClientSockets(); //listen socket free at parent class.eg:closesocket(listensocket);WSACleanup(); } int CSokcetServerBySelect::startServer() { if (initSocket()) { doWaitClient(); setStop(false); doSelect(); } return -1; } int CSokcetServerBySelect::stopServer() { setStop(true); closeClientSockets(); closeListenSocket(); waitThreadExit(); return -1; } bool CSokcetServerBySelect::sendtext( const std::string& content ) { //it is demo . don't write the code. for (int i = 0;i < m_icount;i++ ) { sendData(m_clientArr[i],content); } return true; } void CSokcetServerBySelect::waitThreadExit() { socket_thread_join(&waitid); socket_thread_join(&selectid); waitid = 0; selectid = 0; } void CSokcetServerBySelect::doWaitClient() { socket_thread_create(&waitid,wait_client_thread,(void *)this); } void CSokcetServerBySelect::doSelect() { socket_thread_create(&selectid,select_thread,(void *)this); } void CSokcetServerBySelect::closeClientSockets() { //int len ; //GET_ARRAY_LEN(m_clientArr,len); for (int i = 0 ;i < m_icount; i++) { if (m_clientArr[i] != INVALID_SOCKET) { closesocket(m_clientArr[i]); } } m_icount = 0; } SelectResult CSokcetServerBySelect::listenSelectResult() { return srNone; } bool CSokcetServerBySelect::getStop() { return isStop; } void CSokcetServerBySelect::setStop( bool flag ) { isStop = flag; } static void* wait_client_thread(void *param) { CSokcetServerBySelect *slt = (CSokcetServerBySelect*)param; DISPATCHPARAM dp; memset(&dp,0,sizeof(DISPATCHPARAM)); SOCKET socketClient; while(TRUE) { SOCKADDR_IN addrClient; int addrClientSize=sizeof(SOCKADDR_IN); socketClient=accept(slt->m_listenSocket,(struct sockaddr*)&addrClient,&addrClientSize); if (socketClient==INVALID_SOCKET) { socketClient = NULL; if (slt->checkSocketError(WSAGetLastError())) { break; } continue; } else { //to do limit FD_SETSIZE,rang to out szie. slt->m_clientArr[slt->m_icount++] = socketClient; strcpy(dp.info.ip,inet_ntoa(addrClient.sin_addr)); dp.info.port = addrClient.sin_port; slt->dispatchcallback(cbHasConnect,&dp); } } return 0; } static void* select_thread(void* param) { CSokcetServerBySelect *slt = (CSokcetServerBySelect*)param; DISPATCHPARAM dp; memset(&dp,0,sizeof(DISPATCHPARAM)); fd_set fdread,fdwrite; int ret; struct timeval tv={1,0};//超时时间设为1秒。如果tv = NULL 则一直等待 //tv.tv_sec = xxx/1000; //秒 //tv.tv_usec = xxxx%1000; //毫秒 char szMessage[BUFFERMAX]; while(true) { if (slt->getStop()) { break; } FD_ZERO(&fdread);//将fdread初始化空集 0000 0000 FD_ZERO(&fdwrite); for (int i = 0;i < slt->m_icount; i++) { FD_SET(slt->m_clientArr[i],&fdread); FD_SET(slt->m_clientArr[i],&fdwrite); } /* ERROR CODE; WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。 WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。 WSAEINVAL:超时时间值非法。 WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。 WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。 WSAENOTSOCK:描述字集合中包含有非套接口的元素。 */ ret=select(0,&fdread,&fdwrite,NULL,NULL/*&tv*/);//WINDOWS下第一个参数可以忽略,LINUX下,不能大于1024 if (ret != 0) //-1 or 1 { for (int i = 0;i < slt->m_icount; i++) { if (FD_ISSET(slt->m_clientArr[i],&fdread)) { ret = recv(slt->m_clientArr[i],szMessage,BUFFERMAX,0); if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)) { closesocket(slt->m_clientArr[i]); slt->m_clientArr[i--]=slt->m_clientArr[--slt->m_icount]; dp.errcode = WSAGetLastError(); slt->dispatchcallback(cbDisconnect,&dp); } else { strcpy(dp.msg,szMessage); //回调到界面 slt->dispatchcallback(cbCommunication,&dp); //回调到接收完成 slt->dispatchcallback(cbRecviced,NULL); } } if (FD_ISSET(slt->m_clientArr[i],&fdwrite))// 有可写变化 { } } } } return 0; }