模型设计与实践---(三)IO模型Select IO

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



你可能感兴趣的:(模型设计与实践---(三)IO模型Select IO)