C/S通信之基于select的步骤:
{
int listenfd, connfd, sockfd; //
struct sockaddr_in serv_addr, clie_addr;//存储客户端、服务器的IP和端口
socklen_t clie_addr_len; //用于accept,用于存储客户端的IP和端口
int ret, maxfd, maxi, i, j, nready, nByte;
fd_set rset, allset; //就绪集合、监听集合[监听与就绪分离]
//1.创建本地套接字
listenfd = socket(AF_INET,SOCK_STREAM,0);
//2.地址复用和端口复用[避免服务器退出重连需要等待2tsl时间]
int opt1 = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
//3.服务器绑定IP和端口
bzero(&serv_addr,sizeof(serv_addr)); //清零
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(atoi(SERV_PORT)); //SERV_PORT:服务器指定的端口
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //主机字节序转网络字节序
bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
//4.服务器监听:将listenfd指向的文件对象的RCV/SND缓冲区摧毁,构建半连接和全连接队列,监听listenfd就是监听全连接队列是否有数据[就绪]
listen(sockfd,128);
//-------------------------------------------------------------------
while(1)
{
//监听集合与就绪集合分离,所以每次轮询还得将监听集合的数据拷贝到就序集合中
rset = allset;
//1.监听: nready表示就绪时间发生的次数
nready = select(maxfd+1,&rset,NULL,NULL,NULL);
//2.遍历整个监听集合,判断判断其文件描述符是否存在于rset中
if(FD_ISSET(listenfd,&rset))
{
//2-1表示有新的连接请求:accept返回对应的文件描述符
connfd = accept(listenfd,(struct sockaddr*)&clie_addr,&clie_addr_len);
//将其添加进监听集合
FD_SET(connfd,&allset);
}
else
{
//表示就旧连接上有数据,表明可以进行数据的传输
for(i = 0; i < maxi; ++i)
{
if((sockfd = client[i] < 0))
{
continue;
}
if(FD_ISSET(sockfd,&rset))
{ //2-2旧连接上有数据来了
if((nByte == read(sockfd,buf,sizeof(buf))) == 0)
{
//表明服务端想要断开请求
close(sockfd);
FD_CLR(sockfd,&allset); //将文件描述符从监听集合中删除
client[i] = -1;
continue;
}
else if(nByte > 0)
{
//根据业务逻辑上数据星星处理
write(sockfd,buf,nByte);
write(STDOUT_FILENO,buf,nByte);
}
}
}
}
}
close(listenfd);
}