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高并发服务器:多路IO之epoll相关推荐

  1. 网络编程实战之高级篇, 彻底解决面试C10k问题, 高并发服务器, IO多路复用, 同时监视多个IO事件

    目录 一.前言 二.IO多路复用的理解 三.IO多路复用的发展 select poll epoll ​四.C10K服务端代码 五. 总结 一.前言 网络入门篇,从操作系统的层次推开网络大门 网络入门基 ...

  2. Linux 高并发服务器开发

    该文章是通过观看牛客网的视频整理所得,以及在实践过程中遇到的问题及解决方案的整理总结. Linux 高并发服务器开发 linux 系统编程 linux 环境的搭建 环境搭建需要的软件 虚拟机中安装 u ...

  3. 从简单到高并发服务器(一)

    一个单线程的回声服务器 (Echo Server) 我们从一个简单的服务器开始说起. 它可以接受一个客户的连接,接收消息,然后把这个消息发送回去,关闭连接--完工.我们用 Linux 和 iOS / ...

  4. java设计高并发内存池_高并发服务器-连接池的设计

    高并发服务器-连接池的设计 高并发服务器需要有一些池的设计,如内存池,连接池,数据库连接池. 池(pool)的设计主要考虑到一些资源的频繁申请和释放,尤其是在高并发的服务器中,几万甚至几十万并发每秒, ...

  5. Linux高并发服务器开发---笔记2(多进程)

    0630 第4章 项目制作与技能提升 4.0 视频课链接 4.1 项目介绍与环境搭建 4.2 Linux系统编程1.4.3 Linux系统编程2 4.4 多进程 1-9 10.进程间通信☆☆☆ 进程间 ...

  6. Linux高并发服务器开发---笔记1(环境搭建、系统编程、多进程)

    0613 第4章 项目制作与技能提升 4.0 视频课链接 4.1 项目介绍与环境搭建 4.1.1 项目介绍 4.1.2 开发环境搭建 ①安装Linux系统.XSHELL.XFTP.Visual Stu ...

  7. Linux 高并发服务器实战 - 2 Linux多进程开发

    Linux 高并发服务器实战 - 2 Linux多进程开发 进程概述 概念1: 概念2: 微观而言,单CPU任意时刻只能运行一个程序 并发:两个队列交替使用一台咖啡机 并行:两个队列同时使用两台咖啡机 ...

  8. automation 服务器不能创建对象_高并发服务器逻辑处理瓶颈,如何解决?

    高并发服务器逻辑处理瓶颈,如何解决?首先我们先了解什么是并发! 并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有 ...

  9. Linux高并发服务器开发---笔记4(网络编程)

    0705 第4章 项目制作与技能提升 4.0 视频课链接 4.1 项目介绍与环境搭建 4.2 Linux系统编程1.4.3 Linux系统编程2 4.4 多进程 1-9 10.进程间通信☆☆☆ 4.5 ...

  10. Linux高并发服务器解决方案

    Linux高并发服务器案例演示 在网络通信中,我们常常的服务器经常会受到成千上万的请求提示,而电脑会根据请求建立相对应的socket链接,但是接触过Linux网络编程的人都知道,Linux链接和客户端 ...

最新文章

  1. 数据蒋堂 | JOIN简化 - 消除关联
  2. 介绍Python中的__future__模块
  3. Spring教程--AOP简介
  4. Python爬虫之旅_(数据解析)_bs4
  5. (function($){...})(jQuery)是什么意思 ——jQuery插件
  6. 小话设计模式四:策略模式
  7. web developer tips (38):如何用请求失败记录追踪重写规则
  8. java注解接收上传文件_SpringMVC 处理 multipart/data 请求实现文件上传
  9. 【读书笔记】金字塔原理-呈现金字塔
  10. 光有想法怎么开技术公司?
  11. pm2和pm2-logrotate 日志管理 初探
  12. Win10账户已被锁定解决方法
  13. Linux学习简单教程和常用命令(小白学习法)
  14. python爬房源信息_python爬虫获取链家二手房源信息
  15. ios13 微信提示音插件_iOS 13 替换微信提示音(教程),简单操作
  16. 重学JS(一):什么是枚举?
  17. Js 获取日期加(减)一天并规范日期格式
  18. 【STM32】独立看门狗程序
  19. java android rsa加密解密_Android RSA加密解密
  20. 价值800的swapidc鸟云模板开源版

热门文章

  1. Mac 下变更pip源
  2. JavaScript实现euclideanDistance欧氏距离算法(附完整源码)
  3. OpenCASCADE绘制测试线束:数据交换命令之XDE 图层命令
  4. wxWidgets:wxSystemOptions类用法
  5. wxWidgets:wxGraphicsContext类用法
  6. boost::units模块实现用常量测试所有运算符的组合的测试程序
  7. boost::range_const_reverse_iterator相关的测试程序
  8. boost::mpl模块实现find_if相关的测试程序
  9. boost::intrusive::function_hook用法的测试程序
  10. boost::geometry::within用法的测试程序