一.通过三种方法实现IO多路复用,核心框架
1.select(监测个数,读事件地址,写事件地址,异常事件地址,超时检测地址)
//1.创建表
fd_set readfds,tempfds;
//2.清空表
FD_ZERO readfds;
//3.将关心的描述符写入表(根据描述符的数字写入对应下标)
FD_SET(0,&readfds);
FD_SET(sockfd,&readfds);
//4.定义变量存最大下标
int maxfd=sockfd;
//5.判断是哪个描述符产生了事件
while(1)
{
tempfds=readfds;//表还原
//调用select函数
int set=select(maxfd+1,&tempfds,NULL,NULL,NULL);//第二个参数是结构体首地址
//判断是哪个描述符产生了事件
if(FD_ISSET(0,&tempfds))
{
fgets(buf,sizeof(buf),stdin);
printf("key:%s\n",buf);
}
if(FD_ISSET(sockfd,&tempfds))
{
int acceptfd=accept(sockfd,(struct sockaddr *)&caddr,&len);
//将通过链接产生的通信描述符写入表中
FD_SET(acceptfd,&readfds);
if(maxfd < acceptfd)
maxfd=acceptfd;
}
for(int i=4;i<=maxfd;i++)
{
//判断通信描述符此时是否产生事件
if(FD_ISSET(i,&tempfds));
int ret=recv(i,buf,sizeof(buf),0);
if(ret < 0)
{
perror("recv err.\n");
return -1;
}
else if(ret == 0)
{
printf("%d:client exit\n",i);//谁退了
close(i);
FD_CLR(i,&readfds);
if(maxfd == i)
maxfd--;
}
else
{
printf("%d:%s\n",i,buf);//接受的谁的信号
}
}
}
close(sockfd);
return 0;
2.poll(数组,个数,超时检测)
//表(顺序表)
struct pollfd fds[100]={};
//写入表
fds[0].fd=0;
fds[0].events=POLLIN;//读事件系列
fds[1].fd=sockfd;
fds[1].events=POLLIN;
//定义变量存放最后一个描述符所在位置下标
int last=1;
//循环遍历是否发生事件
while(1)
{
//调用poll函数
poll(fds,last+1,-1);
for(int i=0;i<=last;i++)
//先判断所有描述符是否有发生事件的
{
if(fds[i].revents==POLLIN)
{
//再判断是谁发生了事件,再做出对应的逻辑处理
if(fds[i].fd==0)
{
fgets(buf,sizeof(buf),stdin);
printf("key:%s\n",buf);
}
else if(fds[i].fd==sockfd)
{
int acceptfd=accept(sockfd,(struct sockaddr *)&caddr,&len);
last++;
fds[last].fd=acceptfd;
fds[last].events=POLLIN;
}
else
{
int ret=recv(fds[i].fd,buf,sizeof(buf),0);
if(ret < 0)
{
perror("recv err.");
return -1;
}
else if(ret == 0)
{
printf("%d:client exit\n",fds[i].fd);
close(fds[i].fd);
fds[i]=fds[last];
last--;
i--;//得在for循环里重新判断一下i所在位置
}
else
{
printf("%d:%s\n",fds[i].fd,buf);
}
}
}
}
close(sockfd);
return 0;
3. epoll_ctl(树根,操作,描述符,事件地址)
//1.建树
int epfd=epoll_create(1);
//2.构建结构体暂时存放写入关心的文件描述符
struct epoll_event event;
//3.填充结构体
event.data.fd=0;
event.events=EPOLLIN|EPOLLET;
event.data.fd=sockfd;
event.events=EPOLLIN|EPOLLET;
//4.上树
epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);//第四个参数是结构体指针,传结构体首地址
epoll_ctl(epfd,EPOLL_CTL_ADD,sockffd,&event);
//5.构建结构体暂时保存发生事件的event集合
struct epoll_event events[20];
//6.判断是哪些描述符发生了事件
while(1)
{
//调用函数,列出发生事件的描述符,对描述符做对应的逻辑处理
int wt=epoll_wait(epfd,events,20,-1);//第二个参数也是地址,数组的首地址是数组名
for(int i=0;i<wt;i++)
{
if(events[i].data.fd==0)
{
fgets(buf,sizeof(buf),stdin);
printf("key:%s\n",buf);
}
else if(events[i].data.fd==sockfd)
{
int acceptfd=accept(sockfd,(struct sockaddr *)&caddr,&len);
//上树
event.data.fd=acceptfd;
event.events=EPOLLIN|EPOLLET;
epoll_ctl(efds,EPOLL_CTL_ADD,acceptfd,&event);
}
else
{
int recvtype=recv(events[i].data.fd,buf,sizeof(buf),0);//0:阻塞
if(recvtype < 0)
{
perror("recv err.");
return -1;
}
else if(recvtype == 0)
{
printf("%d client exit\n",events[i].data.fd);//谁退了
close(events[i].data.fd);
epoll_ctl(epfd,EPOLL_CTL_DEL,events[i].data.fd,NULL);//下树不用传事件
}
else
{
printf("%d:%s\n",events[i].data.fd,buf);//打印谁
}
}
}
}
close(sockfd);
return 0;