一个linux下的epoll事件模型实例

11 篇文章 0 订阅
10 篇文章 0 订阅

在red hat linux下(内核版本2.6以上)写了一个epoll事件模型的实例,读取客户端发送的socket请求,记录在日志文件中,暂时使用LT模式(水平模式)触发,当前采用阻塞方式进行,后续打算增加一个事件环状链表,采用多进程方式非阻塞得将事件放入事件链表中,通过子进程来处理具体事件。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>

//epoll设置为水平触发模式
#define ET 0
//定义最大事件句柄数
#define MAX_EPOLL_FD 1024
//epoll_wait收集到事件数
#define WAIT_EPOLL_NUM 20
//需要监听的事件数
#define LISTEN_EPOLL_NUM 20
//句柄错误
#define FD_ERR -1
//日志记录错误
#define LOG_ERR 0
//文件关闭错误
#define COLSE_ERR -1
//文件关闭成功
#define COLSE_SUC 0
//函数返回错误
#define RETURN_ERR 0
//函数返回成功
#define RETURN_SUC 1
//日志字符串最大长度
#define MX_LOG_STRING 1024
//最大监听数量
#define MX_LISTEN_NUM 10
//默认监听端口
#define DEFAULT_LISTEN_PORT 8801
//事件等待超时时间,单位ms
#define MX_WAIT_TIME 200
//写入或读取的缓冲区大小
#define MX_LINE 1024

//日志目录,暂时放在这里
const char *record_filename;

//运行所需全局变量
struct cycle_type{
    //接收事件数组
    struct epoll_event *accept_events;
    //事件监听//后续数组
    struct epoll_event listen_event;
    //事件监听句柄
    int listen_fd;
    //sockte文件句柄
    int socket_fd;
    //socket中connetc文件句柄
    int connect_fd;
    //日志文件描述符
    int record_fd;
    //监听端口//后续数组
    int listen_port;
};
struct cycle_type cycle;

//得到字符串的长度
int str_length(const char *src)
{
    int strnum=0;
    while(*src!= '\0'){
        src++;
        strnum++;
    }
    return strnum;
}

//打开日志文件
int recordfile_open(){
    //int filefd;
    //根据文件名打开文件,读写模式,如果不存在直接创建,写入到文件尾部
    cycle.record_fd=open(record_filename,O_WRONLY|O_CREAT|O_APPEND);
    if(cycle.record_fd==FD_ERR){
        printf("%s file open error!\n",record_filename);
        return FD_ERR;
    }else{
        return cycle.record_fd;
    }
}

//关闭日志文件
int recordfile_close(){
    //int filefd;
    //关闭日志
    if(close(cycle.record_fd)!=COLSE_SUC){
        sprintf("%s file close error!\n",record_filename);
        return COLSE_ERR;
    }
    return COLSE_SUC;
}


//创建事件监听
int create_epoll(){
    //生成用于处理accept的 epoll专用的文件描述符
    //int listen_fd;
    cycle.listen_fd=epoll_create(MAX_EPOLL_FD);

    if(cycle.listen_fd==FD_ERR){
        record_log("epoll_create error!\n\0",30);
        return RETURN_ERR;
    }
    return cycle.listen_fd;
}


//记录日志,只记录日志,日志文件系统初始化时打开,退出时关闭
//先不用可选参数来做,先用库函数snprintf()来取得相关的char *值
int record_log(char * error_string,int record_size){
    //char *log_str;
    //int str_leng=0;
    //char *record_Buf;

    //record_Buf=(char *)malloc(1024);
    //将程序运行相关信息赋值给变量,但是最大长度不得超过MX_LOG_STRING,否则就认为这个字符串返回的有问题(没有结束符)
    //str_leng=sprintf(record_Buf,"function name: %d line num :%d ",__FUNCTION__, __LINE__);

    //如果字符串长度超过限制,认为是日志记录错误
    //if(str_length(log_str)>=MX_LOG_STRING){
    //  printf("record log error!");
    //  return LOG_ERR;
    //}
    //write(cycle.record_fd, record_Buf, str_leng);
    //record_Buf=strcpy(log_str,error_string);

    //如果字符串长度超过限制,认为是日志记录错误
    //if(str_length(error_string)>=MX_LOG_STRING){
    //  printf("record log error!\n");
    //  return LOG_ERR;
    //}

    //写日志文件,限定了所能写的最长字符串
    write(cycle.record_fd, error_string, record_size);
    //printf(error_string);
    return RETURN_SUC;
}

