epoll网络编程实例

标签: epoll 网络 编程 | 发表时间:2015-04-23 14:46 | 作者:lmh12506
分享到:
出处:http://blog.csdn.net

       在前面已经经过了PPC、TPC、select之类( TPC就是使用进程处理data,TPC就是使用线程处理 ),前面两个的缺点大家应该都是知道的是吧,对于select( 其实poll和他差不多 ),缺点是能同时连接的fd是在是不多,在linux中一般是1024/2048,对于很大的服务器来说是不够的!当然我们可以自己修改其值!但是效率上就会下降!

       对于改进poll的epoll来说:支持一个进程打开大数目的socket描述符,也就是说与本机的内存是有关系的!( 一般服务器的都是很大的! )

       下面是我的小PC机上的显示:

       pt@ubuntu:~$ cat /proc/sys/fs/file-max
       391658
       达到了391658个,那么对于服务器而言,显然,嘿嘿嘿~~~

      epoll的基础知识吧大家在网上到处都能找到,不就是epoll_creat 、epoll_ctl、epoll_wait 3函数!大家自己搜去,我也是在学习。。。

      此处主要是贴上自己的测试的一些垃圾代码,与大家共勉!呵呵呵~

     哦,忘了要注意一下:

     epoll_ctl epoll的事件注册函数,其events参数可以是以下宏的集合:
     EPOLLIN:    表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
     EPOLLOUT:   表示对应的文件描述符可以写;
     EPOLLPRI:   表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
     EPOLLERR:   表示对应的文件描述符发生错误;写已关闭socket pipe broken
     EPOLLHUP:   表示对应的文件描述符被挂断;譬如收到RST包。在注册事件的时候这个事件是默认添加。  
     EPOLLRDHUP: 表示对应的文件描述符对端socket关闭事件,主要应用于ET模式下。 
    在水平触发模式下,如果对端socket关闭,则会一直触发epollin事件,驱动去处理client socket。
    在边沿触发模式下,如果client首先发送协议然后shutdown写端。则会触发epollin事件。但是如果处理程序只进行一次recv操作时,根据recv收取到得数据长度来判读后边是  

    否还有需要处理的协议时,将丢失客户端关闭事件。
     EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
    EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里


