linux 网络编程:epoll 的实例

http://blog.csdn.net/shanshanpt/article/details/7383400

在前面已经经过了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端:


[cpp] view plain copy print ?
  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 = sizeofstruct 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, sizeofstruct 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吧,一样的,呵呵:


[cpp] view plain copy print ?
  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. }  

编译运行:

 

你可能感兴趣的:(linux 网络编程:epoll 的实例)