一、epoll的EPOLLONESHOT事件处理
二、编码实现
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define BUFFSIZE 1024
#define LISTEN_NUM 10
#define MAX_EPOLLEVENT_NUM 1024
struct epollArr{
int epoll_fd;
int operation_fd;
};
int setnonblock(int fd);
void reset_oneshot(int epoll_fd,int fd);
void addEpollFd(int epoll_fd,int add_fd,int enable_et);
void work_main(int epoll_fd,struct epoll_event *ready_events,int epoll_ret_value,int ser_fd);
void *thread_func(void *args);
int main(int argc,char *argv[])
{
char *ip=argv[1];
int port=atoi(argv[2]);
int serverAlive;
int serFd,epollFd;
int epollRetValue,waitTimeValue;
struct epoll_event readyEvent[MAX_EPOLLEVENT_NUM];
struct sockaddr_in serAddr;
if((serFd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("socket");
exit(EXIT_FAILURE);
}
bzero(&serAddr,sizeof(serAddr));
serAddr.sin_family=AF_INET;
serAddr.sin_port=htons(port);
if(inet_pton(AF_INET,ip,&serAddr.sin_addr.s_addr)==-1){
perror("inet_pton");
exit(EXIT_FAILURE);
}
if(bind(serFd,(struct sockaddr*)&serAddr,sizeof(serAddr))==-1){
perror("inet_pton");
exit(EXIT_FAILURE);
}
if(listen(serFd,LISTEN_NUM)==-1){
perror("listen");
exit(EXIT_FAILURE);
}
serverAlive=1;
if((epollFd=epoll_create(5))==-1){
perror("epoll_create");
exit(EXIT_FAILURE);
}
/*监听socket不能注册EPOLLONESHOT事件,否则应用程序只能处理
一个客户连接,因为后续的客户连接请求将不再触发监听socket的EPOLLIN事件*/
addEpollFd(epollFd,serFd,0);
while(serverAlive)
{
waitTimeValue=3000;
printf("epoll_wait...\n");
switch(epollRetValue=epoll_wait(epollFd,readyEvent,MAX_EPOLLEVENT_NUM,waitTimeValue))
{
case -1:
perror("epoll_wait");
serverAlive=0;
break;
case 0:
printf("epoll_wait timeout\n");
break;
default:
work_main(epollFd,readyEvent,epollRetValue,serFd);
break;
}
}
exit(EXIT_SUCCESS);
}
int setnonblock(int fd)
{
int oldFlags,newFlags;
if((oldFlags=fcntl(fd,F_GETFL))==-1){
perror("fcntl ");
exit(EXIT_FAILURE);
}
newFlags = oldFlags|O_NONBLOCK;
if(fcntl(fd,F_SETFL,newFlags)==-1){
perror("fcntl ");
exit(EXIT_FAILURE);
}
return oldFlags;
}
void addEpollFd(int epoll_fd,int add_fd,int enable_et)
{
struct epoll_event event;
bzero(&event,sizeof(event));
event.events=EPOLLIN
| EPOLLET;
event.data.fd=add_fd;
if(enable_et){
event.events
|= EPOLLONESHOT;
}
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,add_fd,&event)==-1){
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
setnonblock(add_fd);
}
void work_main(int epoll_fd,struct epoll_event *ready_events,int epoll_ret_value,int ser_fd)
{
int i,acceptFd,commRetValue;
char cliAddrBuf[24];
char serBuff[]="I am server\n";
socklen_t cliAddrLen;
struct sockaddr_in cliAddr;
for(i=0;i<epoll_ret_value;i++)
{
if((ready_events[i].data.fd==ser_fd) && (ready_events[i].events==EPOLLIN)){
if((acceptFd=accept(ser_fd,(struct sockaddr*)&cliAddr,&cliAddrLen))==-1){
if(errno==EINTR){
printf("accept:catch signal...\n");
continue;
}
perror("accept");
exit(EXIT_FAILURE);
}else{
if(inet_ntop(AF_INET,&cliAddr.sin_addr.s_addr,cliAddrBuf,sizeof(cliAddrBuf))==NULL){
printf("Get Connect: an unrecognized client address\n");
}else{
printf("Get Connect: %s:%d\n",cliAddrBuf,ntohl(cliAddr.sin_port));
}
addEpollFd(epoll_fd,acceptFd, 1);
}
}
else if(ready_events[i].events&EPOLLIN){
pthread_t tid;
struct epollArr epollArray;
epollArray.epoll_fd=epoll_fd;
epollArray.operation_fd=ready_events[i].data.fd;
if(pthread_create(&tid,NULL,thread_func,(void*)&epollArray)!=0){
perror("pthread_create");
exit(EXIT_FAILURE);
}
}
else if(ready_events[i].events&EPOLLOUT){
bzero(serBuff,sizeof(serBuff));
switch(commRetValue=send(ready_events[i].data.fd,serBuff,sizeof(serBuff),0))
{
case -1:
perror("recv");
close(ready_events[i].data.fd);
close(ser_fd);
exit(EXIT_FAILURE);
default:
if(bcmp(serBuff,"quit",4)==0){
if(epoll_ctl(epoll_fd,EPOLL_CTL_DEL,ready_events[i].data.fd,NULL)==-1){
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
close(ready_events[i].data.fd);
}
reset_oneshot(epoll_fd, ser_fd);
break;
}
}
}
}
void *thread_func(void *args)
{
int recvValue;
char commBuff[BUFFSIZE];
struct epoll_event cliEvents;
struct epollArr epollArrar=*(struct epollArr*)args;
int epoll_fd=epollArrar.epoll_fd;
int cli_fd=epollArrar.operation_fd;
bzero(commBuff,sizeof(commBuff));
//因为socket是EPOLLET的,所以需要一次性读取完
while(1)
{
recvValue=recv(cli_fd,commBuff,BUFFSIZE,0);
if(recvValue<0){ //recv出错/数据读取完
if(errno==EAGAIN){//recv读取完毕
cliEvents.events=EPOLLOUT | EPOLLONESHOT;
cliEvents.data.fd=cli_fd;
if(epoll_ctl(epoll_fd,EPOLL_CTL_MOD,cli_fd,&cliEvents)==-1){
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
printf("recv end...\n");
reset_oneshot(epoll_fd, cli_fd);
int normal_exit_code=0;
pthread_exit(&normal_exit_code);
}
//recv出错
perror("recv");
int error_exit_code=-1;
pthread_exit(&error_exit_code);
break;
}
else if(recvValue==0){//客户端关闭连接
printf("client close\n");
if(epoll_ctl(epoll_fd,EPOLL_CTL_DEL,cli_fd,NULL)==-1){
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
close(cli_fd);
int close_exit_code=0;
pthread_exit(&close_exit_code);
}
else{
printf("Get data of client:%s\n",commBuff);
if(bcmp(commBuff,"quit",4)==0){
if(epoll_ctl(epoll_fd,EPOLL_CTL_DEL,cli_fd,NULL)==-1){
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
close(cli_fd);
}
}
}
}
void reset_oneshot(int epoll_fd,int fd)
{
struct epoll_event event;
event.data.fd=fd;
event.events=EPOLLIN | EPOLLET | EPOLLONESHOT;
if(epoll_ctl(epoll_fd,EPOLL_CTL_MOD,fd,&event)==-1){
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
}