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

服务器功能添加,客户端升级为select

  • 新增
    • 新增内容
    • 服务器新增代码
    • 客户端新增代码

新增

新增内容

  • 当有新的客户端加入时,服务器通知其他客户端
  • 客户端升级为select模型,以便可以接受服务器的发送通知

服务器新增代码

  1. 在命令中新增一个
enum CMD //里面包含cmd常见的命令
{
    CMD_LOGIN;
    CMD_LOGOUT;
    CMD_GETINFO;
    CMD_NEW_USER_JOIN;//新增
    CMD_RESULT;
};
struct NewUserJoin:public DataHeader 
{
	NewUserJoin()
	{
		dataLength = sizeof(NewUserJoin);
		cmd = CMD_NEW_USER_JOIN;
		soc = 0;
    }
	NewUserJoin(SOCKET s)
	{
		dataLength = sizeof(NewUserJoin);
		cmd = CMD_NEW_USER_JOIN;
		soc = s;
    }
    SOCKET soc;
};
  1. 在process函数中加入当有新客户端加入时,向现有客户端发送通知。
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;
		}
		for(int ii = 0; ii < gClients.size();++ii)
  		{
  			NewUserJoin userJoin(_cSock);
  			send(gClients[ii],(const char *)&userJoin,sizeof(NewUserJoin),0);
  		}
		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;
  	}
}

客户端新增代码

  • 在前面,把数据公共部分全部拉过去。这个部分如怎么定义的enum,struct我们假定都是放在头文件DataHeader.h,具体实现都是放在DataHeader.cpp当中的。
  • 需要注意的是:引入select之后,无法直接使用键盘交互,否则会阻塞在相关环节,需要在后续引入多线程操作。
int main()
{
    WORD ver = MAKEWORD(2,2);//调用API2代创建2.x版本
    WSADATA dat;
    WSAStartup(ver,&dat);//启动
    SOCKET  cliadd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    //bind
    SOCKADDR_IN _sin = {};	
    _sin .sin_family = AF_INET;
//sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序。所以需要转换host to net unsigned.数字可改 
	_sin .sin_port = htons(4567);//和拂去其不同
	_sin.sin_addr_s_un.S_addr = inet_addr("127.0.0.1");//绑定服务器地址
	if (SOCKET_ERROR == connect(cliadd, (sockaddr *) &_sin , sizeof(_sin)))
	{
		std::cout<<"连接失败"<<endl;
	}
	//新增select
	while(true)
	{
		 fd_set fdRead;
    	FD_ZERO(fdRead);
  		//FD_ZERO(fdWrite);
  		//FD_ZERO(fdExp);
  		FD_SET(_sock,&fdRead);
  		//FD_SET(_sock,&fdWrite);
  		//FD_SET(_sock,&fdWrite);
  		int ret = select(_sock+1,&fdRead,NULL,NULL,NULL);
  		if(ret < 0)
  		{
  		  	cout<<"Error"<<endl;
  	  		break;
  		} 
		//输入环节先移除了,到多线程加入才能继续回来
		if(FD_ISSET(_sock,&fdRead))
  		{
  			FD_CLR(_sock,&fdRead);
  			Process(_sock);
  		}
		
		}
   }
   closesocket(cliadd);
   WSAcleanup();//关闭
   return 0;
}


int Process(SOCKET _cSock)
{
  	//修正,设置缓冲区
  	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_RESULT:
  	 	{
  	 		cout<<"收到的回复为:"<<endl;
  	 		recv(XXX)//按照格式继续接受后面实际数据部分。
  	 
  	 	}
  	 	break;
  	 	case CMD_LOGOUT_RESULT:
  	 	{
  	 		
  	 	}
  	 	break;
  	 	case CMD_NEW_USER_JOIN:
  	 	{
  	 		
  	 	}
  	 	break;
  	 	default:
  	 	//这里heder的长度为0,返回一个错误类型。
  	 }
  	
}

你可能感兴趣的:(网络编程,项目)