1.epoll是由poll改进而来的。
2.epoll跟poll在编程实现上有一定程度相似。
3.epoll主要通过结构体 epoll_event 来实现监听。
(!!!!!!不想看原理的可以直接拉到后面看代码!!!!!)
//epoll_event结构体struct epoll_event
{
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
} __attribute__ ((__packed__));
typedef union epoll_data
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
虽然我知道贴出来可能都没卵用,我还是直接说吧。epoll编程的几个步骤:
1.创建 epoll_event 类型的结构体2个, 如: struct epoll_event events,wait_events 前者是用于把套接字描述符加到 epoll句柄中的,后者是用于判断发生了什么事件
struct epoll_event event; //用于往epoll句柄中加入新的需要监测的套接字
struct epoll_event wait_event; //用于装epoll_wait()的返回信息,判断哪一个套接字发生响应
2.用 epoll_create()创造一个 epoll句柄:
int epfd = epoll_create(10); //这个10没什么大概意义,只要是正整数即可。
3.给结构体epoll_event 赋值并加入到 epoll句柄 epfd中。epoll_ctl()
event.data.fd = sockfd; //sockfd为 socket()函数返回的监听套接字
event.events = EPOLLIN; //设置期望该套接字发生的事件为可读事件
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event); //EPOLL_CTL_ADD表示往epfd中加入新的套接字来检测
4.等待套接字响应。epoll_wait()
int ret = epoll_wait(epfd,&wait_event,num,-1);
//解释一下上面的这个函数:
第一个参数:epoll句柄(由epoll_create()返回的)
第二个参数:struct epoll_event 类型的结构体,放在该位置,当epoll_wait返回时,就会把响应的套接字信息放到这个结构体中。
第三个参数:正在监测的套接字个数。
第四个参数:-1表示一直阻塞直到有套接字发生响应,0表示不阻塞,无论有没有套接字响应 都返回,>0表示阻塞的时间,毫秒为单位。
这个函数的返回值ret 为发生响应的套接字的个数。
5.判断epfd中哪个套接字发生响应
if((wait_event.data.fd == sockfd)&&(wait_event.events & EPOLLIN ==EPOLLIN)) //若套接字sockfd发生响应,并且为可读
{......}
下面给出代码:
服务端:
#include "myhead.h"
#define MAX 50
void write_to_all(char *wbuf); //此函数用于把一个客户端发来的消息转发给其他客户端
int client_fd[50]; //此数组用于装连接进来的客户端的套接字文件描述符
int main()
{
int sockfd,newsockfd;
struct sockaddr_in saddr;
int size = sizeof(struct sockaddr_in);
//创建2个epoll_event结构体
struct epoll_event event; //用于往epoll句柄加入新的待检测套接字
struct epoll_event wait_event; //用于装epoll_wait()返回的响应的套接字的消息,判断哪个套接字发生响应
int on = 1;
int ret;
//设置TCP套接字的一些属性
bzero(&saddr,size);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockfd = socket(AF_INET,SOCK_STREAM,0);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //设置端口复用,不设也可以,对功能没影响
bind(sockfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr));
listen(sockfd,10);
//epoll 相关参数
int max = 0;
memset(client_fd,-1,sizeof(client_fd));
int epfd;
//创建epoll句柄epfd
epfd = epoll_create(10); //这是10没啥意义,只要是正数即可。
//加入监听套接字
event.data.fd = sockfd;
event.events = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);
//加入标准输入,使服务端可以发信息
event.data.fd = 0;
event.events = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
int i;
while(1)
{
//等待套接字发生某种响应,否则一直阻塞
ret = epoll_wait(epfd,&wait_event,max+2,-1);
//判断是否有新客户端连入
if((wait_event.data.fd ==sockfd) &&
(wait_event.events &EPOLLIN == EPOLLIN))
{
int len = sizeof(struct sockaddr);
struct sockaddr_in caddr;
newsockfd = accept(sockfd,(struct sockaddr*)&caddr,
&len);
//newsockfd加入client_fd
for(i=0;i<MAX;i++)
{
if(client_fd[i] != -1)
continue;
else
{
client_fd[i] = newsockfd;
break;
}
}
//newsockfd加入epoll句柄 epfd
event.data.fd = newsockfd;
event.events = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,newsockfd,&event);
max+=1;
ret --;//处理完一个响应后,ret减一
if(ret<=0)//若ret已经为0,则说明已经处理完所有的发生响应的套接字,就可以继续epoll_wait()等待套接字发生再响应
{
continue;
}
}
if((wait_event.data.fd ==0)&&(wait_event.events & EPOLLIN==EPOLLIN))
{
char wbuf[50]={0};
bzero(wbuf,50);
scanf("%s",wbuf);
write_to_all(wbuf);
ret-=1;
}
//判断有没有其他客户端的数据发来
for(i=0;i<MAX;i++)
{
char rbuf[50]={0};
if((wait_event.data.fd ==client_fd[i])
&&(wait_event.events&EPOLLIN==EPOLLIN))
{
int recv_num;
bzero(rbuf,50);
recv_num = recv(client_fd[i],rbuf,50,0);
if(recv_num == 0)
{
client_fd[i] = -1;
close(client_fd[i]);
max -=1;
}
else
{
printf("%s\n",rbuf);
bzero(rbuf,50);
}
ret-=1;
if(ret<=0)
break;
}
}
}
}
void write_to_all(char *wbuf)
{
int i;
for(i=0;i<MAX;i++)
{
if(client_fd[i] !=-1)
{
write(client_fd[i],wbuf,50);
}
}
}
客户端:
客户端有很多种写法,这里就给出一种用poll写的,因为也懒得再写,就用之前用开的客户端代码:
#include"myhead.h"
char rbuf[50];
char wbuf[50];
char ipbuf[50];
int main()
{
int sockfd;
int ret,port;
struct pollfd pollfd[2];
struct sockaddr_in saddr;
int size = sizeof(struct sockaddr_in);
bzero(&saddr,size);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = inet_addr("192.168.152.128");
sockfd = socket(AF_INET,SOCK_STREAM,0);
ret=connect(sockfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr));
if(ret == 0)
{
inet_ntop(AF_INET,(void*)&saddr.sin_addr.s_addr,ipbuf,50);
port = ntohs(saddr.sin_port);
printf("%s,%d\n",ipbuf,port);
}
pollfd[0].fd = STDIN_FILENO;
pollfd[0].events = POLLIN;
pollfd[1].fd = sockfd;
pollfd[1].events = POLLIN;
while(1)
{
poll(pollfd,2,-1);
if((pollfd[0].revents & POLLIN)==POLLIN)
{
puts("message from keyboard");
bzero(wbuf,50);
scanf("%s",wbuf);
write(sockfd,wbuf,50);
}
if((pollfd[1].revents & POLLIN)==POLLIN)
{
bzero(rbuf,50);
read(sockfd,rbuf,50);
printf("%s\n",rbuf);
}
}
}