Linux(程序设计):53---epoll处理EPOLLONESHOT事件

一、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);
    }
}
  • 程序有点问题,待续吧
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值