body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

    在 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;
}



epoll 模型的更多相关文章

  1. Epoll模型详解

    Linux 2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数. 1.为什么select落后    首先,在Lin ...

  2. 【转】select和epoll模型的差异

    http://www.cppblog.com/converse/archive/2008/10/12/63836.html epoll为什么这么快 epoll是多路复用IO(I/O Multiplex ...

  3. linux epoll模型

    原文:http://yjtjh.blog.51cto.com/1060831/294119 Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数.Linux ...

  4. Linux网络服务器epoll模型的socket通讯的实现(一)

    准备写一个网络游戏的服务器的通讯模块,参考网上看到的一些代码,在linux下面实现一个多线程的epoll模型的socket通讯的代码,以下是第一部分多线程的切换代码: 1 #include <s ...

  5. (OK) Linux epoll模型—socket epoll server client chat

    http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html http://blog.csdn.net/denkensk/article/d ...

  6. nginx中的epoll模型

    要了解epoll模型,就要一个一个知识点由浅至深地去探索. 1.IO复用技术 IO流请求操作系统内核,有串行处理和并行处理两种概念. 串行处理是前面一个操作处理地时候,后面的所有操作都需要等待.因此, ...

  7. select 和epoll模型区别

    1.select 和epoll模型区别 1.1.网络IO模型概述 通常来说,网络IO可以抽象成用户态和内核态之间的数据交换.一次网络数据读取操作(read),可以拆分成两个步骤:1)网卡驱动等待数据准 ...

  8. Epoll模型

    Epoll模型 相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率.因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多.并且,在l ...

  9. select、poll、epoll模型对比

    select.poll.epoll模型对比 先说Select:            1.Socket数量限制:该模式可操作的Socket数由FD_SETSIZE决定,内核默认32*32=1024. ...

随机推荐

  1. 寻找最小(最大)的k个数

    题目描述:输入n个整数,输出其中最小的k个元素. 例如:输入1,2,3,4,5,6,7,8这8个数字,则最小的4个数字为1,2,3,4. 思路1:最容易想到的方法:先对这个序列从小到大排序,然后输出前 ...

  2. CCScene,CCLayer,CCSprite,CCDirector

    一.CCScene : 游戏中不同的画面可以用不同的场景展示出来,大致的可以分为以下的几类场景: 1. 展示类场景.游戏开场画面,游戏简介,胜利以及失败提示,帮助. 2. 选择类场景.主菜单,游戏设置 ...

  3. maven解决“Could not calculate build plan”问题

    错误提示如下:(eclipse+maven) Could not calculate build plan: Failure to transfer org.apache.maven.plugins: ...

  4. Virtualbox中win7虚拟机中U盘不可用问题的解决

    Virtualbox版本是5.0.0,主机运行多是Ubuntu12.04 LTS,虚拟机是Win7 X64.起初Win7正常运行,Virtualbox的增强功能已安装.下面是如何一步一步解决U盘不可用 ...

  5. Java 对比Vector、ArrayList、LinkedList

    ①引言 在日常生活中能高效的管理和操作数据是非常重要的.Java提供了强大的集合框架,大大提高了开发者的生产力,今天就了解一下有关集合框架方面的问题. Vector.ArrayList.LinkedL ...

  6. SQL优化之limit 1

    在某些情况下,如果明知道查询结果只有一个,SQL语句中使用LIMIT 1会提高查询效率.  例如下面的用户表(主键id,邮箱,密码): create table t_user( id int prim ...

  7. HashMap 遍历的两种方式及性能比较

    HashMap 是Java开发中经常使用的数据结构.相信HashMap 的基本用法你已经很熟悉了.那么我们该如何遍历HashMap 呢?哪种遍历方式的性能更好呢?本篇文章来为你解决这个疑惑. 一.Ha ...

  8. 【javascript】数据结构-队列

    <!DOCTYPE html> <html> <head> <title>queue</title> <meta charset=&q ...

  9. 日志自定义Tag

    import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; /** * Crea ...

  10. nodejs v8引擎

    Node.js 线程你理解的可能是错的 本文代码运行环境 系统:MacOS High Sierra Node.js:10.3.0 复制代码 Node.js是单线程的,那么Node.js启动后线程数是1 ...