//系统初始化
int init_system(){
    int startepfd;
    record_filename="log/error.log";
    //memset(record_filename,0,1024);
    //strcpy(record_filename,"log/error.log");
    //初始化
    cycle.accept_events=(struct epoll_event *)malloc(WAIT_EPOLL_NUM*sizeof(struct epoll_event));//
    //cycle.listen_event=(epoll_event *)malloc(1*sizeof(epoll_event));
    cycle.listen_fd=FD_ERR;
    cycle.socket_fd=FD_ERR;
    cycle.record_fd=FD_ERR;
    cycle.listen_port=DEFAULT_LISTEN_PORT;

    //打开日志文件
    cycle.record_fd=recordfile_open();
    if(cycle.record_fd<=0){
        printf("%s file open error!\n",record_filename);
        return RETURN_ERR;
    }

    //创建事件epoll
    startepfd=create_epoll();
    if(startepfd<=0){
        //日志函数需要修改为可变参数
        record_log("create epoll error!\n\0",30);
        return RETURN_ERR;
    }
    return RETURN_SUC;
}
//设置缓冲区函数,start[0]到start[str_size-1]设置为set_char,start[str_size]='\0'
char *zhm_memset(char *start,const char set_char,int str_size){
    int i=0;
    while(i<str_size){
        start[i]=set_char;
    }
    start[str_size]='\0';
    return start;
}

//创建socket并且设置sockte模式,并开始监听,失败返回0,成功返回
int listen_socket(){
    int epollfd;
    int res;
    char *err_msg;
    struct sockaddr_in serveraddr;

    cycle.socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(cycle.socket_fd<=0){ 
        sprintf(err_msg,"socket fd:%d\n\0",cycle.socket_fd); 
        record_log(err_msg,20);
        return RETURN_ERR;
    };
    //把socket 设置为非阻塞方式
#if ET
    //当使用边缘触发时,因为只发送一遍,必须设置为非阻塞模式
    setnonblocking(cycle.socket_fd);
#endif
    //设置与要处理的事件相关的文件描述符
    cycle.listen_event.data.fd=cycle.socket_fd;
    //设置要处理的事件类型
#if ET
    cycle.listen_event.events=EPOLLIN|EPOLLET;
#else
    cycle.listen_event.events=EPOLLIN;
#endif
    create_epoll();
  epoll_ctl(cycle.listen_fd,EPOLL_CTL_ADD,cycle.socket_fd,&(cycle.listen_event));
    //memset(&address, 0, sizeof(address));
    serveraddr.sin_family = AF_INET ;
    serveraddr.sin_port =htons(cycle.listen_port);
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //inet_addr("127.0.0.1");
    res = bind(cycle.socket_fd, (struct sockaddr *) &serveraddr, sizeof( serveraddr ) );
    if(res){ 
        printf( "port is used , not to repeat bind\n" ); 
        return RETURN_ERR;
    };
    res = listen(cycle.socket_fd,MX_LISTEN_NUM);
    if(res) { 
        printf( "listen port is error ;\n" ); 
        return RETURN_ERR;
    };
    return RETURN_SUC ;
}

