多路转接-EPOLL 及简单的EPOLL服务器实现

10 篇文章 0 订阅

EPOLL简介

EPOLL是linux下公认的最好用的I/O就绪通知方式。
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显着提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

EPOLL 相关的三个系统调用

int epoll_create(int size) 


创建epoll句柄,占用一个fd值,使用完epoll 后必须调用close关闭

int epoll_ctl(int epfd, int op, int fd, struct epoll_events* event) 

epoll的事件注册函数,告诉内核监听什么事件。

  1. 第一个参数epfd是上一个函数create的返回值。
  2. 第二个参数表示动作,用三个宏表示EPOLL_CTL_ADD注册新的fd到epfd中。
    EPOLL_CTL_MOD修改已注册的fd监听事件。EPOLL_CTL_DELepfd中删除一个fd事件。
  3. 第三个参数是需要监听的fd
  4. 第四个参数是需要监听什么事件。
struct epoll_event {
        _uint32_t events; /* Epoll events */
        epoll_data_t data; /* User data variable */
    };
    typedef union epoll_data {
        void *ptr;
        int fd;
        uint32_t u32;
        uint64_t u64;
    } epoll_data_t;
    events可以是以下几个宏的集合
    EPOLLIN :   表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
    EPOLLOUT:   表示对应的文件描述符可以写
    EPOLLPRI:   表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
    EPOLLERR:   表示对应的文件描述符发生错误
    EPOLLHUP:   表示对应的文件描述符被挂断;
    EPOLLET:    将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的
    EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout)

timeout是超时时间(0会立即返回,-1将永久阻塞)。如果调用成功,则返回对应IO上已准备好文件描述符数目。

工作原理

epoll只告知那些已经就绪的文件描述符,通过调用epoll_wait()函数获得就绪的文件描述符时,返回的不是实际的文件描述符,而是就绪描述符数量的值,只需要去epoll指定一个数组去获得这些文件描述符,省去了这些描述符在系统调用时的开销。

*************************************************************************
    > File Name: epoll.c
    > Author: weierxiao
    > Mail: 1091868910@qq.com 
    > Created Time: Tue 04 Jul 2017 04:31:39 PM CST
 ************************************************************************

#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#include<sys/epoll.h>
#include<stdlib.h>

#define EPOLL_REVS_SIZE 64

static void Usage(const char* proc)
{
    printf("Usage : %s, [local ip], [local port]\n", proc);
}

int startup(char *ip, int port)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock <0 )
    {
        perror("sock");
        exit(2);
    }
    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,  &opt, sizeof(opt));

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = inet_addr(ip);

    if (bind(sock, (struct sockaddr*)&local, sizeof(local))<0)
    {
        perror("bind");
        exit(3);
    }

    if (listen(sock, 10) < 0)
    {
        perror("listen");
        exit(4);
    }
    return sock;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }

    int listen_sock = startup(argv[1],atoi(argv[2]));

    int epfd = epoll_create(256);
    if (epfd < 0)
    {
        perror("epoll create!\n");
        return 5;
    }

    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &ev );

    int nums = -1;
    struct epoll_event revs[EPOLL_REVS_SIZE];
    int timeout =-1;
    while (1)
    {
    switch ( (nums = epoll_wait(epfd,revs, EPOLL_REVS_SIZE, timeout )))

        {
            case 0:
               printf("time out ...\n");
               break;

            case -1:
               perror("epoll wait\n");
               break;
            default:
               {
                    int i =0;
                    for (;i< nums; i++)
                    {
                        int sock = revs[i].data.fd;
                        if (sock == listen_sock && (revs[i].events & EPOLLIN))
                        {
                            struct sockaddr_in client;
                            socklen_t len = sizeof(client);

                            int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len);
                            if(new_sock < 0)
                            {
                                perror("accept !\n");
                                continue;
                            }

                            printf(" get client :[%s] [%d]\n", inet_ntoa(client.sin_addr),htons(client.sin_port));
                            ev.events = EPOLLIN;
                            ev.data.fd = new_sock;
                            epoll_ctl(epfd, EPOLL_CTL_ADD, new_sock, &ev);


                        }
                        else if(sock != listen_sock)
                        {
                            if (revs[i].events & EPOLLIN)
                            {
                                char *buf[10240];
                                ssize_t s = read(sock, buf, sizeof(buf)-1);
                                if (s >0 )
                                {
                                    printf("client : %s\n", buf);
                                    ev.events = EPOLLOUT;
                                    ev.data.fd = sock;
                                    epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &ev);
                                }
                                else if (s == 0)
                                {
                                    printf("client quit\n");
                                    close(sock);
                                    epoll_ctl(epfd, EPOLL_CTL_DEL, sock, NULL);
                                }
                                else{
                                    perror("read");
                                    close(sock);
                                    epoll_ctl(epfd, EPOLL_CTL_DEL, sock, NULL);
                                }
                            }

                            else if (revs[i]. events & EPOLLOUT)
                            {
                                const char *msg = "HTTP/1.0  OK 200 \r\n\r\n<html><h1>WX    EPOLL  </h1></html>";
                                 write(sock,msg, strlen(msg));
                                close(sock);
                                epoll_ctl(epfd, EPOLL_CTL_DEL,sock,NULL);

                            }
                            else
                            {}
                        }
                    }
               }
               break;

        }

    }
    return 0;
}

./epoll 0 8080 //调用

打开手机或电脑的浏览器,连接同一个局域网,输入绑定的服务器IP地址:绑定的端口号
这里写图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

魏尔肖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值