server端:


  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <sys/types.h>  
  6. #include <errno.h>  
  7. #include <sys/socket.h>  
  8. #include <netinet/in.h>           /* socket类定义需要*/  
  9. #include <sys/epoll.h>            /* epoll头文件 */  
  10. #include <fcntl.h>                    /* nonblocking需要 */  
  11. #include <sys/resource.h>     /* 设置最大的连接数需要setrlimit */  
  12.   
  13. #define MAXEPOLL    10000   /* 对于服务器来说,这个值可以很大的! */  
  14. #define MAXLINE     1024  
  15. #define     PORT            6000  
  16. #define MAXBACK 1000  
  17.   
  18. //!> 设置非阻塞  
  19. //!>   
  20. int setnonblocking( int fd )  
  21. {  
  22.     if( fcntl( fd, F_SETFL, fcntl( fd, F_GETFD, 0 )|O_NONBLOCK ) == -1 )  
  23.     {  
  24.         printf("Set blocking error : %d\n", errno);  
  25.         return -1;  
  26.     }  
  27.     return 0;  
  28. }  
  29.   
  30. int main( int argc, char ** argv )  
  31. {  
  32.     int         listen_fd;  
  33.     int         conn_fd;  
  34.     int         epoll_fd;  
  35.     int         nread;  
  36.     int         cur_fds;                //!> 当前已经存在的数量  
  37.     int         wait_fds;               //!> epoll_wait 的返回值  
  38.     int     i;  
  39.     struct sockaddr_in servaddr;  
  40.     struct sockaddr_in cliaddr;  
  41.     struct  epoll_event ev;  
  42.     struct  epoll_event evs[MAXEPOLL];  
  43.     struct  rlimit  rlt;        //!> 设置连接数所需  
  44.     char    buf[MAXLINE];  
  45.     socklen_t   len = sizeof( struct sockaddr_in );  
  46.   
  47.     //!> 设置每个进程允许打开的最大文件数  
  48.     //!> 每个主机是不一样的哦,一般服务器应该很大吧!  
  49.     //!>   
  50.     rlt.rlim_max = rlt.rlim_cur = MAXEPOLL;  
  51.     if( setrlimit( RLIMIT_NOFILE, &rlt ) == -1 )      
  52.     {  
  53.         printf("Setrlimit Error : %d\n", errno);  
  54.         exit( EXIT_FAILURE );  
  55.     }  
  56.       
  57.     //!> server 套接口  
  58.     //!>   
  59.     bzero( &servaddr, sizeof( servaddr ) );  
  60.     servaddr.sin_family = AF_INET;  
  61.     servaddr.sin_addr.s_addr = htonl( INADDR_ANY );  
  62.     servaddr.sin_port = htons( PORT );  
  63.       
  64.     //!> 建立套接字  
  65.     if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )  
  66.     {  
  67.         printf("Socket Error...\n" , errno );  
  68.         exit( EXIT_FAILURE );  
  69.     }  
  70.       
  71.     //!> 设置非阻塞模式  
  72.     //!>   
  73.     if( setnonblocking( listen_fd ) == -1 )  
  74.     {  
  75.         printf("Setnonblocking Error : %d\n", errno);  
  76.         exit( EXIT_FAILURE );  
  77.     }  
  78.       
  79.     //!> 绑定  
  80.     //!>  
  81.     if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( struct sockaddr ) ) == -1 )  
  82.     {  
  83.         printf("Bind Error : %d\n", errno);  
  84.         exit( EXIT_FAILURE );  
  85.     }  
  86.   
  87.     //!> 监听  
  88.     //!>   
  89.     if( listen( listen_fd, MAXBACK ) == -1 )  
  90.     {  
  91.         printf("Listen Error : %d\n", errno);  
  92.         exit( EXIT_FAILURE );  
  93.     }  
  94.       
  95.     //!> 创建epoll  
  96.     //!>   
  97.     epoll_fd = epoll_create( MAXEPOLL );    //!> create  
  98.     ev.events = EPOLLIN | EPOLLET;      //!> accept Read!  
  99.     ev.data.fd = listen_fd;                 //!> 将listen_fd 加入  
  100.     if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev ) < 0 )  
  101.     {  
  102.         printf("Epoll Error : %d\n", errno);  
  103.         exit( EXIT_FAILURE );  
  104.     }  
  105.     cur_fds = 1;  
  106.       
  107.     while( 1 )  
  108.     {  
  109.         if( ( wait_fds = epoll_wait( epoll_fd, evs, cur_fds, -1 ) ) == -1 )  
  110.         {  
  111.             printf( "Epoll Wait Error : %d\n", errno );  
  112.             exit( EXIT_FAILURE );  
  113.         }  
  114.   
  115.         for( i = 0; i < wait_fds; i++ )  
  116.         {  
  117.             if( evs[i].data.fd == listen_fd && cur_fds < MAXEPOLL )    
  118.                                                     //!> if是监听端口有事  
  119.             {  
  120.                 if( ( conn_fd = accept( listen_fd, (struct sockaddr *)&cliaddr, &len ) ) == -1 )  
  121.                 {  
  122.                     printf("Accept Error : %d\n", errno);  
  123.                     exit( EXIT_FAILURE );  
  124.                 }  
  125.                   
  126.                 printf( "Server get from client !\n"/*,  inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port */);  
  127.                   
  128.                 ev.events = EPOLLIN | EPOLLET;      //!> accept Read!  
  129.                 ev.data.fd = conn_fd;                   //!> 将conn_fd 加入  
  130.                 if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev ) < 0 )  
  131.                 {  
  132.                     printf("Epoll Error : %d\n", errno);  
  133.                     exit( EXIT_FAILURE );  
  134.                 }  
  135.                 ++cur_fds;   
  136.                 continue;         
  137.             }  
  138.               
  139.             //!> 下面处理数据  
  140.             //!>   
  141.             nread = read( evs[i].data.fd, buf, sizeof( buf ) );  
  142.             if( nread <= 0 )                     //!> 结束后者出错  
  143.             {  
  144.                 close( evs[i].data.fd );  
  145.                 epoll_ctl( epoll_fd, EPOLL_CTL_DEL, evs[i].data.fd, &ev );  //!> 删除计入的fd  
  146.                 --cur_fds;                  //!> 减少一个呗!  
  147.                 continue;  
  148.             }  
  149.               
  150.             write( evs[i].data.fd, buf, nread );            //!> 回写  
  151.               
  152.         }  
  153.     }  
  154.       
  155.     close( listen_fd );  
  156.     return 0;  
  157. }  


对于client:

