epoll网络编程实例

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

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

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

[email protected]:~$ 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
copyprint?

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

对于client:

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

[cpp] view
plain
copyprint?

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

编译运行:

gcc -o server server.c

gcc -o client client.c

./server

./client

END

时间: 2024-11-01 13:34:03

epoll网络编程实例的相关文章

C# Socket网络编程实例

本文实例讲述了C# Socket网络编程技巧.分享给大家供大家参考.具体分析如下: 客户端要连接服务器:首先要知道服务器的IP地址.而服务器里有很多的应用程序,每一个应用程序对应一个端口号 所以客户端想要与服务器中的某个应用程序进行通信就必须要知道那个应用程序的所在服务器的IP地址,及应用程序所对应的端口号 TCP协议: 安全稳定,一般不会发生数据丢失,但是效率低.利用TCP发生数据一般经过3次握手(所有效率低,自己百度三次握手) UDP协议: 快速,效率高,但是不稳定,容易发生数据丢失(没有经

Python Socket,How to Create Socket Server? - 网络编程实例

文章出自:Python socket – network programming tutorial by Silver Moon 原创译文,如有版权问题请联系删除. Network programing in Python: Part2: Programing sockets servers. 在所有的通信实例中,都分为Client 和Server. 其中:Client是请求的发起点,Server是使用Socket接收传入的值并且提供返回数据. Server的职能如下: 1>.创建/打开一个so

朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型

做Linux网络开发,一般绕不开标题中几种网络编程模型.网上已有很多写的不错的分析文章,它们的基本论点是差不多的.但是我觉得他们讲的还不够详细,在一些关键论点上缺乏数据支持.所以我决定好好研究这几个模型.(转载请指明出于breaksoftware的csdn博客) 在研究这些模型前,我决定按如下步骤去做: 实现朴素模型 实现发请求的测试程序 实现Select模型,测试其效率 实现Poll模型,测试其效率 实现Epoll模型,测试其效率 分析各模型性能,分析和对比其源码 针对各模型特点,修改上述程序

Java学习之网络编程实例

转自:http://www.cnblogs.com/springcsc/archive/2009/12/03/1616413.html 多谢分享 网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习. 在学习网络编程以前,很多初学者可能觉得网络编程是比较复杂的系统工程,需要了解很多和网络相关的基础知识,其实这些都不是很必需的.首先来问一个问题:你 会打手机吗?很多人可能说肯定会啊,不就是按按电话号码

Linux IO多路复用之epoll网络编程及源码(转)

原文: 前言 本章节是用基本的Linux基本函数加上epoll调用编写一个完整的服务器和客户端例子,可在Linux上运行,客户端和服务端的功能如下: 客户端从标准输入读入一行,发送到服务端 服务端从网络读取一行,然后输出到客户端 客户端收到服务端的响应,输出这一行到标准输出 服务端代码 代码如下: #include <unistd.h> #include <sys/types.h> /* basic system data types */ #include <sys/soc

Go语言练习:网络编程实例——简易图片上传网站

1.代码结构 2.运行实例 1.代码结构 $ tree . ├── photoweb.go ├── public │   ├── css │   ├── images │   └── js ├── uploads └── views ├── list.html └── upload.html 1.1)photoweb.go 1 package main 2 3 import ( 4 "io" 5 "os" 6 "log" 7 "net/

网络编程实例代码

Makefile: all: gcc -o server server.c -lpthread gcc -o client client.c clean: rm server client server.c: /*TCP: server.c */ #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h&g

linux网络编程实例

获取服务器时间 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #

C语言Socket网络编程实例

转自:http://www.cnblogs.com/huxc/p/4272940.html 服务端: #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <winsock2.h> #pragma comment (lib, "ws2_32.lib") /* TCP服务端 */ int main(void) { int len = 0; WSADATA wd; int ret = 0; SOCKE