linux c epoll mysql_Linux高并发服务器——epoll服务器

epoll服务器

一、概述

epoll是Linux下多路复用IO接口select/poll的增强版本

epoll能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率

select使用轮询来处理,随着监听 fd 数目的增加而降低效率。而epoll只需要监听那些已经准备好的队列集合中的文件描述符,效率较高。

二、epoll API

头文件 #include

1.创建一个epoll句柄,参数size用来告诉内核监听的文件描述符个数,跟内存大小有关int epoll_create(int size)

参数

size 希望监听的文件描述符的个数(建议值),真实值跟内存相关

返回值

一个epoll句柄(返回一根结点,底层由红黑树构成)

2.控制某个epoll监控的文件描述符上的事件(注册、修改、删除)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

参数

epfd:为epoll_creat的句柄

op:表示动作,用3个宏来表示:

EPOLL_CTL_ADD(注册新的fd到epfd),

EPOLL_CTL_MOD(修改已经注册的fd的监听事件),

EPOLL_CTL_DEL(从epfd删除一个fd);

fd:需要操作的文件描述符

event:告诉内核需要监听的事件

其中 struct epoll_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;

events成员可以是如下值

EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭);

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

EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

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

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

EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

返回值

成功时返回 0,失败则返回 -1,并设置 errno

3.等待所监控文件描述符上有事件的产生,类似于select()调用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

需要c/c++ Linux服务器高阶知识视频资料的朋友加群720209036获取

知识点有C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等等。

三、epoll服务器模型#include

#include

#include

#include

#include

#include

#include

//#include "wrap.h"

#define MAXLINE 80

#define SERV_PORT 8000

#define OPEN_MAX 1024

int main(int argc, char *argv[])

{

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;

struct epoll_event tep, ep[OPEN_MAX];//接收 存放数据

//网络socket初始化

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;//后面数据初始化赋值时 数据初始化为-1

//创建树

efd = epoll_create(OPEN_MAX);

if (efd == -1)

perr_exit("epoll_create");

//添加监听套接字

tep.events = EPOLLIN;

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

client[j] = connfd;

break;

}

//是否到达最大值 保护判断

if (j == OPEN_MAX)

perr_exit("too many clients");

//更新client下标

if (j > maxi)

maxi = j;

//添加通信套接字到树上

tep.events = EPOLLIN;

tep.data.fd = connfd;

res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);

if (res == -1)

perr_exit("epoll_ctl");

}

else

{

//将connfd赋值给socket

sockfd = ep[i].data.fd;

//读取数据

n = Read(sockfd, buf, MAXLINE);

//无数据则删除该结点

if (n == 0)

{

//将Client中对应fd数据值恢复为-1

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\n", j);

}

else //有数据则写回数据

{

for (j = 0; j < n; j++)

buf[j] = toupper(buf[j]);

Writen(sockfd, buf, n);

}

}

}

}

close(listenfd);

close(efd);

return 0;

}

补充

因为epoll可以处理并发连接数量比select的最大1024数量可以更多,一般需要修改最大文件描述符数量

修改文件描述符最大个数

编辑文件sudo vi /etc/security/limits.conf

写入以下配置(soft软限制,hard硬限制)* soft nofile 65536

* hard nofile 100000

之后重启linux再查看最大文件描述个数就修改成对应的65536了

查看最大文件描述符ulimit -n

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值