由于本人比较懒,所以就使用上一次的select的client吧,一样的,呵呵:


  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <errno.h>  
  6. #include <netinet/in.h>  
  7. #include <sys/types.h>  
  8. #include <sys/socket.h>  
  9. #include  <arpa/inet.h>  
  10. #include <sys/select.h>  
  11.   
  12. #define MAXLINE 1024  
  13. #define SERV_PORT 6000  
  14.   
  15. //!> 注意输入是由stdin,接受是由server发送过来  
  16. //!> 所以在client端也是需要select进行处理的  
  17. void send_and_recv( int connfd )  
  18. {  
  19.     FILE * fp = stdin;  
  20.     int   lens;  
  21.     char send[MAXLINE];  
  22.     char recv[MAXLINE];  
  23.     fd_set rset;  
  24.     FD_ZERO( &rset );  
  25.     int maxfd = ( fileno( fp ) > connfd ? fileno( fp ) : connfd  + 1 );    
  26.                                     //!> 输入和输出的最大值  
  27.     int n;  
  28.       
  29.     while( 1 )  
  30.     {  
  31.         FD_SET( fileno( fp ), &rset );  
  32.         FD_SET( connfd, &rset );            //!> 注意不要把rset看作是简单的一个变量  
  33.                                 //!> 注意它其实是可以包含一组套接字的哦,  
  34.                                 //!> 相当于是封装的数组!每次都要是新的哦!  
  35.           
  36.         if( select( maxfd, &rset, NULL, NULL, NULL ) == -1 )  
  37.         {  
  38.             printf("Client Select Error..\n");  
  39.             exit(EXIT_FAILURE  );  
  40.         }  
  41.           
  42.         //!> if 连接口有信息  
  43.         if( FD_ISSET( connfd, &rset ) ) //!> if 连接端口有信息  
  44.         {  
  45.             printf( "client get from server ...\n" );  
  46.             memset( recv, 0, sizeof( recv ) );  
  47.             n = read( connfd, recv, MAXLINE );  
  48.             if( n == 0 )  
  49.             {  
  50.                 printf("Recv ok...\n");  
  51.                 break;  
  52.             }  
  53.             else if( n == -1 )  
  54.             {  
  55.                 printf("Recv error...\n");  
  56.                 break;  
  57.             }  
  58.             else  
  59.             {  
  60.                 lens = strlen( recv );  
  61.                 recv[lens] = '\0';  
  62.                 //!> 写到stdout  
  63.                 write( STDOUT_FILENO, recv, MAXLINE );  
  64.                 printf("\n");  
  65.             }  
  66.   
  67.         }  
  68.           
  69.         //!> if 有stdin输入  
  70.         if( FD_ISSET( fileno( fp ), &rset ) )   //!> if 有输入  
  71.         {  
  72.             //!> printf("client stdin ...\n");  
  73.               
  74.             memset( send, 0, sizeof( send ) );  
  75.             if( fgets( send, MAXLINE, fp ) == NULL )  
  76.             {  
  77.                 printf("End...\n");  
  78.                 exit( EXIT_FAILURE );  
  79.             }  
  80.             else  
  81.             {  
  82.                 //!>if( str )  
  83.                 lens = strlen( send );  
  84.                 send[lens-1] = '\0';        //!> 减一的原因是不要回车字符  
  85.                                 //!> 经验值:这一步非常重要的哦!!!!!!!!  
  86.                 if( strcmp( send, "q" ) == 0 )  
  87.                 {  
  88.                     printf( "Bye..\n" );  
  89.                     return;  
  90.                 }  
  91.                   
  92.                 printf("Client send : %s\n", send);  
  93.                 write( connfd, send, strlen( send ) );  
  94.             }  
  95.         }  
  96.           
  97.     }  
  98.       
  99. }  
  100.   
  101. int main( int argc, char ** argv )  
  102. {  
  103.     //!> char * SERV_IP = "10.30.97.188";  
  104.     char    buf[MAXLINE];  
  105.     int     connfd;  
  106.     struct sockaddr_in servaddr;  
  107.       
  108.     if( argc != 2 )  
  109.     {  
  110.         printf("Input server ip !\n");  
  111.         exit( EXIT_FAILURE );  
  112.     }  
  113.       
  114.     //!> 建立套接字  
  115.     if( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )  
  116.     {  
  117.         printf("Socket Error...\n" , errno );  
  118.         exit( EXIT_FAILURE );  
  119.     }  
  120.   
  121.     //!> 套接字信息  
  122.     bzero(&servaddr, sizeof(servaddr));  
  123.     servaddr.sin_family = AF_INET;  
  124.     servaddr.sin_port = htons(SERV_PORT);  
  125.     inet_pton(AF_INET, argv[1], &servaddr.sin_addr);  
  126.       
  127.     //!> 链接server  
  128.     if( connect( connfd, ( struct sockaddr *  )&servaddr, sizeof( servaddr ) ) < 0 )  
  129.     {  
  130.         printf("Connect error..\n");  
  131.         exit(EXIT_FAILURE);  
  132.     }     
  133.     /*else 
  134.     { 
  135.         printf("Connet ok..\n"); 
  136.     }*/  
  137.   
  138.     //!>  
  139.     //!> send and recv  
  140.     send_and_recv( connfd );  
  141.       
  142.     //!>   
  143.   
  144.     close( connfd );  
  145.     printf("Exit\n");  
  146.       
  147.     return 0;  
  148. }  

