epoll监听文件_服务器端使用epoll监听大量并发链接

首先看一下epoll的几个函数的介绍。

1、epoll_create函数

/**

* @brief 该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存

* 放你想关注的socket fd上是否发生以及发生了什么事件。

* @param size: size就是你在这个epoll fd上能关注的最大socket fd数

* @return 生成的文件描述符*/

int epoll_create(int size);

2、epoll_ctl函数

/**

* @brief 该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删

* 除事件。

* @param epfd : 由 epoll_create 生成的epoll专用的文件描述符

* @param op : 要进行的操作例如注册事件,可能的取值

* EPOLL_CTL_ADD 注册

* EPOLL_CTL_MOD 修改

* EPOLL_CTL_DEL 删除

* @param fd : 关联的文件描述符

* @param event : 指向epoll_event的指针

* @return 0 if success, -1 if fail*/

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

其中用到的数据结构结构如下:

typedef union epoll_data

{void *ptr;intfd;

__uint32_t u32;

__uint64_t u64;

} epoll_data_t;structepoll_event

{

__uint32_t events;/*Epoll events*/epoll_data_t data;/*User data variable*/};

常用的事件类型:

EPOLLIN :表示对应的文件描述符可以读;

EPOLLOUT:表示对应的文件描述符可以写;

EPOLLPRI:表示对应的文件描述符有紧急的数据可读

EPOLLERR:表示对应的文件描述符发生错误;

EPOLLHUP:表示对应的文件描述符被挂断;

EPOLLET: 表示对应的文件描述符有事件发生;

例:

structepoll_event ev;//设置与要处理的事件相关的文件描述符

ev.data.fd=listenfd;//设置要处理的事件类型

ev.events=EPOLLIN|EPOLLET;//注册epoll事件

epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

3、epoll_wait函数

/**

* @brief 该函数用于轮询I/O事件的发生

* @param epfd : 由epoll_create 生成的epoll专用的文件描述符

* @param events : 用于回传代处理事件的数组

* @param maxevents : 每次能处理的事件数

* @param timeout : 等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可

* @return if >=0, 返回发生事件数, if -1, 错误*/

int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);

epoll_wait运行的原理:

等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。并且将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注这个socket fd的话,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类型。这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空。这一步非常重要。

好了,其实在epoll的使用中无非就用到了上面介绍的几个函数,下面贴一段用epoll实现的服务器代码:

服务器代码

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 #include

8 #include

9 #include

10 #include

11 #include

12 #include

13 #include

14 #include

15 #include

16 #include

17 #define MAXBUF 1024

18 #define MAXEPOLLSIZE 10000

19 /*

20 setnonblocking - 设置句柄为非阻塞方式21 */

22 int setnonblocking(intsockfd)23 {24 if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)25 {26 return -1;27 }28 return 0;29 }30 /*

31 handle_message - 处理每个 socket 上的消息收发32 */

33 int handle_message(intnew_fd)34 {35 char buf[MAXBUF + 1];36 intlen;37 /*开始处理每个新连接上的数据收发*/

38 bzero(buf, MAXBUF + 1);39 /*接收客户端的消息*/

40 len = recv(new_fd, buf, MAXBUF, 0);41 if (len > 0)42 {43 printf("%d接收消息成功:'%s',共%d个字节的数据\n",44 new_fd, buf, len);45 }46 else

47 {48 if (len < 0)49 printf50 ("消息接收失败!错误代码是%d,错误信息是'%s'\n",51 errno, strerror(errno));52 close(new_fd);53 return -1;54 }55 /*处理每个新连接上的数据收发结束*/

56 returnlen;57 }58 int main(int argc, char **argv)59 {60 intlistener, new_fd, kdpfd, nfds, n, ret, curfds;61 socklen_t len;62 structsockaddr_in my_addr, their_addr;63 unsigned intmyport, lisnum;64 structepoll_event ev;65 structepoll_event events[MAXEPOLLSIZE];66 structrlimit rt;67 myport = 5000;68 lisnum = 2;69 /*设置每个进程允许打开的最大文件数*/

70 rt.rlim_max = rt.rlim_cur =MAXEPOLLSIZE;71 if (setrlimit(RLIMIT_NOFILE, &rt) == -1)72 {73 perror("setrlimit");74 exit(1);75 }76 else

77 {78 printf("设置系统资源参数成功!\n");79 }80 /*开启 socket 监听*/

81 if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)82 {83 perror("socket");84 exit(1);85 }86 else

87 {88 printf("socket 创建成功!\n");89 }90 setnonblocking(listener);91 bzero(&my_addr, sizeof(my_addr));92 my_addr.sin_family =PF_INET;93 my_addr.sin_port =htons(myport);94 my_addr.sin_addr.s_addr =INADDR_ANY;95 if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)96 {97 perror("bind");98 exit(1);99 }100 else

101 {102 printf("IP 地址和端口绑定成功\n");103 }104 if (listen(listener, lisnum) == -1)105 {106 perror("listen");107 exit(1);108 }109 else

110 {111 printf("开启服务成功!\n");112 }113 /*创建 epoll 句柄,把监听 socket 加入到 epoll 集合里*/

114 kdpfd =epoll_create(MAXEPOLLSIZE);115 len = sizeof(structsockaddr_in);116 ev.events = EPOLLIN |EPOLLET;117 ev.data.fd =listener;118 if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0)119 {120 fprintf(stderr, "epoll set insertion error: fd=%d\n", listener);121 return -1;122 }123 else

124 {125 printf("监听 socket 加入 epoll 成功!\n");126 }127 curfds = 1;128 while (1)129 {130 /*等待有事件发生*/

131 nfds = epoll_wait(kdpfd, events, curfds, -1);132 if (nfds == -1)133 {134 perror("epoll_wait");135 break;136 }137 /*处理所有事件*/

138 for (n = 0; n < nfds; ++n)139 {140 if (events[n].data.fd ==listener)141 {142 new_fd = accept(listener, (struct sockaddr *) &their_addr,&len);143 if (new_fd < 0)144 {145 perror("accept");146 continue;147 }148 else

149 {150 printf("有连接来自于: %d:%d, 分配的 socket 为:%d\n",151 inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);152 }153 setnonblocking(new_fd);154 ev.events = EPOLLIN |EPOLLET;155 ev.data.fd =new_fd;156 if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_fd, &ev) < 0)157 {158 fprintf(stderr, "把 socket '%d' 加入 epoll 失败!%s\n",159 new_fd, strerror(errno));160 return -1;161 }162 curfds++;163 }164 else

165 {166 ret =handle_message(events[n].data.fd);167 if (ret < 1 && errno != 11)168 {169 epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd,&ev);170 curfds--;171 }172 }173 }174 }175 close(listener);176 return 0;177 }

epoll用到的所有函数都是在头文件sys/epoll.h中声明,有什么地方不明白或函数忘记了可以去看一下。

epoll和select相比,最大不同在于:

epoll返回时已经明确的知道哪个socket fd发生了事件,不用再一个个比对。这样就提高了效率。在select 中,要去判断每个socket, 通过if(FD_ISSET(socket, &集合));在 epoll 中,for(inti=0; i

select的FD_SETSIZE是有限止的,而epoll是没有限止的只与系统资源有关。

注:本文选自http://www.cnblogs.com/cipc/articles/2431042.html,

如有侵犯您的权益,请邮件通知我,我将会在收到通知后尽快删除相关内容。

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页