zoukankan      html  css  js  c++  java
  • epoll使用实例说明

    之前一直在讲如何epoll如何好用,但是并没有实例来演示epoll的使用,下面我们就看一个服务器端使用epoll监听大量并发链接的例子。

    首先看一下epoll的几个函数的介绍。
    1、epoll_create函数

    /** 
     * @brief    该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。
     * 
     * @param    size    size就是你在这个epoll fd上能关注的最大socket fd数
     * 
     * @return   生成的文件描述符
     */
    int epoll_create(int size);

    2、epoll_ctl函数

    /** 
     * @brief    该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。
     * 
     * @param    epfd    由 epoll_create 生成的epoll专用的文件描述符
     * @param    op      要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除
     * @param    fd      关联的文件描述符
     * @param    event   指向epoll_event的指针
     * 
     * @return   0       succ
     *           -1      fail
     */
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

    其中用到的数据结构结构如下:

    typedef union 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 */
    };

    常用的事件类型:
    EPOLLIN :表示对应的文件描述符可以读;
    EPOLLOUT:表示对应的文件描述符可以写;
    EPOLLPRI:表示对应的文件描述符有紧急的数据可读
    EPOLLERR:表示对应的文件描述符发生错误;
    EPOLLHUP:表示对应的文件描述符被挂断;
    EPOLLET: 表示对应的文件描述符有事件发生;

    例:

    struct epoll_event ev;
    //设置与要处理的事件相关的文件描述符
    ev.data.fd=listenfd;
    //设置要处理的事件类型
    ev.events=EPOLLIN|EPOLLET;
    //注册epoll事件
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

    3、epoll_wait函数

    /** 
     * @brief    该函数用于轮询I/O事件的发生
     * 
     * @param    epfd        由epoll_create 生成的epoll专用的文件描述符
     * @param    events      用于回传代处理事件的数组
     * @param    maxevents   每次能处理的事件数
     * @param    timeout     等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可
     * 
     * @return   >=0         返回发生事件数
     *           -1          错误
     */
    int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);

    好了,其实在epoll的使用中无非就用到了上面介绍的几个函数,下面贴一段用epoll实现的服务器代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <openssl/ssl.h>
    #include <openssl/err.h>
    #include <fcntl.h>
    #include <sys/epoll.h>
    #include <sys/time.h>
    #include <sys/resource.h>
    #define MAXBUF 1024
    #define MAXEPOLLSIZE 10000
    /*
       setnonblocking – 设置句柄为非阻塞方式
       */
    int setnonblocking(int sockfd)
    {
        if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)
        {
            return -1;
        }
        return 0;
    }
    /*
       handle_message – 处理每个 socket 上的消息收发
       */
    int handle_message(int new_fd)
    {
        char buf[MAXBUF + 1];
        int len;
        /* 开始处理每个新连接上的数据收发 */
        bzero(buf, MAXBUF + 1);
        /* 接收客户端的消息 */
        len = recv(new_fd, buf, MAXBUF, 0);
        if (len > 0)
        {
            printf(“%d接收消息成功:’%s’,共%d个字节的数据/n”,
                 new_fd, buf, len);
        }
        else
        {
            if (len < 0)
                printf
                    (“消息接收失败!错误代码是%d,错误信息是’%s’/n”,
                     errno, strerror(errno));
            close(new_fd);
            return -1;
        }
        /* 处理每个新连接上的数据收发结束 */
        return len;
    }
    int main(int argc, char **argv)
    {
        int listener, new_fd, kdpfd, nfds, n, ret, curfds;
        socklen_t len;
        struct sockaddr_in my_addr, their_addr;
        unsigned int myport, lisnum;
        struct epoll_event ev;
        struct epoll_event events[MAXEPOLLSIZE];
        struct rlimit rt;
        myport = 5000;
        lisnum = 2; 
        /* 设置每个进程允许打开的最大文件数 */
        rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;
        if (setrlimit(RLIMIT_NOFILE, &rt) == -1) 
        {
            perror(“setrlimit”);
            exit(1);
        }
        else 
        {
            printf(“设置系统资源参数成功!/n”);
        }
        /* 开启 socket 监听 */
        if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
        {
            perror(“socket”);
            exit(1);
        }
        else
        {
            printf(“socket 创建成功!/n”);
        }
        setnonblocking(listener);
        bzero(&my_addr, sizeof(my_addr));
        my_addr.sin_family = PF_INET;
        my_addr.sin_port = htons(myport);
        my_addr.sin_addr.s_addr = INADDR_ANY;
        if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) 
        {
            perror(“bind”);
            exit(1);
        } 
        else
        {
            printf(“IP 地址和端口绑定成功/n”);
        }
        if (listen(listener, lisnum) == -1) 
        {
            perror(“listen”);
            exit(1);
        }
        else
        {
            printf(“开启服务成功!/n”);
        }
        /* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */
        kdpfd = epoll_create(MAXEPOLLSIZE);
        len = sizeof(struct sockaddr_in);
        ev.events = EPOLLIN | EPOLLET;
        ev.data.fd = listener;
        if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0) 
        {
            fprintf(stderr, “epoll set insertion error: fd=%d/n”, listener);
            return -1;
        }
        else
        {
            printf(“监听 socket 加入 epoll 成功!/n”);
        }
        curfds = 1;
        while (1) 
        {
            /* 等待有事件发生 */
            nfds = epoll_wait(kdpfd, events, curfds, -1);
            if (nfds == -1)
            {
                perror(“epoll_wait”);
                break;
            }
            /* 处理所有事件 */
            for (n = 0; n < nfds; ++n)
            {
                if (events[n].data.fd == listener) 
                {
                    new_fd = accept(listener, (struct sockaddr *) &their_addr,&len);
                    if (new_fd < 0) 
                    {
                        perror(“accept”);
                        continue;
                    } 
                    else
                    {
                        printf(“有连接来自于: %d:%d, 分配的 socket 为:%d/n”,
                                inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
                    }
                    setnonblocking(new_fd);
                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = new_fd;
                    if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_fd, &ev) < 0)
                    {
                        fprintf(stderr, “把 socket ‘%d’ 加入 epoll 失败!%s/n”,
                                new_fd, strerror(errno));
                        return -1;
                    }
                    curfds++;
                } 
                else
                {
                    ret = handle_message(events[n].data.fd);
                    if (ret < 1 && errno != 11)
                    {
                        epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd,&ev);
                        curfds–-;
                    }
                }
            }
        }
        close(listener);
        return 0;
    }

    OK,基本到这里epoll的使用也就明朗了,希望对大家有用~

  • 相关阅读:
    C#+ArcEngine10.0+SP5实现鼠标移动动态显示要素属性信息
    C#中实现excel文件批量导入access数据表中
    C#子窗体闪烁问题解决
    C#打印代码运行时间
    TableLayoutPanel导致的闪屏问题
    线段余弦角+凸包算法
    ICommand相关知识
    批量导出access某表内容到word文档
    通过数组里的时间重新排序数组
    数组层级叠加
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/4053010.html
Copyright © 2011-2022 走看看