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

本文介绍了在C++中使用select模型处理高并发网络编程的方法,包括如何新增select模型,使用数组存储客户端socket,并将监听代码整合进select。详细解析了fd_set的数据结构,并讨论了将select从阻塞模式修正为非阻塞模式的必要性和实现方法。

新增

新增内容

  • 加入select模型
  • 新增数组,存储所有客户端socket
  • 将监听客户端代码移动到select模型中。

新增代码

vector<SOCKET> 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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值