编译运行:

gcc -o server server.c

gcc -o client client.c


./server

./client


END

作者:lmh12506 发表于2015/4/23 14:46:20 原文链接
阅读:84 评论:0 查看评论

相关 [epoll 网络 编程] 推荐:

epoll网络编程实例

- - CSDN博客推荐文章
       在前面已经经过了PPC、TPC、select之类( TPC就是使用进程处理data,TPC就是使用线程处理 ),前面两个的缺点大家应该都是知道的是吧,对于select( 其实poll和他差不多 ),缺点是能同时连接的fd是在是不多,在linux中一般是1024/2048,对于很大的服务器来说是不够的.

poll,select与epoll

- - 操作系统 - ITeye博客
select的参数类型fd_set没有将文件描述符和事件绑定,它仅仅是一个文件描述符集合,因此select需要提供3个这种类型的参数来分别传入和输出可读,可写及异常等事件.这一方面使得select不能处理更多类型的事件,另一方面由于内核对fd_set集合的在线修改,应用程序下次调用select前不得不重置这3个fd_set集合.

EPOLL下的accept(转载)

- chuang - C++博客-首页原创精华区
     摘要: (转载者注:看完这个,再回头看看nginx源码,发现它在accept时用的是LT模式,read,write时是ET模式)不知道是谁第一个犯了错,在网上贴出所谓epoll通用框架的代码. 注意看accpet的处理:1epfd = epoll_create(10);2 3struct sockaddr_in clientaddr;4struct sockaddr_in se...  阅读全文.

linux kernel中epoll的设计和实现

- 神气 - pagefault
原创文章,转载请注明: 转载自pagefault. 本文链接地址: linux kernel中epoll的设计和实现. 这里就不贴源码了,源码分析的话,网上一大堆,我这里只是简要的描述下epoll的实现和一些关键的代码片段. 相关的文件在 fs/eventpoll.c中,我看的是2.6.38的内核代码..

select、poll、epoll之间的区别总结

- - 企业架构 - ITeye博客
select、poll、epoll之间的区别总结. select,poll,epoll都是IO多路复用的机制. I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. 但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间.

使用epoll 在 linux 上开发高性能应用服务器

- - C++博客-首页原创精华区
epoll是linux提供一种多路复用的技术,类似各个平台都支持的select,只是epoll在内核的实现做了更多地优化,可以支持比select更多的文件描述符,当然也支持 socket这种网络的文件描述符. linux上的大并发的接入服务器,目前的实现方式肯定都通过epoll实现. 有很多开发人员用epoll的时候,会开多个线程来进行数据通信,比如一个线程专门accept(我个人早些年在FreeBSD用kqueue的时候,由于对内部机制没有基本了解也这样搞),一个线程收发,或者接收和发送都用各自独立的线程.

epoll机制在搜索引擎spider中的应用

- - 海之沙
本文将介绍epoll的概念,原理, 优点,及使用接口,同时结合作者在搜索引擎spider开发中epoll使用方式的代码向大家具体介绍epoll的使用方式. 笔者08年曾有使用epoll编写未考虑压力控制的crawler,将国内著名票务网站压垮并在boss的带领下登门道歉的经历:) 足见epoll的强悍!.

[转]我读过最好的Epoll模型讲解

- - 芒果先生Mango的专栏
首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象.     不管是文件,还是套接字,还是管道,我们都可以把他们看作流.     之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据;通过write,我们可以往流写入数据. 现在假定一个情形,我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读如数据,但是服务器还没有把数据传回来),这时候该怎么办.

epoll 或者 kqueue 的原理(摘自知乎蓝形参)

- - Linux - 操作系统 - ITeye博客
首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流. 之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据;通过write,我们可以往流写入数据. 现在假定一个情形,我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读如数据,但是服务器还没有把数据传回来),这时候该怎么办.

linux下epoll模式和select模式的区别

- - Linux - 操作系统 - ITeye博客
        支持高并发连接. 官方测试的是5w并发连接但在实际生产中可制成2-4w并发连接数,得益于nginx使用最新的epoll(linux 2.6内核)和kqueue(freebsd)网络I/O模型. 而apache使用的则是传统的select模型,其比较稳定的prefork模式为多进程模式,需要经常派生子进程,所消耗的CPU等服务器资源要比nginx高的多.