文档章节

Redis 网络编程

老汉-憨憨
 老汉-憨憨
发布于 2017/07/25 15:53
字数 712
阅读 9
收藏 1
/**
 * 
 * 基于Redis网络事件框架Demo
 * 
 * @author xushun
 * 
 *  email : xushun007@gmail.com
 */


#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include "ae.h"
#include "anet.h"


#define MAX_LEN 10240
#define MAX_REQUEST_SIZE 5120

#define PORT 23456
#define IP_ADDR_LEN 40

#define TIMER_LOOP_CYCLE 10000

// 读数据状态
#define OK 0
#define ERR -1
#define NO_READY 1

// 存放错误信息的字符串
char g_err_string[MAX_LEN];

// 事件循环体
aeEventLoop *g_event_loop = NULL;

// 客户端结构体
typedef struct client {
	int fd;                    // 文件描述符
	char ipaddr[IP_ADDR_LEN];  // client IP 地址
	int port;                  // 端口
	char request[MAX_LEN];     // 客户端请求字符串
	char response[MAX_LEN];    // 响应字符串
	int len;                   // 缓冲长度
	int rsplen;
}client_t;

client_t* createClient() {
	client_t* c = malloc(sizeof(client_t));
	if(c == NULL) {
		fprintf(stderr, "alloc mem failure.");
		exit(1);
	}
	c->fd = -1;
	c->len = 0;
	c->rsplen = 0;
	return c;
}

// 定时器
int PrintTimer(struct aeEventLoop *eventLoop, long long id, void *clientData)
{
	static int i = 0;
	printf("Timer: %d\n", i++);
	// TIMER_LOOP_CYCLE/1000 秒后再次执行该函数
	return TIMER_LOOP_CYCLE;
}

//停止事件循环
void StopServer()
{
	aeStop(g_event_loop);
}

// 客户退出处理函数
void ClientClose(aeEventLoop *el, client_t* c, int err)
{
	//如果err为0,则说明是正常退出,否则就是异常退出
	if( 0 == err )
		printf("Client quit: %d\n", c->fd);
	else if( -1 == err )
		fprintf(stderr, "Client Error: %s\n", strerror(errno));

	//删除结点,关闭文件
	aeDeleteFileEvent(el, c->fd, AE_READABLE);
	close(c->fd);
	free(c);
}

// 响应客户
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {	
	client_t* c = privdata;
	int nwritten = c->rsplen;
	int res, sentlen = 0;
	
	printf("Request From %s:%d : %s\n", c->ipaddr, c->port, c->response);
	while(nwritten) {
	    res = write(fd, c->response + sentlen, c->rsplen - sentlen);

	    // 写入出错
	    if (res == -1) {
		if (errno == EAGAIN) {
		    continue;
		} else {
		    fprintf(stderr, "send response to client failure.\n");
		    ClientClose(el, c, res);
		}
	    }
            
            nwritten -= res;
            sentlen += res;

            if (sentlen == c->rsplen) {
                c->rsplen = 0;
            }
	}

	aeDeleteFileEvent(el,c->fd,AE_WRITABLE);
}

int processBuffer(aeEventLoop *el, client_t* c, int res) {
	char *newline = strstr(c->request,"\r\n");
	int reqlen;	

	if(newline == NULL) {
		if(c->len > MAX_REQUEST_SIZE) {
			fprintf(stderr,"Protocol error: too big request");
			ClientClose(el, c, res);
			return ERR;
		}
		return NO_READY;
	}

	reqlen = newline - c->request + 2;
	memcpy(c->response, c->request, reqlen);
	c->rsplen = reqlen;
	c->len -= reqlen;
	if(c->len)
		memmove(c->request, c->request+reqlen, c->len);

	return OK;
}

// 读取客户端数据
void ReadFromClient(aeEventLoop *el, int fd, void *privdata, int mask)
{
	int res;
	client_t* c = privdata;
	res = read(fd, c->request + c->len, MAX_LEN - c->len);
	if( res <= 0 )
	{
		ClientClose(el, c, res);
		return;
	}
	c->len += res;	
	
	if(processBuffer(el, c, res) == OK) {
		if(aeCreateFileEvent(el, fd, AE_WRITABLE, sendReplyToClient, c) == AE_ERR)  {
			fprintf(stderr, "Can't Register File Writeable Event.\n");
			ClientClose(el, c, res);
		}			
	}
	
}



