io多路复用之poll的详细执行过程

1.结构体struct pollfd的定义

struct pollfd {
    int   fd;        /* 文件描述符 */
    short events;    /* 想要监视的事件(input/output/priority) */
    short revents;   /* 实际发生的事件(返回的事件) */
};

2.定义pollfd数组,并设置listenfd想要监听的事件

    struct pollfd fds[POLL_SIZE]={0};//定义一个POLL_SIZE大小的pollfd数组
    fds[sockfd].fd=sockfd;
    fds[sockfd].events=POLLIN;//设置想要监听的事件为POLLIN

3.调用函数poll()监听事件的发生,并返回实际发生的事件

int nready=poll(fds,maxfd+1,-1);
printf("nready:%d\n",nready);//实际发生的事件的数量

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds:一个指向 struct pollfd 结构体数组的指针,每个结构体描述了一个要监视的文件描述符及其关联的事件和发生的事件

nfds:表示文件描述符数组 fds 的大小,即要监视的文件描述符的数量。

timeout:超时值,指定 poll() 函数的阻塞时间。它可以有以下几种取值:

  • 传递负数:表示无限阻塞,poll() 函数将一直等待,直到至少一个文件描述符的事件发生。
  • 传递0:表示非阻塞模式,poll() 函数会立即返回,不管文件描述符的状态如何。
  • 传递正整数:表示超时时间(以毫秒为单位),poll() 函数会等待指定的毫秒数后返回,如果在超时前没有任何事件发生,它将返回0

注意:poll的返回值并不是clientfd(客户端)连接注册想要监听的事件,而是在这些已注册监听事件中实际发生的事件的个数. 

4.判断revent数值

在调用 poll 函数后,revents 字段会被设置为实际发生的事件。revents 字段是一个位掩码,它可以包含以下几个值中的一个或多个,表示文件描述符的状态:

  • POLLIN:文件描述符可以进行读取操作。
  • POLLOUT:文件描述符可以进行写入操作。
  • POLLERR:文件描述符发生了错误。
  • POLLHUP:文件描述符挂起(连接关闭)。
  • POLLNVAL:文件描述符无效。

 如果要检查文件描述符是否可以读取,可以执行以下操作:

if (revents & POLLIN) {
    // 文件描述符可以进行读取操作
}

只有当被监听的事件实际发生时才会执行 

demo: 


#include
#include
#include
#include
#include
#include
#include
#include

#define PORT 8848

#define POLL_SIZE 1024
#define BUFLEN 128

int main(){

    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(struct sockaddr_in));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_port=htons(PORT);
    serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);

    if(-1==bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))){
        printf("bind failed\n");
        exit(1);
    }

    listen(sockfd,10);
    //poll

    /*
    struct pollfd {
        int   fd;        //文件描述符 
        short events;    // 想要监视的事件(input/output/priority) 
        short revents;   // 实际发生的事件(返回的事件) 
    };
    */

    struct pollfd fds[POLL_SIZE]={0};
    fds[sockfd].fd=sockfd;
    fds[sockfd].events=POLLIN;
    int maxfd=sockfd;
    int clientfd=0;
    int count=0;
    while(1){
        printf("count:%d\n",count++);//打印while循环次数
        //sleep(10);//休眠10s
        int nready=poll(fds,maxfd+1,-1);
        printf("nready:%d\n",nready);//实际发生的事件的数量

        //revent
        if(fds[sockfd].revents&POLLIN){
            struct sockaddr_in clientaddr;
            socklen_t len=sizeof(clientaddr);
            clientfd=accept(sockfd,(struct sockaddr*)&clientaddr,&len);
            printf("new client:%d\n",clientfd);
            fds[clientfd].fd=clientfd;
            fds[clientfd].events=POLLIN;
            if(clientfd>maxfd){
                maxfd=clientfd;
            }
            if(nready-1==0){
                continue;
            }
            
        }
        int i=sockfd+1;
        for(;i<=maxfd;i++){
            char buffer[BUFLEN]={0};
            int ret=0;
            if(fds[i].revents&POLLIN){//只有事件实际发生时,该if程序才会被执行
                ret=read(fds[i].fd,buffer,BUFLEN-1);
                if(ret==0){//客户端主动关闭
                    printf("client %d close\n",fds[i].fd);
                    close(fds[i].fd);//关闭socket
                    //重置
                    fds[i].fd=-1;
                    fds[i].events=0;
                    continue;
                }
                buffer[ret]='\0';
                printf("recv from client:%d  buffer:%s\n",fds[i].fd,buffer);

                ret=write(fds[i].fd,buffer,strlen(buffer));
            }
        }
    }

    exit(0);
}

你可能感兴趣的:(linux,运维,服务器)