新增
新增内容
- 加入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);