多路io

一、select

设置select代理,将lfd交给select,监听socket连接,若有连接,则通知server进行accpet接收,并将cfd也交给select,因此,select有两个监听,一个监听连接(lfd),一个监听传输(cfd)

int select(int nfds, fd_set * readfds, fd_set *writefds,
         fd_set * errorfds, struct timeval * timeout);
//返回值:所有监听集合中,满足对应事件的文件描述符总数
/*
nfds:  所有监听的文件描述符中最大的文件描述符+1
readfds,writefds,exceptfds监听集合,都是位图集合和传入传出参数
timeout:NULL阻塞等待,0非阻塞监听,其他值则等待此时间

*/

//接下来介绍位图的操作
void FD_ZERO(fd_set *fdset);//清空集合
void FD_SET(fd, fd_set *fdset);//添加文件描述符到监听集合
void FD_CLR(fd, fd_set *fdset);//将一个文件描述符从监听集合删除
int  FD_ISSET(fd, fd_set *fdset);//判断文件描述符是否在监听集合中
void FD_COPY(fd_set *fdset_orig, fd_set *fdset_copy);

1.1 select 实现server分析

socket();
bind();
listen();

fd_set rset;
FD_ZERO(&rset);
FD_set(lfd,&rset);
select(lfd+1,&rset)
accept
read

1.2 select实现server

#define port 8100
int main(int argc,char *argv[])
{
    int lfd,cfd,maxfd=0;
    char buf[80];
    struct sockaddr_in client_addr,server_addr;
    socklen_t client_addr_len;
    fd_set rset,allset;
 //创建所需的变量,包含文件描述符,缓冲区,sockaddr,文件描述符集等
    bzero(&server_addr, sizeof(server_addr));
    lfd=socket(AF_INET,SOCK_STREAM,0);
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(port);
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    bind(lfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    listen(lfd,128);
    printf("listen sucess-----\n");
        //进行server的bind,linsten操作
    maxfd=lfd;
    FD_ZERO(&allset);
    FD_SET(lfd,&allset);
    //对读集合进行初始化

    while(1){
        printf("step while-----\n");
        rset=allset;//拷贝集合
        int sret=select(maxfd+1,&rset,NULL,NULL,NULL);//进行select
        if(sret<0)perror("select error");
        printf("select sucess-----\n");

        if(FD_ISSET(lfd,&rset)){//如果有客户端请求,则开始accept
            socklen_t len=sizeof(client_addr);
            cfd=accept(lfd,(struct sockaddr*)&client_addr,&len);
            printf("accept sucess-----\n");
            FD_SET(cfd,&allset);
            if(maxfd


1.3 select 特点

缺点

  • 监听上限受文件描述符限制(最大1024)
  • 只能自己添加逻辑,去提高轮询效率(比如只有3号和1024号,需要轮询1000多次)
    优点
  • 跨平台

2.1.4 select优化

为了提高轮询效率,增加一个数组,用来记录有操作的文件描述符,以免进行1024次轮询。

for(int i=0;i<1024;i++)client[i]=-1;
int i;
    while(1){
        printf("第%d次循环\n",count++);
        rset=allset;
         sret=select(maxfd+1,&rset,NULL,NULL,NULL);
        if(sret<0)perror("select error");
        printf("select sucess-----\n");
        if(FD_ISSET(lfd,&rset)){
            socklen_t len=sizeof(client_addr);
            cfd=accept(lfd,(struct sockaddr*)&client_addr,&len);
            for (i = 0; i < 1024; i++) 
                if (client[i] < 0) {
                    client[i] = cfd; /* 保存accept返回的文件描述符到client[]里 */
                    break;
                }

            if(i==1024)printf("too many clients");
            if(i>maxi)maxi=i;
            FD_SET(cfd,&allset);
            if(maxfd

二、 poll

#include 
 int poll(struct pollfd* fds, nfds_t nfds, int timeout);

2.1 poll特点

  • 优点
    自带数组,避免轮询过多
    可以讲监听集合和返回集合分离
    没有1024大小的限制
  • 缺点
    还是需要轮询
    不能跨平台

三、突破1024文件描述符限制

cat /proc/sys/fs/file-max 查看当前系统最大描述符上限
sudo vim /etc/security/limits.conf //修改上限

  • soft nofile 3000
  • hard nofile 50000

四、 epoll

本质是红黑树

#include
int epoll_create(int size)
//size 监听节点数量且仅供参考,可动态扩容
//return:返回红黑树根结点的epfd

int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);//操作监听
/*
op:    EPOLL_CTL_ADD:添加fd监听到红黑树
       EPOLL_CTL_MOD   修改epoll红黑树上的监听事件
       EPOLL_CTL_DEL  删除监听
  
event:   
struct epoll_event{
        uint32_t   events;
        epoll_data_t data;
    
};
/*
events:  EPOLLIN/EPOLLOUT/EPOLLERR/EPOLLET
data:    int fd
         void *ptr  
*/



int epoll_wait(int epfd,struct epoll_events*events,int maxevents,int timeout);
/*
events:  传出参数,event[数组],将满足的event传出
maxevents:数组元素总个数,而非数组大小
timeout:-1阻塞,0非阻塞,>0超时时间
return:>0满足监听总个数,可作为循环上限
*/

4.1 epoll实现server思路

socket->bind->listen->epoll_create->epoll_ctl(lfd)
while()->epoll_wait()->for(ep[i])->if(ep[i].data.fd==lfd)accept->else write

4.2 epoll的事件模型

4.2.1 ET(edge trigger)边缘触发

event.events =EPOLLIN | EPOLLET
缓冲区剩余数据不会导致wait返回
只支持非阻塞模式
设置非阻塞状态:

int flag=fcnl(cfd,F_GETFL);
flag|=O_NOBLOCK;
fcnl(cfd,F_SETFL,flag);

4.2.2 LT(level trigger)水平触发 (默认)

缓冲区剩余数据会导致wait返回

4.3 epoll反应堆

你可能感兴趣的:(多路io)