c++千万级别高并发网络编程(五)

将服务器模型升级为select模型

新增 新增内容
  • 加入select模型
  • 新增数组,存储所有客户端socket
  • 将监听客户端代码移动到select模型中。
新增代码
vectorSOCKET gClients;//全局变量
int Process(SOCKET _cSock)
{
	//检测是否有socket可读
  	if(FD_ISSET(_sock,fdRead))
  	{
  		FD_CLR(_sock,fdRead);
  		//处理该服务器
  		//移动其他位置的代码
  		sockaddr_in clentAddr = {};//不用赋值了,因为是监听到的,不是给定的
		int nAddrLen = sizeof(sockaddr_in);//给定长度
		SOCKET _cSock = INVAID_SOCKET;
		_cSock = accept(seradd,(sockaddr *) clentAddr,nAddrLen);
		if(_cSock = INVAID_SOCKET)
		{
			cout"获取失败"endl;
			return -1;
		}
		cout"新客户端加入"endl;
		gClients.push_back(_cSock);
  	}
  	//time可以根据需要传入
  	//修正,设置缓冲区
  	char szRecv[4096]={};
  	DataHeader *header = NULL;
  	int nLen = recv(_cSock,szRecv,sizeof(DataHeader),0);
  	header = (DataHeader *)szRecv;
  	if(nLen = 0)
  	{
  	 	cout"客户端已经退出,任务结束"endl;
  	}
  	switch(header.cmd)
  	 {
  	 	case CMD_LOGIN:
  	 	{
  	 		cout"收到的命令为:Login"endl;
  	 		Login login = {};
  	        int nLen = recv(_cSock,(char *) login,sizeof(Login),0);
  	        if(nLen = 0)
  	        {
  	 		    cout"客户端已经退出,任务结束"endl;
  	        }
  	        else
  	        {
  	        	//判断用户名和密码是否正确;
  	        	if(0 == strcmp(login.username,"wushuomin"  0 == strcmp(login.password,"enter")
  	        	{
  	        		LoginResult msgBuf = {1};
  	        		//发送也是先发包头
  	        		send(_cSock,(char *)header,sizeof(DataHeader),0);
  	 	            send(_cSock,(char *)msgBuf,sizeof(msgBuf),0);
  	        	}
  	        	else
  	        	{
  	        		LoginResult msgBuf = {0};
  	 	            send(_cSock,(char *)msgBuf,sizeof(msgBuf),0);
  	        	}
  	        	
  	        }
  	 
  	 	}
  	 	break;
  	 	case CMD_LOGOUT:
  	 	{
  	 		
  	 	}
  	 	break;
  	 	case CMD_GETINFO:
  	 	{
  	 	}
  	 	break;
  	 	default:
  	 	//这里heder的长度为0,返回一个错误类型。
  	 }
  	
}
while(true)
  {
  	//新增
  	fd_set fdRead;
  	fd_set fdWrite;
  	fd_set fdExp;
  	FD_ZERO(fdRead);
  	FD_ZERO(fdWrite);
  	FD_ZERO(fdExp);
  	FD_SET(_sock,fdRead);
  	FD_SET(_sock,fdWrite);
  	FD_SET(_sock,fdWrite);
  	for(int ii = 0; ii  gClients.size();++ii)
  	{
  		FD_SET(gClients[ii],fdRead);
  	}
  	int ret = select(_sock+1,fdRead,fdWrite,fdExp,NULL);
  	if(ret  0)
  	{
  	  	cout"Error"endl;
  	  	break;
  	} 
  	for(int ii = 0; ii  gClients.size();++ii)
  	{
  		//FD_SET(gClients[ii],fdRead);
  		int len = Process(fdRead.fd_array[ii]);
  		//len == -1,将该socket删除
  	}
  	
  }
部分代码详解 fd_set

fd_set其实是一个数组的宏定义,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪个句柄可读

FD_SET(int fd, fd_set *fdset);       //将fd加入set集合
FD_CLR(int fd, fd_set *fdset);       //将fd从set集合中清除
FD_ISSET(int fd, fd_set *fdset);     //检测fd是否在set集合中,不在则返回0
FD_ZERO(fd_set *fdset);              //将set清零使集合中不含任何fd
select代码

原型:

    int select( 
      int nfds, //nfds会被忽略,之所以保留是为了与早期套接字兼容.linux下作用较大。所有描述符范围,是文件描述符最大值+1
      fd_set FAR* readfds, //可读集合
      fd_set FAR* writefds, //可写集合
      fd_set FAR* exceptfds, //异常集合
      const struct timeval FAR* timeout //如果再一段时间内未等到消息,那么就返回。
    ); 
修正为非阻塞 说明

原先select中传入time为NULL时,是阻塞模式,即一直等到有数据可操作时才返回继续执行。否则一致阻塞在那里。
如果是响应客户端程序,那么这个就可以了。
如果主动推送,那么就需要采用非阻塞模式。

修正方法

在select中传入time

timeval t = {0,10};//第一个是秒,第二个是微秒
int ret = select(_sock+1,fdRead,fdWrite,fdExp,t);
最新回复(0)
/jishuINrqB_2Fn2SUVTXtR1MPRRT2DPoav89Ns7PkTjA_2B6dMEE_3D4858731
8 简首页