epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
目前epell是linux大规模并发网络程序中的热门首选模型。
epoll除了提供select/poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
epoll中有两个关键的数据结构:红黑树和链表,红黑树操作的是加入或者删除客户端节点,链表中存放的是就绪客户端,这也是epoll比select/poll更加高效的原因之一
服务端代码:
/*************************************************************************
> File Name: server.c
> Author: chencj
> Mail: [email protected]
> Created Time: 2020年08月06日 星期四 16时41分30秒
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
std::string IP,PORT;
#define MAX_SIZE 1024
const int N = 128;
static int num = 0;
void sys_err(std::string msg)
{
perror(msg.c_str());
exit(1);
}
int main()
{
printf("please input IP and PORT:\n");
std::cin>>IP>>PORT;
int lfd,cfd,nfds;
char buf[N];
lfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字
if(lfd == -1) sys_err("scoket error\n");
struct sockaddr_in server_addr,client_addr;
socklen_t clen = sizeof(client_addr);
struct epoll_event ev , events[MAX_SIZE]; //监听事件 最大监听事件集合
//建立地址结构
server_addr.sin_addr.s_addr = inet_addr(IP.c_str());
server_addr.sin_port = htons(atoi(PORT.c_str()));
server_addr.sin_family = AF_INET;
//设置端口复用
int vel = 1;
if(setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,(void*)&vel,sizeof(vel)) == -1)
sys_err("setsockopt error\n");
//bind
if(bind(lfd,(struct sockaddr*)&server_addr,sizeof(server_addr)) == -1)
sys_err("bind error\n");
//listen
if(listen(lfd,N) == -1)
sys_err("listen error\n");
//建立epoll的树根节点
int epollfd = epoll_create(MAX_SIZE);
if(epollfd == -1) sys_err("epoll error\n");
//把监听套接字放到集合中去
ev.data.fd = lfd;
ev.events = EPOLLIN;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,lfd,&ev) == -1)
sys_err("lfd add error\n");
//开始监听并且通信
while(1)
{
nfds = epoll_wait(epollfd,events,MAX_SIZE,-1); // 阻塞等待
if(nfds == -1) sys_err("epoll wait\n");
int i;
for(i = 0;i < nfds; i++) {
//如果传出的是监听套接字
if(events[i].data.fd == lfd)
{
num++;
cfd = accept(lfd,(struct sockaddr*)&client_addr,&clen);
if(cfd == -1) sys_err("accept error\n");
printf("[IP : %s---PORT : %d] is connect,%d client\n",inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port),num);
ev.data.fd = cfd;
ev.events = EPOLLIN;
//加入
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,cfd,&ev) == -1) sys_err("cfd add error\n");
}
else
{
//数据通信
memset(buf,'\0',sizeof(buf));
int Bits = read(events[i].data.fd,buf,sizeof(buf));
if(Bits == 0){
num--;
printf("One client disconnect,remian %d client\n",num);
epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,NULL);
close(events[i].data.fd);
}
else
{
printf("[IP : %s---PORT : %d ]---Data : %s\n",inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port),buf);
write(events[i].data.fd,"Accept",7);
}
}
}
}
close(lfd);
return 0;
}
客户端代码:
/*************************************************************************
> File Name: client.cpp
> Author: chencj
> Mail: [email protected]
> Created Time: 2020年08月06日 星期四 18时00分02秒
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const int N = 128;
std::string IP,PORT;
void sys_err(std::string msg)
{
perror(msg.c_str());
exit(1);
}
int main()
{
printf("please input IP and PORT:\n");
std::cin>>IP>>PORT;
int cfd;
char buf[N],recv_buf[N];
//创建套接字
cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd == -1)
sys_err("socket error\n");
struct sockaddr_in client_addr;
//绑定地址结构
client_addr.sin_addr.s_addr = inet_addr(IP.c_str());
client_addr.sin_port = htons(atoi(PORT.c_str()));
client_addr.sin_family = AF_INET;
//进行连接
if(connect(cfd,(struct sockaddr*)&client_addr,sizeof(client_addr)) == -1)
sys_err("connect error\n");
printf("-------connect-------\n");
while(1)
{
memset(buf,'\0',sizeof(buf));
scanf("%s",buf);
write(cfd,buf,sizeof(buf));
if(memcmp("###",buf,3) == 0) {
printf("------disconnect------\n");
break;
}
memset(recv_buf,'\0',sizeof(recv_buf));
read(cfd,recv_buf,sizeof(recv_buf));
printf("%s\n",recv_buf);
}
close(cfd);
return 0;
}