在red hat linux下(内核版本2.6以上)写了一个epoll事件模型的实例,读取客户端发送的socket请求,记录在日志文件中,暂时使用LT模式(水平模式)触发,当前采用阻塞方式进行,后续打算增加一个事件环状链表,采用多进程方式非阻塞得将事件放入事件链表中,通过子进程来处理具体事件。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//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!= ' '){
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 ",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 ",30);
return RETURN_ERR;
}
return RETURN_SUC;
}
//设置缓冲区函数,start[0]到start[str_size-1]设置为set_char,start[str_size]=' '
char *zhm_memset(char *start,const char set_char,int str_size){
int i=0;
while(i0){
err_msg1=(char *)malloc(1024);
//err_msg=(char *)malloc(1024);
sprintf(err_msg1,"listen_fd : %d n ",cycle.listen_fd);
record_log(err_msg1,20);
}else{
record_log("listen_fd error !n ",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 ",10);
if(cycle.socket_fd>0){
err_msg=NULL;
err_msg=(char *)malloc(1024);
sprintf(err_msg,"socket_fd : %d n ",cycle.socket_fd);
record_log(err_msg,20);
}else{
record_log("socket fd error !n ",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 %dn ",errno);
record_log(err_msg,20);
err_msg2=strerror(errno);
record_log(err_msg2,30);
perror( "connfd<0");
exit(1);
}
record_log("accept connect! ",20);
char *str = inet_ntoa(client_addr.sin_addr);
//char *log_str=sprintf( "accapt a connection from %sn" ,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( "EPOLLINn");
record_log( "EPOLLINn ",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 ",20);
}else{
record_log("socket fd error !n ",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 %dn",errno);
//record_log(err_msg);
err_msg2=strerror(errno);
record_log(err_msg2,30);
record_log("n ",2);
}
if (errno == ECONNRESET) {
close(cycle.socket_fd);
cycle.accept_events[i].data.fd = -1;
} else{
record_log( "read error!n ",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 ",20);
line[n] = ' ';
err_msg=(char *)malloc(1024*sizeof(char));
sprintf(err_msg,"read %sn ",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( "EPOLLOUTn ",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);
}
}
}