//接受新连接
void AcceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask)
{
	client_t* c = createClient();

	c->fd = anetTcpAccept(g_err_string, fd, c->ipaddr, &c->port);
	if(c->fd == ANET_ERR) {
		fprintf(stderr, "Accepting client connection: %s", g_err_string);
		free(c);
		return;	
	}
	printf("Connected from %s:%d\n", c->ipaddr, c->port);
	
	if( aeCreateFileEvent(el, c->fd, AE_READABLE, ReadFromClient, c) == AE_ERR )
	{
		fprintf(stderr, "Create File Event fail: fd(%d)\n", c->fd);
		close(c->fd);
                free(c);
	}
}

int main()
{

	printf("Start\n");

	signal(SIGINT, StopServer);

	//初始化网络事件循环
	g_event_loop = aeCreateEventLoop(1024*10);

	//设置监听事件
	int fd = anetTcpServer(g_err_string, PORT, NULL);
	if( ANET_ERR == fd )
		fprintf(stderr, "Open port %d error: %s\n", PORT, g_err_string);
	if( aeCreateFileEvent(g_event_loop, fd, AE_READABLE, AcceptTcpHandler, NULL) == AE_ERR )
		fprintf(stderr, "Unrecoverable error creating server.ipfd file event.");

	//设置定时事件
	aeCreateTimeEvent(g_event_loop, 1, PrintTimer, NULL, NULL);

	//开启事件循环
	aeMain(g_event_loop);

	//删除事件循环
	aeDeleteEventLoop(g_event_loop);

	printf("End\n");
	return 0;
}

Makefile:

objects = ae.o ae_epoll.o anet.o main.o
server : $(objects) 
	gcc -o server $(objects)

$(objects) : ae.h anet.h

ae.o : ae.c
ae_epoll.o : ae_epoll.c
anet.o : anet.c
mian.o : mian.c

clean :
	rm server $(objects)

 

本文转载自:https://github.com/microheart/Redis_Event_Demo

共有 人打赏支持
老汉-憨憨
粉丝 19
博文 322
码字总数 68382
作品 0
深圳
程序员
私信 提问
acl 3.1.2 版本发布,网络通信与服务器编程框架

acl 3.1.2 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台;整个 acl 项目主要包含三个...

郑树新
2015/05/11
1K
6
【推荐】我的书单(Book List)

下面列了我读过的一些编程相关的书籍,写了一些对书的印象,都是个人观点。书名后是我个人对书的推荐强烈度打的分(10分满分),都是拍脑袋打的。 我比较习惯读纸质书,买书也比较谨慎,踩的...

枕边书
01/10
0
0
acl 网络通信服务器框架 3.1.0 版本发布

acl 3.1.0 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台;整个 acl 项目主要包含三个...

郑树新
2015/02/08
2.9K
2
360开源的网络编程库--Pink

Pink是360开源的网络编程库,支持 pb、Redis 等协议。对网络编程的封装,用户实现一个高性能的 server 只需要实现对应的 DealMessage 函数即可。 支持单线程模型、多线程 worker 模型。...

匿名
2016/08/11
919
1
acl 3.1.1 版本发布,跨平台网络通信与服务器框架

acl 3.1.1 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台;整个 acl 项目主要包含三个...

郑树新
2015/03/30
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

内存性能的正确解读

一台服务器,不管是物理机还是虚拟机,必不可少的就是内存,内存的性能又是如何来衡量呢。 1. 内存与缓存 现在比较新的CPU一般都有三级缓存,L1 Cache(32KB-256KB),L2 Cache(128KB-2MB)...

阿里云官方博客
16分钟前
2
0
并发+超时示例

func installMantisAgent() {log.Println("begin auto repair mantis agent")num := 0succNum := 0failNum := 0var Q *queue.Queueswitch g.Config().RepairType {ca......

我爱吃葱花
29分钟前
1
0
增加一列自增id

ALTER TABLE xxxx ADD iSiteId INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT FIRST;

colin_86
38分钟前
3
0
安卓代码混淆

Proguard是安卓提供的方便开发者对代码和apk进行保护和精简的工具,可在SDK/tools文件夹下找到。 proguard的作用 : 1,代码混淆 2,精简代码,删掉没有用到的代码,减小apk的体积。 使用场景...

whoisliang
47分钟前
2
0
配置Tomcat虚拟主机

12月13日任务 16.4 配置Tomcat监听80端口 16.5/16.6/16.7 配置Tomcat虚拟主机 16.8 Tomcat日志 配置tomcat监听80端口 默认tomcat监听的是8080端口,如果想直接输入ip就访问到网页,就需要进行...

robertt15
53分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部