在 linux 的网络编程中,很长的时间都在使用 select 来做事件触发。在 linux 新的内核中,有了一种替换它的机制,就是 epoll。相比于 select, epoll 最大的好处在于它不会随着监听 fd 数目的增长而降低效率。因为在内核中的 select 实现中,它是采用轮询来处理的,轮询的 fd 数目越多,自然耗时越多。并且,在 linux/posix_types.h 头文件有这样的声明:
#define __FD_SETSIZE 1024
表示 select 最多同时监听 1024 个 fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。
|
||||
epoll 的接口非常简单,一共就三个函数:效率稳定,不会随着监控的描述符增多而减小 | ||||
1. int epoll_create(int size);
创建一个 epoll 的句柄, size 用来告诉内核这个监听的数目一共有多大。(epoll模型对监控的描述符没有限制,写什么都无所谓,只要不写0就好)这个参数不同于select()中的第一个参数,给出最大监听的 fd+1 的值。需要注意的是,当创建好 epoll 句柄后,它就是会占用一个 fd 值,在 linux 下如果查看/proc/进程 id/fd/,是能够看到这个 fd 的,所以在使用完 epoll 后,必须调用 close()关闭,否则可能导致 fd 被耗尽。
|
||||
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll 的事件注册函数,它不同于 select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。//成功返回0,失败返回-1
第一个参数是 epoll_create() 的返回值,
第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的 fd 到 epfd 中;
EPOLL_CTL_MOD:修改已经注册的 fd 的监听事件;
EPOLL_CTL_DEL:从 epfd 中删除一个 fd;(解注册)
第三个参数是需要监听的 fd,
第四个参数是告诉内核需要监听什么事, struct epoll_event 结构如下:
|
||||
3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents,int timeout);
等待事件的产生,类似于 select()调用。参数 events 用来从内核得到事件的集合(所以每次使用前都要清空,这里相当于以前用的select的rdset集合), maxevents 告之内核这个 events 有多大,这个 maxevents 的值不能大于创建 epoll_create()时的 size,参数 timeout 是超时时间(毫秒,0 会立即返回,-1 将不确定,也有说法说是永久阻塞;是一个相对时间)。该函数返回需要处理的事件数目,如返回 0 表示已超时。有描述符可读,主动通知 epoll_wait()
|
//数组取地址还是它本身
func.h
#include <sys/types.h>
#include <sys/socket.h>
|
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
|
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
|
"ctrl+d"read()读到0
epoll_tcp_server.c | epoll_tcp_client.c |
#include "func.h"
#define NUM 10
int main(int argc,char* argv[])
{
if(argc!=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2]));
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("bind");
return -1;
}
ret=listen(sfd,NUM);
if(-1==ret)
{
perror("listen");
return -1;
}
int epfd=epoll_create(1); //创建一个句柄,参数只要不是0就OK
struct epoll_event event,evs[NUM+1];
//第二个数组用来传参给epoll_wait(),得到哪个描述符有输入
event.events=EPOLLIN; //注册事件,多个操作要用或操作
event.data.fd=sfd; //注册要监听的描述符
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
event.events=EPOLLIN; //注册标准输入
event.data.fd=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
int i;
int new_fd;
char buf[128];
int n;
while(1)
{
memset(evs,0,sizeof(evs));
ret=epoll_wait(epfd,evs,NUM+2,-1);
if(ret >0)
{
for(i=0;i<ret;i++)
{
if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)
{
new_fd=accept(sfd,NULL,NULL);
printf("accept newfd =%d\n",newfd);
event.events=EPOLLIN;
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_ADD,new_fd,&event);
}
if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
{
memset(buf,0,sizeof(buf));
n=read(0,buf,sizeof(buf));
if(n>0)
{
send(new_fd,buf,strlen(buf)-1,0);
}else if(n==0)
{
printf("bye\n");
event.events=EPOLLIN;
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
close(new_fd);
}
}
if(evs[i].events == EPOLLIN && evs[i].data.fd == new_fd)
{
memset(buf,0,sizeof(buf));
n=recv(new_fd,buf,sizeof(buf),0);
if(n>0)
{
printf("recv client buf =%s\n",buf);
}else if(n==0){
printf("bye\n");
event.events=EPOLLIN;
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
close(new_fd);
}
}
}
}
}
return 0;
}
|
#include "func.h"
int main(int argc,char** argv)
{
if(argc !=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2])); //一定要用htons
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("connect");
return -1;
}
int epfd=epoll_create(1);
struct epoll_event event,evs[2];
event.events=EPOLLIN;
event.data.fd=sfd;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
event.events=EPOLLIN;
event.data.fd=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
int i;
char buf[128];
int n;
while(1)
{
memset(evs,0,sizeof(evs));
ret=epoll_wait(epfd,evs,2,-1);
if(ret>0)
{
for(i=0;i<ret;i++)
{
if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
{
memset(buf,0,sizeof(buf));
n=read(0,buf,sizeof(buf));
if(n==0)
{
printf("bye\n");
close(sfd);
return 0;
}
n=send(sfd,buf,strlen(buf)-1,0);
if(-1==n)
{
perror("send");
return -1;
}
}
if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)
{
memset(buf,0,sizeof(buf));
n=recv(sfd,buf,sizeof(buf),0);
if(n > 0)
{
printf("recv form server buf =%s\n",bu f);
}else if(n==0)
{
printf("bye\n");
close(sfd);
return 0;
}
}
}
}
}
return 0;
}
|
//服务器端ctrl+d;