将服务器模型升级为select模型
新增
新增内容
- 加入select模型
- 新增数组,存储所有客户端socket
- 将监听客户端代码移动到select模型中。
新增代码
vectorSOCKET gClients;
int Process(SOCKET _cSock)
{
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);
}
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:
}
}
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)
{
int len = Process(fdRead.fd_array[ii]);
}
}
部分代码详解
fd_set
fd_set其实是一个数组的宏定义,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪个句柄可读。
FD_SET(int fd, fd_set *fdset);
FD_CLR(int fd, fd_set *fdset);
FD_ISSET(int fd, fd_set *fdset);
FD_ZERO(fd_set *fdset);
select代码
原型:
int select(
int nfds,
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);