网络编程之——多路IO转接(epoll模型)

15 篇文章 0 订阅
12 篇文章 0 订阅

epoll内部使用红黑树数据结构,epoll_create函数创建一个根节点并返回,epoll_ctl函数进行添加删除修改子节点。
epoll操作过程需要三个接口,分别如下:

#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

(1) int epoll_create(int size);
  创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

(2)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

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

events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

(3) int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
  等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

示例

#include "stdlib.h"
#include "sys/epoll.h"
#include "netinet/ip.h"
#include "netinet/in.h"
#include "wrap.h"
#include "strings.h"
#include "arpa/inet.h"
#include "unistd.h"
#include "fcntl.h"
#include "ctype.h"
#include "string.h"

#define PORT 8888
//#define IP "127.0.0.1"
int socket_bind( in_addr_t ip,int port)
{
    int listenfd;
    struct sockaddr_in servaddr;
    bzero(&servaddr,sizeof(servaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr .s_addr= htonl(ip);
    //inet_pton(AF_INET,ip,&servaddr.sin_addr);
    servaddr.sin_port = htons(port);
    listenfd = Socket(AF_INET,SOCK_STREAM,0);
    int opt = 1;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,sizeof(opt));
    Bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));

    return listenfd;
}

void handle_accept(int epfd,int listfd)
{
    int cfd;
    struct sockaddr_in clitaddr;
    socklen_t clit_len;
    char clit_ip[1024];

    bzero(clit_ip,1024);
    bzero(&clitaddr,sizeof(clitaddr));
    clit_len = sizeof(clit_len);

    cfd = Accept(listfd,(struct sockaddr*)&clitaddr,&clit_len);
    printf("client ip:%s,port:%d\n",
                    inet_ntop(AF_INET,&clitaddr.sin_addr.s_addr,clit_ip,1024),
                    ntohs(clitaddr.sin_port));
    struct epoll_event ev;
    ev.events = EPOLLIN|EPOLLET;
    ev.data.fd = cfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev); 
    int flag = fcntl(cfd,F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(cfd,F_SETFL,flag);
}

char buf[BUFSIZ];
void do_read(int epfd,int cfd)
{
    bzero(&buf,BUFSIZ);
    int n = read(cfd,buf,BUFSIZ);
    
    if(n == -1)
    {
        sys_err("read error");
        Close(cfd);
        epoll_ctl(epfd,EPOLL_CTL_DEL,cfd,NULL);
    }else if(n == 0)
    {
        printf("client exit.code:%d...\n",cfd);
        Close(cfd);
        epoll_ctl(epfd,EPOLL_CTL_DEL,cfd,NULL);
    }else
    {
        write(STDOUT_FILENO,buf,n);
        
        struct epoll_event ev;
        ev.events = EPOLLOUT|EPOLLET;
        ev.data.fd = cfd;
        epoll_ctl(epfd,EPOLL_CTL_MOD,cfd,&ev); 
    }   
}

void do_write(int epfd,int cfd)
{
    int  n = strlen(buf);
    for (int i = 0; i < n; i++)
     {
         buf[i] = toupper(buf[i]);
     }
     
     write(cfd,buf,n);

    struct epoll_event ev;
    ev.events = EPOLLIN|EPOLLET;
    ev.data.fd = cfd;
    epoll_ctl(epfd,EPOLL_CTL_MOD,cfd,&ev); 
}

void server_epoll(int listenfd)
{
    int epollfd;
    struct epoll_event events[1000];
    int ret;

    epollfd = epoll_create(1000);//创建epoll头结点
    struct epoll_event ev;
    ev.events = EPOLLIN|EPOLLET;//ET 模式
    ev.data.fd = listenfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&ev);//添加监听套接字
    int flag = fcntl(listenfd,F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(listenfd,F_SETFL,flag);//设置非阻塞

    while (1)
    {
        ret = epoll_wait(epollfd,events,1000,-1);
        if(ret == -1)
        {
            sys_err("epoll wait error");
            break;
        }

        int fd;
        //进行遍历
        for (int i = 0;i < ret;i++)
        {
            fd = events[i].data.fd;
            //根据描述符的类型和事件类型进行处理
            if ((fd == listenfd) &&(events[i].events & EPOLLIN))
                handle_accept(epollfd,listenfd);
            else if (events[i].events & EPOLLIN)
                do_read(epollfd,fd);
            else if (events[i].events & EPOLLOUT)
                do_write(epollfd,fd);
        }
    }
    
    Close(listenfd);
    Close(epollfd);
}

int main(int args,char *argc[])
{
    int listenfd;
    listenfd = socket_bind((INADDR_ANY),PORT);
    Listen(listenfd,128);
    server_epoll(listenfd);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值