当前位置:网站首页>【IO复用】poll

【IO复用】poll

2022-08-10 02:57:00 白U

  • select 支持1024 处理3类(读时间,写时间,异常事件)
  • poll 支持大于1024
    处理多类事件

poll系统调用

poll系统调用和select相似,也是指定时间内循环遍历一定数量的文件描述符,来测试是否有就绪者。
#include<poll.h>

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

fds参数是一个pollfd结构类型的数组,(数组越大,容纳的事件更多)

struct pollfd
{
    
int fd ;             //文件描述符
short events;    //注册事件
short revents;    //实际发生的事件,

fd指定文件描述符,
event告诉poll监听的fd上了那些事件,
revents通知应用程序fd上实际发生了那些事件

请添加图片描述
请添加图片描述

  • 文件描述符的就绪条件
  • [ ]socket可读
  1. socket内核接收缓冲区的内存有数据,就可以无阻塞读该socket,并且读操作返回字节数大于0;
  2. socket 关闭连接,则只需要读取关闭信息,即可返回0;(无阻塞)
  3. 监听套接字上有连接请求。(客户端connect发起连接,则就有读事件产生)
  • socket 可写
  1. socket内核发送缓冲区有空间,则可以无阻塞写该socket。字节返回大于0.
  2. socket写操作关闭,对写操作关闭的socket执行写操作,会触发SIGPIPE信号。
    6. socket使用非阻塞Connect连接成功或失败(超时)
  3. socket上有未处理的错误。
  • int num = recv(fds[i].fd,buff,127,0);
    如果recv每次只读取一个数据,
    那么IO函数检测到该缓冲区还有数据,就会依次读完。

    请添加图片描述
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/select.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<assert.h>
#include<poll.h>
  • void fds_init(struct pollfd fds[])//初始化事件
#define FD_MAX 10
int socket_init();
void fds_init(struct pollfd fds[])//初始化事件
{
    
        for(int i = 0;i<FD_MAX;i++) // 遍历数组
        {
    
                fds[i].fd = -1;
                fds[i].events = 0;
                fds[i].revents = 0;
        }
}
  • void fds_add(struct pollfd fds[],int fd) //添加事件
void fds_add(struct pollfd fds[],int fd) //添加事件
{
    
        //把文件描述符按照数组的位添加进去,就不需要在遍历,就可以访问到。
    for(int i = 0;i<FD_MAX;i++)   //遍历数组
    {
    
        if(fds[i].fd == -1)
        {
    
           fds[i].fd = fd;
           fds[i].events = POLLIN;   //有读事件
           fds[i].revents = 0;
           break;
        }
    }

}
  • void fds_del(struct pollfd fds[],int fd)//删除事件
void fds_del(struct pollfd fds[],int fd)//删除事件
{
    
         for(int i = 0;i<FD_MAX;i++)  //遍历数组
         {
    
            if(fds[i].fd == fd)
            {
    
               fds[i].fd = -1;
               fds[i].events = 0;
               fds[i].revents = 0;
            }
         }
}
- **int 
  • main()
int main()
{
    
     int sockfd = socket_init(); //返回的是监听套接子
     if(sockfd == -1)
     {
    
             printf("socket init failed\n");
             exit(1);
     }
     struct pollfd fds[FD_MAX];
     fds_init(fds);
     fds_add(fds,sockfd);
     while(1)
     {
    
             int n = poll(fds,FD_MAX,5000);//返回就绪文件描述符总数(socket,connnect完成三次握手就是读事件)
             if(n < 0)
             {
    
                     printf("poll err\n");
             }
             else if(n == 0)
             {
    
                     printf("time out\n");
                    
             }
             else
             {
    
                     for(int i = 0;i <FD_MAX;i++)
                     {
    
                            if(fds[i].fd == -1)
                            {
    
                                    continue;
                            }
                            if(fds[i].revents & POLLIN)//实际发生的事件和读事件与,如果有,那么就是该就需事件有数据
                            {
    
                                if(fds[i].fd == sockfd)
                                {
    
                                  struct sockaddr_in caddr;
                                  int len = sizeof(caddr);
                                  int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
                                  if(c < 0)
                                  {
     
                                     continue;
                                  }
                                  printf("accept c = %d\n",c);
                                  fds_add(fds,c);

                                }
                                else
                                {
    
                                    char buff[128] ={
    0};
                                    int num = recv(fds[i].fd,buff,1,0);//如果recv每次只读取一个数据,那么IO函数检测到该缓冲区还有数据,就会依次读完。
                                    if(num <= 0)
                                    {
    
                                     close(fds[i].fd);
                                     fds_del(fds,fds[i].fd);
                                     printf("client close\n");

                                    }
                                    else
                                    {
    
                                            printf("recv(%d):%s\n",num,buff);
                                            send(fds[i].fd,"ok",2,0);

                                    }
                                }
                            }
                     }
             }  
             
     }
}
  • int socket_init();
int socket_init()
{
    
        int sockfd = socket(AF_INET,SOCK_STREAM,0);
        if(sockfd == -1)
        {
    
                return -1;
        }
        struct sockaddr_in saddr;
        memset(&saddr,0,sizeof(saddr));
        saddr.sin_family = AF_INET;
        saddr.sin_port = htons(6000);
        saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

        int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
        if(res == -1)
        {
    
                return -1;
        }
        res = listen(sockfd,5);
        if(res == -1)
        {
    
                 return -1;
        }                       //不通过accept来处理完成连接的监听套接子,而是由select来处理。
        return sockfd;

}

epoll(性能更好)

注册回调函数,专门为描述符较多的情况准备。(事件复杂度为1)(回调函数)
epoll_create()用于创建内核事件表(红黑树))
epoll_ctl()用于操作内核事件表
epoll_wait()用于在一段超时时间内等待一组文件描述符上的事件

原网站

版权声明
本文为[白U]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_52958292/article/details/126196197