• select、poll、epoll


    首先看一下man文档中这三个函数的定义:

    select函数:

    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int select(int nfds, fd_set *readfds, fd_set *writefds,
               fd_set *exceptfds, struct timeval *timeout);
    void FD_CLR(int fd, fd_set *set); // 从集合中删除指定文件描述符 int FD_ISSET(int fd, fd_set *set); // 检查集合中指定文件描述符是否准备好 void FD_SET(int fd, fd_set *set); // 将一个文件描述符加入到集合中 void FD_ZERO(fd_set *set); // 将一个集合请空

    参数说明:

    nfds:所要监视的文件描述符范围,即被监听的文件描述符最大值加一。

    readfds:被监视的可读操作的文件描述符。

    writefds:被监视的可写操作的文件描述符。

    exceptfds:被监视的文件错误异常的文件描述符。

    下面是使用select的一个echo服务端例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/time.h>
    #include <sys/select.h>
    #include <iostream>
    #define BUF_SIZE 100
    
    void error_handling(char* message)
    {
        fputs(message, stderr);
        fputc('
    ', stderr);
        exit(1);
    }
    
    int main(int argc, char* argv[])
    {
        int serv_sock, clnt_sock;
        struct sockaddr_in serv_adr, clnt_adr;
        struct timeval timeout;
        fd_set reads, cpy_reads;
    
        socklen_t adr_sz;
        int fd_max, str_len, fd_num;
        char buf[BUF_SIZE];
    
        if ((serv_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket error");
    	return 1;
        }
    
        memset(&serv_adr, 0, sizeof(serv_adr));
        serv_adr.sin_family = AF_INET;
        serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_adr.sin_port = htons(8000);
    
        if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) {
            error_handling("bind error");
        }
    
        if (listen(serv_sock, 5) == -1) {
            error_handling("listen error");
        }
    
        FD_ZERO(&reads);  // 初始化
        // 注册serv_sock
        FD_SET(serv_sock, &reads);
        fd_max = serv_sock;
    
        while (true) {
            cpy_reads = reads;
    	timeout.tv_sec = 5;
    	timeout.tv_usec = 5000;
    
    	// 监听服务端socket和与客户端连接的服务端socket的read事件
    	if ((fd_num = select(fd_max + 1, &cpy_reads, 0, 0, &timeout)) == -1)
    	    break;
    
    	if (fd_num == 0)
    	    continue;
    
    	if (FD_ISSET(serv_sock, &cpy_reads)) {   // 受理客户端连接请求
    	    adr_sz = sizeof(clnt_adr);
    	    clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
    	    FD_SET(clnt_sock, &reads);
    
    	    if (fd_max < clnt_sock)
                    fd_max = clnt_sock;
    
    	    std::cout << "connected client: " << clnt_sock << std::endl;
    	}
    	else {   // 转发客户数据
    	    str_len = read(clnt_sock, buf, BUF_SIZE);
    
    	    if (str_len == 0) {  // 客户端发送退出EOF
    	        // 取消注册该推出的套接字
    		FD_CLR(clnt_sock, &reads);
    		close(clnt_sock);
    		std::cout << "closed client: " << clnt_sock << std::endl;
    	    }
    	    else {
    		// 回写到客户端
    	        write(clnt_sock, buf, str_len);
    	    }
    	}
        }
    
        close(serv_sock);
        return 0;
    }
    

    poll函数:

    #include <poll.h>
    
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);

      struct pollfd {
        int fd; /* file descriptor */
        short events; /* requested events */
        short revents; /* returned events */
      };

    参数说明:

    fds:所监听的描述符。

    nfds:所监听的文件描述符数目。

    timeout:超时时间,单位是毫秒,设为-1表示永不超时。

    下面是poll函数的事件标志符:

     epoll:

    int epoll_create1(int flags); // 创建epoll实例
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); // 管理epoll事件

    函数参数:

    • epfd : epoll实例的fd
    • op : 操作标志,下文会描述
    • fd : 监控对象的fd
    • event : 事件的内容,下文描述

    op可以有3个值,分别为:

    • EPOLL_CTL_ADD : 添加监听的事件
    • EPOLL_CTL_DEL : 删除监听的事件
    • EPOLL_CTL_MOD : 修改监听的事件

    typedefunion epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;

    struct epoll_event {__uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */ };

    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); // 等待epoll事件

    函数参数:

    • epfd : epoll实例的fd
    • events : 储存事件的数组首地址
    • maxevents : 最大事件的数量
    • timeout : 等待的最长时间

    使用epoll的echo服务端例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/epoll.h>
    #include <iostream>
    
    #define BUF_SIZE 100
    #define EPOLL_SIZE 50
    
    void error_handling(char* message)
    {
        fputs(message, stderr);
        fputc('
    ', stderr);
        exit(1);
    }
    
    int main(int argc, char* argv[])
    {
        int serv_sock, clnt_sock;
        struct sockaddr_in serv_adr, clnt_adr;
        socklen_t adr_sz;
        int str_len, i;
        char buf[BUF_SIZE];
    
        // 类似select的fd_set变量查看监视对象的状态变化
        // epoll_event结构体将发生变化的文件描述符集中到一起
        struct epoll_event* ep_events;
        struct epoll_event event;
        int epfd, event_cnt;
    
        if ((serv_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            error_handling("socket error");
        }
    
        memset(&serv_adr, 0, sizeof(serv_adr));
        serv_adr.sin_family = AF_INET;
        serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_adr.sin_port = htons(8000);
    
        if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) < 0) {
            error_handling("bind error");
        }
    
        if (listen(serv_sock, 5) < 0) {
            error_handling("listen error");
        }
    
        // 创建文件描述符的保存空间成为epoll例程
        epfd = epoll_create(EPOLL_SIZE);
        ep_events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * EPOLL_SIZE);
    
        // 添加读取事件的监视(注册)
        event.events = EPOLLIN;   // 读取数据事件
        event.data.fd = serv_sock;
        epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);
    
        while (true) {
            // 响应事件 返回发生事件的文件描述符数
        event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);  // 传入-1 表示一直等待直到事件发生
    
        if (event_cnt == -1) {
            std::cout << "epoll_wait error" << std::endl;
            break;
        }
        
        // 服务端套接字个客户端套接字
        for (i = 0; i < event_cnt; i++) {
            if (ep_events[i].data.fd == serv_sock) {  // 服务端与客户端建立连接
                adr_sz = sizeof(clnt_adr);
            clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
            event.events = EPOLLIN;
            event.data.fd = clnt_sock;
            epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
            std::cout << "connected client: " << clnt_sock << std::endl;
            }
            else {   // 连接后的客户端 传递数据
                str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);
    
            if (str_len == 0) {
                // 删除事件
                epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
                close(ep_events[i].data.fd);
                std::cout << "close client: " << ep_events[i].data.fd << std::endl;
            }
            else {
                write(ep_events[i].data.fd, buf, str_len);
            }
            }
        }
        }
    
        close(serv_sock);
        close(epfd);
        return 0;
    }

    select、poll、epoll比较:

    转载请注明出处
  • 相关阅读:
    常用操作之增、删、改、查
    文本编辑器相关操作
    关于Secondary NameNode
    hive基础概念总结(1)
    Shell 十三问[转]
    《SQL Server 2012 Tutorials Analysis Services Multidimensional Modeling》读后感
    HDFS随笔(1)
    Hue for Apache Hadoop
    大数据面试题总结
    关于数据倾斜
  • 原文地址:https://www.cnblogs.com/lnlin/p/14525907.html
Copyright © 2020-2023  润新知