/*
增加一个父进程和N个子进程,父进程负责处理专心等待事件,并把事件放入事件链表,子进程负责处理事件链表中的事件
*/
int main(int argc, char *argv[]){
    socklen_t cli_len;
    struct sockaddr_in client_addr;
    struct epoll_event ev_tmp;
  int initStatus=0,sockStatus=0;
  int epint=0;
    //初始化系统
    initStatus=init_system();
    if(initStatus<=0){
        printf( "init system error!\n" ); 
        return RETURN_ERR;
    }
    //设置监听
    sockStatus=listen_socket();
    if(sockStatus<=0){
        printf( "init socket error!\n" ); 
        return RETURN_ERR;
    }

    cli_len = sizeof(struct sockaddr);
//开始等待事件
//事件处理
    while(1){
        //事件等待
        int i;
        char *err_msg1;
        if(cycle.listen_fd>0){
            err_msg1=(char *)malloc(1024);
            //err_msg=(char *)malloc(1024);
            sprintf(err_msg1,"listen_fd : %d \n\0",cycle.listen_fd);
            record_log(err_msg1,20);
        }else{
            record_log("listen_fd error !\n\0",30);
            continue;
        }
        epint=epoll_wait(cycle.listen_fd,cycle.accept_events,WAIT_EPOLL_NUM,MX_WAIT_TIME);
        //sleep(1);
        for(i=0;i<=epint;i++){
            //事件处理,有新的连接
            if(cycle.accept_events[i].data.fd==cycle.socket_fd)
            {
                int conn_fd;
                char *err_msg,*err_msg2;//accept这个连接
                printf( "CONNECT \n");
                record_log( "CONNECT \n\0",10);
                if(cycle.socket_fd>0){
                    err_msg=NULL;
                    err_msg=(char *)malloc(1024);
                    sprintf(err_msg,"socket_fd : %d \n\0",cycle.socket_fd);
                    record_log(err_msg,20);
                }else{
                    record_log("socket fd error !\n\0",30);
                }
                cycle.connect_fd = accept(cycle.socket_fd,( struct sockaddr *)&client_addr, &cli_len);
                if(cycle.connect_fd<0){
                    err_msg=NULL;
                    err_msg=(char *)malloc(1024);
                    sprintf(err_msg,"errno %d\n\0",errno);
                    record_log(err_msg,20);
                    err_msg2=strerror(errno);
                    record_log(err_msg2,30);
                    perror( "connfd<0");
                    exit(1);
                }
                record_log("accept connect!\0",20);
                char *str = inet_ntoa(client_addr.sin_addr);
                //char *log_str=sprintf( "accapt a connection from %s\n" ,str);
                //record_log(log_str);
                //设置用于读操作的文件描述符
                ev_tmp.data.fd=cycle.connect_fd;
                //设置用于注册的读操作事件
                #if ET
                    ev_tmp.events=EPOLLIN|EPOLLET;
                #else
                    ev_tmp.events=EPOLLIN;
                #endif
                //注册ev_tmp
                epoll_ctl(cycle.listen_fd,EPOLL_CTL_ADD,cycle.connect_fd,&ev_tmp);
            }else if(cycle.accept_events[i].events&EPOLLIN)//接收到数据,读socket
            {
                int sock_fd;
                char *read_log;
                char *line;
                char *err_msg,*err_msg2;
                int n;
                printf( "EPOLLIN\n");
                record_log( "EPOLLIN\n\0",10);

                line=(char *)malloc(1024*sizeof(char));
                //line=(char *)zhm_memset(line,0,1024);
                //line=(char *)memset((void *)linetmp,0,1024*sizeof(char));
                //if ((cycle.socket_fd = cycle.accept_events[i].data.fd) < 0)
                //  continue;
                if(cycle.socket_fd>0){
                    //err_msg=(char *)malloc(1024*sizeof(char));
                    //sprintf(err_msg,"socket_fd : %d \n",cycle.socket_fd);
                    //record_log(err_msg);
                    record_log("socket fd right !\n\0",20);
                }else{
                    record_log("socket fd error !\n\0",20);
                }
                printf("read begin!\n");
                if ((n = read(cycle.connect_fd, line, MX_LINE))<0){
                    printf("read error!\n");
                    if(errno>0){
                        //sprintf(err_msg, "errno %d\n",errno);
                        //record_log(err_msg);
                        err_msg2=strerror(errno);
                        record_log(err_msg2,30);
                        record_log("\n\0",2);
                    }
                    if (errno == ECONNRESET) {
                        close(cycle.socket_fd);
                        cycle.accept_events[i].data.fd = -1;
                    } else{
                        record_log( "read error!\n\0",10);
                    }
                }else if (n == 0){
                    printf("read empty!\n");
                    close(cycle.socket_fd);
                    cycle.accept_events[i].data.fd = -1;
                }else{
                    printf("read fininsh! \n");
                    record_log("read fininsh! \n\0",20);

                    line[n] = '\0';
                    err_msg=(char *)malloc(1024*sizeof(char));
                    sprintf(err_msg,"read %s\n\0",line);
                    record_log(err_msg,n);
                  //record_log(read_log);
                }

                //设置用于写操作的文件描述符
                ev_tmp.data.fd=cycle.socket_fd;
                //设置用于注册的写操作事件
                #if ET
                    ev_tmp.events=EPOLLOUT|EPOLLET;
                #else
                    ev_tmp.events=EPOLLOUT;
                #endif    
                //修改sockfd 上要处理的事件为 EPOLLOUT,设置下一个循环为输出事件
                epoll_ctl(cycle.listen_fd,EPOLL_CTL_MOD,cycle.socket_fd,&ev_tmp);
            }else if(cycle.accept_events[i].events&EPOLLOUT)//有数据待发送,写socket
            {
                char *line;
                //int sock_fd;

                record_log( "EPOLLOUT\n\0",10);
                //int n=20;
                //没有东西发送
                memset(line,0,1024);
                //memset(line,' ',20);

                cycle.socket_fd = cycle.accept_events[i].data.fd;
                write(cycle.socket_fd, line, MX_LINE);
                //设置用于写操作的文件描述符
                ev_tmp.data.fd=cycle.connect_fd;
                //设置用于注册的写操作事件
                #if ET
                    ev_tmp.events=EPOLLIN|EPOLLET;
                #else
                    ev_tmp.events=EPOLLIN;
                #endif    
                //修改sockfd 上要处理的事件为 EPOLLOUT,设置下一个循环为输出事件
                epoll_ctl(cycle.listen_fd,EPOLL_CTL_MOD,cycle.connect_fd,&ev_tmp);
            }
            sleep(1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值