这并不是一个在线上跑的版本,逻辑也并不复杂。服务器具有超时机制,
我给每个客户连接都加入了一个timer,
客户活动丢失一定时间就会触发超时事件。
这几天会继续完善。可能会有些许bug,还望大家指出。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ev.h> #include <errno.h> #include <unistd.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include "xml.h" #define PORT 25565 #define MAX_FD 500 //fd的最大值 #define CONNECT_TIMEOUT 5.0//5秒无通讯服务器就主动关闭连接,这里定的如此短时为了测试 /*每个客户端单独需要的上下文,暂时没有用到*/ struct client_context { }; /*libev的上下文*/ struct libev_context { struct ev_io watcher;//存储每个设备的libev结构体 struct ev_timer timer;//各自的timer struct client_context context;//存储每个单独客户端需要的上下文 }; void accept_cb(EV_P_ ev_io *w,int revents); void client_cb(EV_P_ ev_io *w, int revents); void client_timer_cb(struct ev_loop *loop, ev_timer *w, int revents); //客户端连接数组 struct libev_context *client[MAX_FD]; int main(int argc, char *argv[]) { int sd; int t1; struct ev_loop *loop = EV_DEFAULT;//ev事件循环 struct sockaddr_in addr;//socket的服务器监听地址 struct ev_io accept_watcher;//accept的监听监视器 for(t1=0;t1<MAX_FD;t1++)//清除client指针数组 { client[t1] = NULL; } if((sd = socket(PF_INET,SOCK_STREAM,0)) <0) { printf("Socket error,code %d: %s\n",errno,strerror(errno)); return 0; } bzero(&addr,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = INADDR_ANY; if(bind(sd,(struct sockaddr *)&addr,sizeof(addr)) != 0) { printf("Bind error,code %d: %s\n",errno,strerror(errno)); return 0; } if(listen(sd,2) < 0) { printf("Listen error,code %d: %s\n",errno,strerror(errno)); return 0; } printf("Server start working...\n"); ev_io_init (&accept_watcher,accept_cb,sd,EV_READ); ev_io_start (loop,&accept_watcher); ev_run(loop,0); //当服务器退出时结束一些任务 return 0; } /*每个任务各自自己的timer,用于超时检测,当长时间没有通讯时,timer会关闭连接。 进入此事件表明系统已经长时间没有收到客户端的数据包,主动关闭服务。*/ void client_timer_cb(struct ev_loop *loop, ev_timer *w, int revents) { printf("Client not alive,close.\n"); int csd; struct libev_context *clientcontext; clientcontext = (struct libev_context *)((char*)w - (int)(&(((struct libev_context *)0)->timer)));//获取上下文 csd = clientcontext->watcher.fd; ev_timer_stop(loop, &(clientcontext->timer)); //停止定时器 ev_io_stop(loop,&(clientcontext->watcher));//注销已经停止响应的客户端的任务 free(clientcontext); printf("Context free OK.\n"); clientcontext = NULL; close(csd); } //accept客户端的任务 void accept_cb(struct ev_loop *loop,ev_io *w, int revents) { int client_sd = -1; //struct ev_io *w_client; //struct ev_timer *w_timer; //struct client_context *context; struct sockaddr_in client_addr; socklen_t client_len = sizeof(struct sockaddr_in); if(EV_ERROR & revents) { printf("Error event in accept,code %d: %s\n",errno,strerror(errno)); return; } /*accept 客户端的连接*/ client_sd = accept(w->fd, (struct sockaddr*)(&client_addr), &client_len); if(client_sd < 0) { printf("Accept error,code %d: %s\n",errno,strerror(errno)); return; } if(client_sd > MAX_FD) { printf("Max fd thread.\n"); goto err_sd; } /*accept客户端成功,给客户端分配它所需要的内存*/ client[client_sd] = (struct libev_context *)malloc(sizeof(struct libev_context)); if(client[client_sd] == NULL) { printf("Alloc context error,code %d: %s\n",errno,strerror(errno)); goto err_sd; } printf("Context alloc OK.\n"); //添加客户端任务 ev_io_init(&((*client[client_sd]).watcher), client_cb, client_sd, EV_READ); ev_io_start(loop, &((*client[client_sd]).watcher)); ev_timer_init(&((*client[client_sd]).timer),client_timer_cb,CONNECT_TIMEOUT,0); ev_timer_start(loop,&((*client[client_sd]).timer)); printf("Client connected success. \n"); return; err_sd: close(client_sd); } //每个客户端的处理任务。 void client_cb(struct ev_loop *loop, ev_io *w, int revents) { char buf[1024]; ssize_t read; //ev_timer_set(&((*client[w->fd]).timer),CONNECT_TIMEOUT,0.); client[w->fd]->timer.repeat = CONNECT_TIMEOUT; ev_timer_again(loop,&((*client[w->fd]).timer)); bzero(buf, 1024); if(EV_ERROR & revents) { printf("Error in Read!,code %d: %s\n",errno,strerror(errno)); return; } read = recv(w->fd,buf,1024,0); if(read<0) { printf("read Error,code %d: %s\n",errno,strerror(errno)); } if(read == 0) { printf("Disconnected,code %d: %s\n",errno,strerror(errno)); int t = w->fd; ev_io_stop(loop, &((*client[t]).watcher)); ev_timer_stop(loop, &(clientcontext->timer)); //停止定时器 free(client[t]); client[t] = NULL; close(w->fd); printf("Context free OK.\n"); } /*每个客户端对应的工作应该在此完成*/ printf("%s",buf); }