正在加载……
专注、离线、切勿分心
    在 linux 的网络编程中,很长的时间都在使用 select 来做事件触发。在 linux 新的内核中,有了一种替换它的机制,就是 epoll。相比于 select, epoll 最大的好处在于它不会随着监听 fd 数目的增长而降低效率。因为在内核中的 select 实现中,它是采用轮询来处理的,轮询的 fd 数目越多,自然耗时越多。并且,在 linux/posix_types.h 头文件有这样的声明:
#define __FD_SETSIZE 1024
表示 select 最多同时监听 1024 个 fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。
epoll 的接口非常简单,一共就三个函数:效率稳定,不会随着监控的描述符增多而减小
1. int epoll_create(int size);
创建一个 epoll 的句柄, size 用来告诉内核这个监听的数目一共有多大。(epoll模型对监控的描述符没有限制,写什么都无所谓,只要不写0就好)这个参数不同于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()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。//成功返回0,失败返回-1
第一个参数是 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 */  把我们要监控的描述符再填一遍
};
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 队列里。
EPOLL 事件有两种模型:
Edge Triggered  (ET) 边缘触发 只有数据到来,才触发,不管缓存区中是否还有数据。
不管你发多少数据,我一次只能读多少就读多少,读不完的也不会再次触发epoll_wait函数,下次再发数据在触发,就会连同上次没读完,仍然在缓存区的数据也接着读到
Level Triggered (LT) 水平触发 只要有数据都会触发。  
当缓冲区有数据是,epoll_wait会不断得到触发(效率不高)如果一次发送的数据太多,超过接受数据数组的大小,当接受数据的数组读满后,read会再次触发epoll_wait函数,来接着读输入到缓冲区的数据
3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents,int timeout);
等待事件的产生,类似于 select()调用。参数 events 用来从内核得到事件的集合(所以每次使用前都要清空,这里相当于以前用的select的rdset集合), maxevents 告之内核这个 events 有多大,这个 maxevents 的值不能大于创建 epoll_create()时的 size参数 timeout 是超时时间(毫秒,0 会立即返回,-1 将不确定,也有说法说是永久阻塞;是一个相对时间)。该函数返回需要处理的事件数目,如返回 0 表示已超时。有描述符可读,主动通知 epoll_wait()
      


//数组取地址还是它本身
func.h
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
"ctrl+d"read()读到0
epoll_tcp_server.c epoll_tcp_client.c
#include "func.h"
#define NUM 10
int main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("error args\n");
                return -1;
        }
        int sfd=socket(AF_INET,SOCK_STREAM,0);
        if(-1==sfd)
        {
                perror("socket");
                return -1;
        }
        struct sockaddr_in ser;
        memset(&ser,0,sizeof(ser));
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi(argv[2]));
        ser.sin_addr.s_addr=inet_addr(argv[1]);
        int ret;
        ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
        if(-1==ret)
        {
                perror("bind");
                return -1;
        }
        ret=listen(sfd,NUM);
        if(-1==ret)
        {
                perror("listen");
                return -1;
        }
        int epfd=epoll_create(1);   //创建一个句柄,参数只要不是0就OK 
        struct epoll_event event,evs[NUM+1];
//第二个数组用来传参给epoll_wait(),得到哪个描述符有输入
        event.events=EPOLLIN;      //注册事件,多个操作要用或操作
        event.data.fd=sfd;   //注册要监听的描述符
        ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
        if(-1==ret)
        {
                perror("epoll_ctl");
                return -1;
        }
        event.events=EPOLLIN;     //注册标准输入
        event.data.fd=0;
        ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
        if(-1==ret)
        {
                perror("epoll_ctl");
                return -1;
        }
        int i;
        int new_fd;
        char buf[128];
        int n;
        while(1)
        {
                memset(evs,0,sizeof(evs));
                ret=epoll_wait(epfd,evs,NUM+2,-1);
                if(ret >0)
                {
                        for(i=0;i<ret;i++)
                        {
                                if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)               
                                {
                                        new_fd=accept(sfd,NULL,NULL);
                                        printf("accept newfd =%d\n",newfd);
                                        event.events=EPOLLIN;
                                        event.data.fd=new_fd;
                                        epoll_ctl(epfd,EPOLL_CTL_ADD,new_fd,&event);       
                                }
                                if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
                                {
                                        memset(buf,0,sizeof(buf));
                                        n=read(0,buf,sizeof(buf));
                                        if(n>0)
                                        {
                                                send(new_fd,buf,strlen(buf)-1,0);
                                        }else if(n==0)
                                        {
                                                printf("bye\n");
                                                event.events=EPOLLIN;
                                                event.data.fd=new_fd;
                                                epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
                                                close(new_fd);       
                                        }
                                }
                                if(evs[i].events == EPOLLIN && evs[i].data.fd == new_fd)
                                {
                                        memset(buf,0,sizeof(buf));
                                        n=recv(new_fd,buf,sizeof(buf),0);
                                        if(n>0)
                                        {
                                                printf("recv client buf =%s\n",buf);
                                        }else if(n==0){
                                                printf("bye\n");
                                                event.events=EPOLLIN;
                                                event.data.fd=new_fd;
                                                epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
                                                close(new_fd);
                                        }
                                }       
                        }
                }
        }
        return 0;
}
#include "func.h"

int main(int argc,char** argv)
{
        if(argc !=3)
        {
                printf("error args\n");
                return -1;
        }
        int sfd=socket(AF_INET,SOCK_STREAM,0);
        if(-1==sfd)
        {
                perror("socket");
                return -1;
        }
        struct sockaddr_in ser;
        memset(&ser,0,sizeof(ser));
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi(argv[2]));           //一定要用htons
        ser.sin_addr.s_addr=inet_addr(argv[1]);
        int ret;
        ret=connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
        if(-1==ret)
        {
                perror("connect");
                return -1;
        }
        int epfd=epoll_create(1);
        struct epoll_event event,evs[2];
        event.events=EPOLLIN;
        event.data.fd=sfd;
        ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
        if(-1==ret)
        {
                perror("epoll_ctl");
                return -1;
        }
        event.events=EPOLLIN;
        event.data.fd=0;
        ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
        if(-1==ret)
        {
                perror("epoll_ctl");
                return -1;
        }
        int i;
        char buf[128];
        int n;
        while(1)
        {
                memset(evs,0,sizeof(evs));
                ret=epoll_wait(epfd,evs,2,-1);
                if(ret>0)
                {
                        for(i=0;i<ret;i++)
                        {
                                if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
                                {
                                        memset(buf,0,sizeof(buf));
                                        n=read(0,buf,sizeof(buf));                    
                                        if(n==0)
                                        {
                                                printf("bye\n");
                                                close(sfd);
                                                return 0;
                                        }
                                        n=send(sfd,buf,strlen(buf)-1,0);
                                        if(-1==n)
                                        {
                                                perror("send");
                                                return -1;
                                        }
                                }
                                if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)
                                {
                                        memset(buf,0,sizeof(buf));
                                        n=recv(sfd,buf,sizeof(buf),0);                
                                        if(n > 0)
                                        {
                                                printf("recv form server buf =%s\n",bu    f);
                                        }else if(n==0)
                                        {
                                                printf("bye\n");
                                                close(sfd);
                                                return 0;
                                        }
                                }
                        }
                }
        }
        return 0;
}


   









  //服务器端ctrl+d;












posted on 2018-03-20 11:01  正在加载……  阅读(200)  评论(0编辑  收藏  举报