Linux网络编程IO复用

本文详细介绍了多路IO复用技术,包括select、poll和epoll的使用方法和优缺点。select用于监听多个文件描述符,但存在监听上限限制;poll拓展了监听上限,但不跨平台;epoll则通过红黑树优化了性能,提供高效且灵活的监听机制。通过示例代码展示了如何利用这些技术进行IO操作,并提到了突破文件描述符限制的方法。
摘要由CSDN通过智能技术生成

多路IO转换:

I/O多路复用使得程序能同时监听多个文件描述符能够提高程序的性能

原理:借助内核,select来监听,客户端连接、数据通信事件。

select原型:

int select (int maxfd + 1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval * timeout);

参数一:最大的文件描述符加1。
参数二:用于检查可读性,
参数三:用于检查可写性,
参数四:用于检查带外数据,
参数五:一个指向timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待。t
select:

 
void FD_ZERO(fd_set*set);            ---清空一个文件描述符集合。
            fd_set rset;
            FD_ZERO(&rset);
void FD_SET(int fd, fd_set *set);        ---将一个文件描述符从监听集合 移除。
            FD_CLR(4, &rset);
int FD_ISSET(int fd,fd_set *set);        ---判断一个文件描述符是否在监听集合中。
            返回值:在:1;不在:0FD_ISSET(4, &rset);

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds:监听的所有文件描述符中,最大文件描述符+1
readfds:读文件描述符监听集合。    传入、传出参数
writefds:写文件描述符监听集合。    传入、传出参数    NULL
exceptfds:异常 文件描述符监听集合   传入、传出参数   NULL
timeout:    > 0:    设置监听超时时长。
                    NULL:阻塞监听
                    0:        非阻塞监听,轮询
返回值:
                    >0:所有监听集合(3个)中,满足对应事件的总数。
                    0:    没有满足监听条件的文件描述符
                    -1:    errno

思路分析:

lfd=socket();                        创建套接字
bind();                                  绑定地址结构
listen();                                 设置监听上限
fd_set rset, allset;                创建r监听集合
FD_ZERO(&allset);                将r监听集合清空
FD_SET(lfd,&allset);                将lfd添加至读集合中。
while1{
    rset=allset;                保存监听集合
    ret=select(lfd+1,&rset, NULL,NULL,NULL);        监听文件描述符集合对应事件。
    if(ret>0{                                     有监听的描述符满足对应事件
        ifFD_ISSET(lfd,&rset){                   //1在。0不在
            cfd=accept();                            建立连接,返回用于通信的文件描述符
            maxfd=cfd;
            FD_SET(cfd,&allset);                     添加到监听通信描述符集合中。
            }
            for(i=lfd+1;i<=最大文件描述符;i++){
                FD_ISSET(i,&rset)                    有read、write事件
                read()--write();
            }
    
        }
}

select优缺点:

缺点:监听上限受文件描述符限制。最大 1024

检测满足条件的fd,自己添加业务逻辑提高小。提高了编码难度。

优点:跨平台。win、Linux、macOS、Unix、mips

poll

poll:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
     fds:监听的文件描述符【数组】
        struct pollfd{
            int fd:              待监听的文件描述符
            short events:        待监听的文件描述符对应的监听事件
                                  取值:POLLIN、POLLOUT、POLLERR
            short revnets:        传入时,给0。如果满足对应事件的话,返回 非0---》POLLIN、POLLOUT、POLLERR
         }
    nfds:监听数组的,实际有效监听个数。
    timeout:> 0:超时时长。 单位:毫秒。
                -1:阻塞等待
                0:不阻塞
    返回值:返回满足对应监听事件的文件描述符 总个数。

优点:
    自带数组结构。将监听事件集合 和返回事件集合  分离。
    拓展  监听上限。    超出1024限制。

缺点:
    不能跨平台。Linux
    无法直接定位满足监听事件的文件描述符, 编码难度较大。

read函数

read函数返回值:
        > 0:实际读到的字节数
        = 0:socket中,表示对端关闭。close()
        -1:如果errno==EINTR     被异常终端。需要重启。
    如果errno==EAGIN或EWOULDBLOCK以非阻塞方式读数据,但是没有数据。需要,再次读。
    如果errno==ECONNRESET    说明连接被  重置。需要close( ),移除监听队列。
    错误。

突破1024文件描述符限制:
        cat /proc/sys/fs/file-max  -->当前计算机能打开的最大文件个数。受硬件影响。
        ulimit -a                     ——>当前用户下的进程,默认打开文件描述符个数。缺省为1024
        修改:
                打开sudo vi /etc/security/limits.conf,写入:
                * soft nofile 65536             ——>设置默认值,可以直接借助命令修改。【注销用户,使其生效】
                * hard nofile 100000           ——>命令修改上限。

epoll

epoll:

int epoll_create(int size);                                                  创建一棵监听红黑树
     size:创建的红黑树的监听节点数量。(仅供内核参考。)
      返回值:指向新创建的红黑树的根节点的fd。
             失败:-1 errno

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);        操作监听红黑树
     epfd:epoll_create 函数的返回值。    epfd
      op:对该监听红黑树所做的操作。
               EPOLL_CTL_ADD 添加fd到 监听红黑树
               EPOLL_CTL_MOD 修改fd在监听红黑树上的监听事件。
               EPOLL_CTL_DEL    将一个fd从监听红黑树上摘下(取消监听)
           fd:
               待监听的fd
           event:本质 struct epoll_event 结构体  地址
                成员 events:
                   EPOLLIN/EPOLLOUT/EPOLLERR
                 成员 data:联合体:
                    int fd;    对应监听事件的fd
                    void *ptr;
                    uint32_t u32;
                    uint64_t u64;
                返回值:成功 0;失败:-1 errno

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);    阻塞监听
        epfd:epoll_create 函数的返回值。  epfd
        events:传出参数,【数组】,满足监听条件的哪些fd结构体。
        maxevents:数组 元素的总个数。    1024
                struct epoll_event evnets[1024];
        timeout:
                -1:阻塞
                0: 不阻塞
                >0:超出时间(毫秒)
        返回值:
                >0:满足监听的总个数。    可以用作循环上限。
                 0:没有fd满足监听事件
                -1:失败。 errno

epoll实现多路IO转接思路



lfd=socket();            监听连接事件lfd
bind();
listen();
int epfd=epoll_create(1024);                epfd,监听红黑树的树根。
struct epoll_event    tep, ep[1024];        tep,用来设置单个fd属性,ep是epoll_wait()传出的满足监听事件的数组。
tep.events=EPOLLIN;
tep.data.fd=lfd
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&tep);        将lfd添加到监听红黑树上。

while1{
   ret=epoll_wait(epfd,ep,1024-1);                        实施监听
   for(i=0;i<ret;i++){
        if(ep[i].data.fd==lfd){                        //lfd满足读事件,有新的客户端发起连接请求
            cfd=Accept();                                初始化    cfd的监听属性
            tep.data.fd=cfd;
            epoll_ctl(epfd,EPOLL_CTL_ADD,cfd, &tep);
        }else{                                          cfd们满足读事件,有客户端写数据来。
            n=read(ep[i].data.fd,buf,sizeof(buf));
            if(n==0){
                close(ep[i].data.fd);
                epoll_ctl(epfd,EPOLL_CTL_DEL,ep[i].data.fd,NULL);    //将关闭的cfd,从监听树上摘下。
            }else if(n>0{--大
                write(ep[i].data.fd,buf,n)
            }
         }
     }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑马金牌编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值