这并不是一个在线上跑的版本,逻辑也并不复杂。服务器具有超时机制,
我给每个客户连接都加入了一个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);
}