libev socket 的一个服务器

时间:2022-02-10 23:58:20

这并不是一个在线上跑的版本,逻辑也并不复杂。服务器具有超时机制,

我给每个客户连接都加入了一个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);


}