linux 网络编程:epoll 的实例

来源:互联网 发布:示知的意思 编辑:程序博客网 时间:2024/05/07 17:05

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


#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <errno.h>#include <sys/socket.h>#include <netinet/in.h>/* socket类定义需要*/#include <sys/epoll.h>/* epoll头文件 */#include <fcntl.h>                /* nonblocking需要 */#include <sys/resource.h>/* 设置最大的连接数需要setrlimit */#defineMAXEPOLL10000/* 对于服务器来说,这个值可以很大的! */#defineMAXLINE1024#define PORT6000#defineMAXBACK1000//!> 设置非阻塞//!> int setnonblocking( int fd ){if( fcntl( fd, F_SETFL, fcntl( fd, F_GETFD, 0 )|O_NONBLOCK ) == -1 ){printf("Set blocking error : %d\n", errno);return -1;}return 0;}int main( int argc, char ** argv ){int listen_fd;int conn_fd;int epoll_fd;int nread;int cur_fds;//!> 当前已经存在的数量int wait_fds;//!> epoll_wait 的返回值inti;struct sockaddr_in servaddr;struct sockaddr_in cliaddr;struct epoll_eventev;struct epoll_eventevs[MAXEPOLL];struct rlimitrlt;//!> 设置连接数所需char buf[MAXLINE];socklen_tlen = sizeof( struct sockaddr_in );//!> 设置每个进程允许打开的最大文件数//!> 每个主机是不一样的哦,一般服务器应该很大吧!//!> rlt.rlim_max = rlt.rlim_cur = MAXEPOLL;if( setrlimit( RLIMIT_NOFILE, &rlt ) == -1 ){printf("Setrlimit Error : %d\n", errno);exit( EXIT_FAILURE );}//!> server 套接口//!> bzero( &servaddr, sizeof( servaddr ) );servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl( INADDR_ANY );servaddr.sin_port = htons( PORT );//!> 建立套接字if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ){printf("Socket Error...\n" , errno );exit( EXIT_FAILURE );}//!> 设置非阻塞模式//!> if( setnonblocking( listen_fd ) == -1 ){printf("Setnonblocking Error : %d\n", errno);exit( EXIT_FAILURE );}//!> 绑定//!>if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( struct sockaddr ) ) == -1 ){printf("Bind Error : %d\n", errno);exit( EXIT_FAILURE );}//!> 监听//!> if( listen( listen_fd, MAXBACK ) == -1 ){printf("Listen Error : %d\n", errno);exit( EXIT_FAILURE );}//!> 创建epoll//!> epoll_fd = epoll_create( MAXEPOLL );//!> createev.events = EPOLLIN | EPOLLET;//!> accept Read!ev.data.fd = listen_fd;//!> 将listen_fd 加入if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev ) < 0 ){printf("Epoll Error : %d\n", errno);exit( EXIT_FAILURE );}cur_fds = 1;while( 1 ){if( ( wait_fds = epoll_wait( epoll_fd, evs, cur_fds, -1 ) ) == -1 ){printf( "Epoll Wait Error : %d\n", errno );exit( EXIT_FAILURE );}for( i = 0; i < wait_fds; i++ ){if( evs[i].data.fd == listen_fd && cur_fds < MAXEPOLL )//!> if是监听端口有事{if( ( conn_fd = accept( listen_fd, (struct sockaddr *)&cliaddr, &len ) ) == -1 ){printf("Accept Error : %d\n", errno);exit( EXIT_FAILURE );}printf( "Server get from client !\n"/*,  inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port */);ev.events = EPOLLIN | EPOLLET;//!> accept Read!ev.data.fd = conn_fd;//!> 将conn_fd 加入if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev ) < 0 ){printf("Epoll Error : %d\n", errno);exit( EXIT_FAILURE );}++cur_fds; continue;}//!> 下面处理数据//!> nread = read( evs[i].data.fd, buf, sizeof( buf ) );if( nread <= 0 )//!> 结束后者出错{close( evs[i].data.fd );epoll_ctl( epoll_fd, EPOLL_CTL_DEL, evs[i].data.fd, &ev );//!> 删除计入的fd--cur_fds;//!> 减少一个呗!continue;}write( evs[i].data.fd, buf, nread );//!> 回写}}close( listen_fd );return 0;}


对于client:

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


#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <netinet/in.h>#include <sys/types.h>#include <sys/socket.h>#include  <arpa/inet.h>#include <sys/select.h>#define MAXLINE 1024#define SERV_PORT 6000//!> 注意输入是由stdin,接受是由server发送过来//!> 所以在client端也是需要select进行处理的void send_and_recv( int connfd ){FILE * fp = stdin;int   lens;char send[MAXLINE];char recv[MAXLINE];fd_set rset;FD_ZERO( &rset );int maxfd = ( fileno( fp ) > connfd ? fileno( fp ) : connfd  + 1 );        //!> 输入和输出的最大值int n;while( 1 ){FD_SET( fileno( fp ), &rset );FD_SET( connfd, &rset );//!> 注意不要把rset看作是简单的一个变量//!> 注意它其实是可以包含一组套接字的哦,//!> 相当于是封装的数组!每次都要是新的哦!if( select( maxfd, &rset, NULL, NULL, NULL ) == -1 ){printf("Client Select Error..\n");exit(EXIT_FAILURE  );}//!> if 连接口有信息if( FD_ISSET( connfd, &rset ) )//!> if 连接端口有信息{printf( "client get from server ...\n" );memset( recv, 0, sizeof( recv ) );n = read( connfd, recv, MAXLINE );if( n == 0 ){printf("Recv ok...\n");break;}else if( n == -1 ){printf("Recv error...\n");break;}else{lens = strlen( recv );recv[lens] = '\0';//!> 写到stdoutwrite( STDOUT_FILENO, recv, MAXLINE );printf("\n");}}//!> if 有stdin输入if( FD_ISSET( fileno( fp ), &rset ) )//!> if 有输入{//!> printf("client stdin ...\n");memset( send, 0, sizeof( send ) );if( fgets( send, MAXLINE, fp ) == NULL ){printf("End...\n");exit( EXIT_FAILURE );}else{//!>if( str )lens = strlen( send );send[lens-1] = '\0';//!> 减一的原因是不要回车字符//!> 经验值:这一步非常重要的哦!!!!!!!!if( strcmp( send, "q" ) == 0 ){printf( "Bye..\n" );return;}printf("Client send : %s\n", send);write( connfd, send, strlen( send ) );}}}}int main( int argc, char ** argv ){//!> char * SERV_IP = "10.30.97.188";char buf[MAXLINE];int   connfd;struct sockaddr_in servaddr;if( argc != 2 ){printf("Input server ip !\n");exit( EXIT_FAILURE );}//!> 建立套接字if( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ){printf("Socket Error...\n" , errno );exit( EXIT_FAILURE );}//!> 套接字信息bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(SERV_PORT);    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);//!> 链接serverif( connect( connfd, ( struct sockaddr *  )&servaddr, sizeof( servaddr ) ) < 0 ){printf("Connect error..\n");exit(EXIT_FAILURE);}/*else{printf("Connet ok..\n");}*///!>//!> send and recvsend_and_recv( connfd );//!> close( connfd );printf("Exit\n");return 0;}

编译运行:

gcc -o server server.c

gcc -o client client.c


./server

./client


END

原创粉丝点击