一、Epoll模型的三个函数
int epoll_create(int size);
作用:创建一个epoll句柄,告诉他需要监听的数目(也可以理解成申请一片空间,用于存放监听的套接字)
参数一:通知内核监听size个fd,只是个建议值并与硬件有关系。(从 Linux 内核 2.6.8 版本起,size 这个参数就被忽略了,只要求 size 大于 0 即可)
返回值:返回epoll句柄(fd)
*int epoll_ctl(int epfd, int op, int fd, struct epoll_event event);
作用:控制某个epoll监控的文件描述符上的事件:注册,修改、删除(也就是增添 删除 修改一个事件)
参数一:int epfd:epoll_create()的返回值
参数二:int op: 表示动作,用三个宏来表示
EPOLL_CTL_ADD(注册新的fd到epfd)
EPOLL_CTL_MOD(修改已经注册的fd监听事件)
EPOLL_CTL_DEL(从epfd删除一个fd)
参数三:int fd 操作对象(socket)
参数四:struct epoll_evevt* evevt; 告诉内核需要监听的事件
结构体如下:
struct epoll_event {
__uint32_t events; 宏定义读和写EPOLLIN读EPOLLOUT写
epoll_data_t data; 联合体
};
联合体如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
返回值:成功返回0,不成功返回-1
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
作用:监听红黑树上的事件,将产生动静的事件放在event这个数组内
参数一:int epfd:epoll_create()函数返回值
参数二:struct epoll_events* events用于回传代处理事件的数组(也就是存放产生动静的事件)
参数三:int maxevents 同时最多产生多少事件,告诉内核events有多大,该值必须大于0
参数四:int timeout表示 -1相当于阻塞,0相当于非阻塞,超时时间(单位:毫秒)
返回值:成功返回产生动静事件的个数
二、简单demo
sever.c
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#define OPEN_MAX 1024 //最多连接数
int main(int argc, char *argv[])
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
int on = 1;
int i;
int connfd;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));//设置为可重复使用的端口
struct sockaddr_in serv_addr; //服务器的地址结构体
memset(&serv_addr,0,sizeof(serv_addr));
//设置本服务器要监听的地址和端口,这样客户端才能连接到该地址和端口并发送数据
serv_addr.sin_family = AF_INET; //选择协议族为IPV4
serv_addr.sin_port = htons(9000); //绑定我们自定义的端口号,客户端程序和我们服务器程序通讯时,就要往这个端口连接和传送数据
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本地所有的IP地址;INADDR_ANY表示的是一个服务器上所有的网卡(服务器可能不止一个网卡)多个本地ip地址都进行绑定端口号,进行侦听。
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 32);
int efd = epoll_create(OPEN_MAX);
struct epoll_event event, events[OPEN_MAX];
event.events=EPOLLIN|EPOLLET;
event.data.fd = listenfd;
epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &event);//把服务器fd包装成事件放在红黑树上
while(1)
{
int nready=epoll_wait(efd,events,OPEN_MAX,-1);//判断为可读事件
for(i=0; i<nready; i++)
{
if(!(events[i].events & EPOLLIN))
{
continue;
}
if(events[i].data.fd == listenfd)//表示有新的连接
{
struct sockaddr_in client_addr;
int len=sizeof(client_addr);
memset(&client_addr,0,sizeof(client_addr));
connfd = accept(listenfd, (struct sockaddr *)&client_addr, &len);
event.events = EPOLLIN|EPOLLET;
event.data.fd = connfd;
epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);
}
else//表示旧的数据产生可读事件(1 客户端发来数据 2 客户端断开链接)
{
connfd=events[i].data.fd;
char recvline [1024];
memset(recvline,0,1024);
int nread=read(connfd,recvline,sizeof(recvline));
if(nread==0)
{
printf("client is close..\n"); //打印
epoll_ctl(efd, EPOLL_CTL_DEL, connfd, NULL);//删除果子 select是从集合 和 数组 删除
close(connfd);//关闭客服端 select一样
}
else
{
printf("%s",recvline);
}
}
}
}
return 0;
}
client.c
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9000);
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
{
printf("inet_pton failed exit !\n");
exit(1);
}
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("connect() failed exit!\n");
exit(1);
}
int len;
char recvline[1024];
char writeline[1024];
while(1)
{
memset(recvline, 0, sizeof(recvline));
memset(writeline, 0, sizeof(writeline));
//发送消息
printf("send to server:");
fgets(writeline,sizeof(writeline),stdin);
write(sockfd,writeline,strlen(writeline));
len = read(sockfd,recvline,1024);
if(len == 0)
{
printf("server is close");
}
printf("receive from server:%s\n",recvline);
}
close(sockfd); //关闭套接字
printf("end exit !\n");
return 0;
}