3高并发server:多路IO之epoll
1 epoll
epoll是Linux下多路复用IO接口select/poll的增强版本号,它能显著提高程序在大量并、发连接中仅仅有少量活跃的情况下的系统CPU利用率,由于它会复用文件描写叙述符集合来传递结果而不用迫使开发人员每次等待事件之前都必须又一次准备要被侦听的文件描写叙述符集合,还有一点原因就是获取事件的时候,它无须遍历整个被侦听的描写叙述符集,仅仅要遍历那些被内核IO事件异步唤醒而增加Ready队列的描写叙述符集合即可了。
眼下epell是linux大规模并发网络程序中的热门首选模型。
epoll除了提供select/ poll那种IO事件的电平触发(Level
Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,降低epoll_wait/epoll_pwait的调用,提高应用程序效率。
一个进程打开大数目的socket描写叙述符
cat/proc/sys/fs/file-max
设置最大打开文件描写叙述符限制
sudo vi /etc/security/limits.conf
epollAPI
1.创建一个epoll句柄,參数size用来告诉内核监听的文件描写叙述符个数,跟内存大小有关。
依赖的头文件
#include <sys/epoll.h>
函数声明
int epoll_create(int size);
函数说明:
size:告诉内核监听的数目
2
控制某个epoll监听的文件描写叙述符上的事件:注冊、改动、删除
依赖的头文件
#include <sys/epoll.h>
函数声明
int epoll_ctl(int epfd,int op,int fd,structepoll_event);
函数说明:
epfd:为epoll_create的句柄
op:表示动作,用3个宏来表示
EPOLL_CTL_ADD(注冊新的fd到epfd)
EPOLL_CTL_MOD(改动已经注冊的fd的监听事件)
EPOLL_CTL_DEL(从epfd删除一个fd)
event:告诉内核须要监听的事件
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
EPOLLIN
:表示相应的文件描写叙述符能够读(包含对端SOCKET正常关闭)
EPOLLOUT:表示相应的文件描写叙述符能够写
EPOLLPRI:表示相应的文件描写叙述符有紧急的数据可读(这里应该表示有带外数据到来)
EPOLLERR:表示相应的文件描写叙述符错误发生
EPOLLHUP:表示相应的文件描写叙述符被挂断;
EPOLLET:将EPOLL设为边缘触发(Edge
Triggered)模式,这是相对于水平触发(Level Triggered)来说的
EPOLLONESHOT:仅仅监听一次事件,当监听完这次事件之后,假设还须要继续监听这个socket的话,须要再次把这个socket增加到EPOLL队列里
3
等待所监控文件描写叙述符上有事件的产生,类似select()调用。
依赖的头文件
#include <sys/epoll.h>
函数声明:
int epoll_wait(int epfd, struct epoll_event*events, int maxevents, int timeout);
參数介绍:
events:用来从内核得到事件的集合。
maxevents:告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,timeout:是超时时间
-1:堵塞
0:马上返回,非堵塞
>0:指定微秒
返回值:成功返回有多少文件描写叙述符就绪,时间到时返回0,出错返回-1
案例说明:
server.c
|
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/epoll.h> #include<errno.h> #include<unistd.h> #include<ctype.h> #include"wrap.h" #define MAXLINE 80 #define SERV_PORT 8000 #define OPEN_MAX 1024 int main(void) { int i,j,maxi,listenfd,connfd,sockfd; int nready,efd,res; ssize_t n; char buf[MAXLINE],str[INET_ADDRSTRLEN]; socklen_t clilen; int client[OPEN_MAX]; struct sockaddr_in cliaddr,servaddr; //ep[OPEN_MAX]保存就绪的文件描写叙述符 struct epoll_event tep,ep[OPEN_MAX]; listenfd = Socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); Listen(listenfd,20); for(i = 0;i < OPEN_MAX;i++) { client[i] = -1; } maxi = -1; efd = epoll_create(OPEN_MAX); if(efd == -1) perr_exit("epoll_create"); //监听读属性 tep.events = EPOLLIN; //data里面保存了就绪的文件的文件描写叙述符。 tep.data.fd = listenfd; res = epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep); if(res == -1) perr_exit("epoll_ctl"); for(;;) { /*堵塞监听*/ nready = epoll_wait(efd,ep,OPEN_MAX,-1); if(nready == -1) { perr_exit("epoll_wait"); } for(i = 0;i< nready;i++) { if(!ep[i].events & EPOLLIN) continue; if(ep[i].data.fd == listenfd) { clilen = sizeof(cliaddr); connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&clilen); printf("received from %s at PORT %d\n", inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)), ntohs(cliaddr.sin_port)); for(j = 0; j < OPEN_MAX; j++) { if(client[j] < 0) { client[j] = connfd; /*save descriptor*/ break; } } if(j==OPEN_MAX) perr_exit("too many clients"); if(j > maxi) maxi = j; /*max index in client[] array*/ tep.events = EPOLLIN; tep.data.fd = connfd; res = epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep); if(res == -1) { perr_exit("epoll_ctl"); } else { sockfd = ep[i].data.fd; n = Read(sockfd,buf,MAXLINE); if(n==0) { for(j = 0; j <= maxi;j++) { if(client[j] == sockfd) { client[j] = -1; break; } } res = epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL); if(res == -1) { perr_exit("epoll_ctl"); } Close(sockfd); printf("client[%d] closed connection",j); } else { for(j = 0;j < n;j++) buf[j] = toupper(buf[j]); Writen(sockfd,buf,n); } } } } } Close(listenfd); Close(efd); return 0; } |
client.c
|
#include<stdio.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<netinet/in.h> #include"wrap.h" #define MAXLINE 80 #define SERV_PORT 8000 int main(void) { struct sockaddr_in servaddr; char buf[MAXLINE]; int sockfd,n; sockfd = Socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr); servaddr.sin_port = htons(SERV_PORT); Connect(sockfd,(struct sockaddr *) &servaddr,sizeof(servaddr)); while(fgets(buf,MAXLINE,stdin) != NULL) { Write(sockfd,buf,strlen(buf)); n = Read(sockfd,buf,MAXLINE); if(n == 0) printf("the other side has been closed.\n"); else Write(STDOUT_FILENO,buf,n); } Close(sockfd); return 0; } |
wrap.h
|
#ifndef __WRAP_H_ #define __WRAP_H_ void perr_exit(const char *s); int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); void Bind(int fd, const struct sockaddr *sa, socklen_t salen); void Connect(int fd, const struct sockaddr *sa, socklen_t salen); void Listen(int fd, int backlog); int Socket(int family, int type, int protocol); ssize_t Read(int fd, void *ptr, size_t nbytes); ssize_t Write(int fd, const void *ptr, size_t nbytes); void Close(int fd); ssize_t Readn(int fd, void *vptr, size_t n); ssize_t Writen(int fd, const void *vptr, size_t n); static ssize_t my_read(int fd, char *ptr); ssize_t Readline(int fd, void *vptr, size_t maxlen); #endif |
wrap.c
|
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <sys/socket.h> void perr_exit(const char *s) { perror(s); exit(1); } int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) { int n; again: if ( (n = accept(fd, sa, salenptr)) < 0) { if ((errno == ECONNABORTED) || (errno == EINTR)) goto again; else perr_exit("accept error"); } return n; } void Bind(int fd, const struct sockaddr *sa, socklen_t salen) { if (bind(fd, sa, salen) < 0) perr_exit("bind error"); } void Connect(int fd, const struct sockaddr *sa, socklen_t salen) { if (connect(fd, sa, salen) < 0) perr_exit("connect error"); } void Listen(int fd, int backlog) { if (listen(fd, backlog) < 0) perr_exit("listen error"); } int Socket(int family, int type, int protocol) { int n; if ( (n = socket(family, type, protocol)) < 0) perr_exit("socket error"); return n; } ssize_t Read(int fd, void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = read(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } ssize_t Write(int fd, const void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = write(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } void Close(int fd) { if (close(fd) == -1) perr_exit("close error"); } ssize_t Readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; else return -1; } else if (nread == 0) break; nleft -= nread; ptr += nread; } return n - nleft; } ssize_t Writen(int fd,const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return n; } static ssize_t my_read(int fd, char *ptr) { static int read_cnt; static char *read_ptr; static char read_buf[100]; if (read_cnt <= 0) { again: if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { if (errno == EINTR) goto again; return -1; } else if (read_cnt == 0) return 0; read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return 1; } ssize_t Readline(int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; for (n = 1; n < maxlen; n++) { if ( (rc = my_read(fd, &c)) == 1) { *ptr++ = c; if (c == '\n') break; } else if (rc == 0) { *ptr = 0; return n - 1; } else return -1; } *ptr = 0; return n; } |
3高并发server:多路IO之epoll的更多相关文章
- Linux C++ 网络编程学习系列(5)——多路IO之epoll边沿触发
多路IO之epoll边沿触发+非阻塞 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_ET_LT_NOBLOCK_example 源码说 ...
- Linux C++ 网络编程学习系列(6)——多路IO之epoll高级用法
poll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_libevent 源码说明: server.cpp: 监听127. ...
- Linux C++ 网络编程学习系列(4)——多路IO之epoll基础
epoll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll 源码说明: server.cpp: 监听127.1:6666,功 ...
- 1高并发server:多路IO之select
1 select A:select能监听的文件描写叙述符个数受限于FD_SETSIZE,一般为1024.单纯改变进程打开 的文件描写叙述符个数并不能改变select监听文件个数 B:解决1024 ...
- 您的快递(高并发服务器之poll和epoll)请签收
前言 之前已经介绍过select函数,请参考这篇博客:https://www.cnblogs.com/liudw-0215/p/9661583.html,原理都是类似的,有时间先阅读下那篇博客,以便于 ...
- ServerSocketChannel实现多Selector高并发server
参考hbase RpcServer,编写了一个简洁版多Selector server,对nio怎么用,Selector如何选择事件会有更深入的认识. client端发送消息:内容长度 + 内容,200 ...
- 应用层协议实现系列(一)——HTTPserver之仿nginx多进程和多路IO的实现
近期在尝试自己写一个Httpserver,在粗略研究了nginx的代码之后,决定仿照nginx中的部分设计自己实现一个高并发的HTTPserver,在这里分享给大家. 眼下使用的较多的Httpserv ...
- 高并发之网络IO模型
你好,我是坤哥 今天我们聊一下高并发下的网络 IO 模型 高并发即我们所说的 C10K(一个 server 服务 1w 个 client),C10M,写出高并发的程序相信是每个后端程序员的追求,高并发 ...
- select poll epoll Linux高并发网络编程模型
0 发展历程 同步阻塞迭代模型-->多进程并发模型-->多线程并发模型-->select-->poll-->epoll-->... 1 同步阻塞迭代模型 bind( ...
随机推荐
- ThinkPHP表单令牌验证功能详细介绍
注:TP版本为3.1.3 在ThinkPHP框架下,两次提交同一个表单,比如提交信息后在浏览器点击后退退回上次的页面,重新点击提交按钮,就会提示“表单令牌错误”的信息. ThinkPHP新版内置了表单 ...
- input 标签的class 失效
今天CSS网页的是时候,动态添加input class属性失效, 检查原因是因为之前对此input 使用了 input[type='checkbox'] 应该给其定义一个CLASS,其后面动态添加C ...
- C#中标准Dispose模式的实现
http://www.cnblogs.com/luminji/archive/2011/03/29/1997812.html 需要明确一下C#程序(或者说.NET)中的资源.简单的说来,C#中的每一个 ...
- cvThreshold()函数理解
对图像二值化函数cvThreshold的理解 Threshold 对数组元素进行固定阈值操作 void cvThreshold( const CvArr* src, CvArr* dst, doubl ...
- rust尝鲜
官网 http://www.rust-lang.org/ 下载链接 http://static.rust-lang.org/dist/rust-0.10-i686-unknown-linux-gnu. ...
- [BZOJ 1004] [HNOI2008] Cards 【Burnside引理 + DP】
题目链接:BZOJ - 1004 题目分析 首先,几个定义和定理引理: 群:G是一个集合,*是定义在这个集合上的一个运算. 如果满足以下性质,那么(G, *)是一个群. 1)封闭性,对于任意 a, b ...
- Bomb Game
hdu3622:http://acm.hdu.edu.cn/showproblem.php?pid=3622 题意:你有n次,每次你可以在平面上放置一个点,并且每一次都会有两个位置可以选,每一次只能选 ...
- LeetCode_3 sum closet
Given an array S of n integers, find three integers in S such that the sum is closest to a given num ...
- SSH自定义标签
一.标签处理类:package cn.conris.sys.form; import java.io.IOException; import java.util.Enumeration; impor ...
- wcf中 生成x5.09证书的工具
原文链接http://blog.pluralsight.com/selfcert-create-a-self-signed-certificate-interactively-gui-or-progr ...