登陆

章鱼彩票app-Linux并发服务器模型五 -- epoll

admin 2019-07-18 175人围观 ,发现0个评论

多进程和多线程模型在完成中相对简略, 但其开支和CPU高度比较大, 一般不必多线程和多进程来完成服务器多路模型.

select由于其跨渠道, 但其最高上限默以为1024, 修正打破1024的话需求从头编译linux内核, poll尽管处理了select1024的约束, 但由于poll实质完成上也是轮询机制, 所以关于客户端的增加也会使功率下降.

epoll是Linux下多路复用IO接口select/poll的增强版别,它能明显进步程序在很多并发衔接中只需少数活泼的情况下的体系CPU利用率,由于它会复用文件描述符调集来传递成果而不必迫使开发者每次等候事情之前都必须从头预备要被侦听的文件描述符调集,另一点原因便是获取事情的时分,它无须遍历整个被侦听的描述符集,只需遍历那些被内核IO事情异步唤醒而参加Ready行列的描述符调集就行了。

epoll除了供给select/poll那种IO事情的水平触发(Level Triggered)外,还供给了边缘触发(Edge Triggered),这就使得用户空间程序有或许缓存IO状况,削减epoll_wait/epoll_pwait的调用,进步应用程序功率

  • 剖析epoll API
//生成一个专用的文件描述符, size写多少便是多少, 可是假如运转过程中增加的越来越多, 后边的其实还会监听
int epoll_create(int size);
//用于操控某个epoll文件描述符事情,能够注册、修正、删去
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
//epfd: 生成的专用描述符, 相当于一个树(红黑树)的根节点, 假如再有其他的增加进来, 则体系将其会挂载到其节点下面
//op: EPOLL_CTL_ADD -- 注册, EPOLL_CTL_MOD -- 修正, EPOLL_CTL_DEL -- 删去
//fd: 相关的文件描述符
//event: struct epoll_event 结构体类型的指针, 告知内核要监听什么事情
//其间event结构体剖析如下
struct epoll_event {
uint32_t events; //结构体
epoll_data_t data; //联合体
}
/*
events:
- EPOLLIN - 读
- EPOLLOUT - 写
- EPOLLERR - 反常
*/
/*
typedef union epoll_data {
void *ptr;
int fd; //常用
uint32_t u32;
uint64_t u64;
} epoll_data_t;
*/
/*等候IO事情发作 - 能够设置堵塞的函数, 对应select和poll函数
该函数能够告知咱们是哪个文件描述符发作变化, 并放到下面的数组中
由于传进去的时分结构体中包括结构体中含有fd和event*/
int epoll_wait(int epfd,
struct epoll_event* events, //章鱼彩票app-Linux并发服务器模型五 -- epoll数组
int maxevents,
int timeout);
/*参数:
epfd: 要检测的句柄
events:用于回传待处理事情的数组
maxevents:告知内核这个events的巨细
timeout:为超时时刻
-1: 永久堵塞; 0: 当即回来; >0: 堵塞多久*/

server

#include 
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 8192
#define SERV_PORT 8000
#define OPEN_MAX 1000
int main(int argc, char *argv[]) {
// 记载是第几个衔接上来的客户端
int num = 0;

// 创立监听的套接字
int listenfd = socket(AF_INET, SOCK_STREAM, 0);

// 端口复用
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

struct sockaddr_in servaddr;
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);

//创立epoll模型, efd指向红黑树根节点
int efd = epoll_create(OPEN_MAX);
if(efd == -1) {
perror("epoll_create error");
exit(1);
}

// tep: epoll_ctl参数
struct epoll_event tep;
//指定lfd的监听时刻为"读"
tep.events = EPOLLIN;
tep.data.fd = listenfd;
//将lfd及对应的结构体设置到树上,efd可找到该树
int res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);
if(res == -1) {
perror("epoll_ctl error");
exit(1);
}

socklen_t clilen;
struct sockaddr_in cliaddr;
// ep[] : epoll_wait参数
struct epoll_event ep[OPEN_MAX];
char buf[MAXLINE], str[INET_ADDRSTRLEN];

while(1) {
/*epoll为server堵塞监听事情, ep为struct epoll_e猫薄荷vent类型数组, OPEN_MAX为数组容量, -1表永久堵塞*/
int nready = epoll_wait(efd, ep, OPEN_MAX, -1);
if(nready == -1) {
perror("epoll_wait error");
exit(1);
}

for (int i = 0; i < nready; i++) {
//假如不是"读"事情, 持续循环
if (!(ep[i].events & EPOLLIN)) {
continue;
}

//判别满意事情的fd是不是lfd
if (ep[i].data.fd 章鱼彩票app-Linux并发服务器模型五 -- epoll== listenfd) {
clilen = sizeof(cliaddr);
// 承受衔接恳求
int connfd = accept(liste章鱼彩票app-Linux并发服务器模型五 -- epollnfd, (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));
printf("cfd %d---client %d\n", connfd, ++num);

tep.events = EPOLLIN;
tep.data.fd = connfd;
res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);
if(res == -1) {
perror("epoll_ctl error");
exit(1);
}
} else { // 不是监听的文件描述符, 通讯的fd
int sockfd = ep[i].data.fd;
int n = read(sockfd, buf, MAXLINE);

//读到0,阐明客户端封闭链接
if (n == 0) {
//将该文件描述符从红黑树去除
res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
if(res == -1) {
perror("epoll_ctl error");
exit(1);
}
//封闭与该客户端的链接
close(sockfd);
printf("client[%d] closed connection\n", sockfd);
} else if (n < 0) { //犯错
perror("read n < 0 error: ");
res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
close(sockfd);
} else { // 实践读到了字节数
for (int j = 0; j < n; j++) {
buf[j] = toupper(buf[j]); //转大写,写回给客户端
}
write(STDOUT_FILENO, buf, n); //发送到终端, 也能够打印出来
write(sockfd, buf, n); //发送给客户端
}
}
}
}
close(listenfd);
close(efd);

return 0;
}

请关注微信公众号
微信二维码
不容错过
Powered By Z-BlogPHP