libevent(16)基于zlib进行压缩和解压缩

一、zlib库中的API

zlib是非常著名的压缩工具,其官网:[zlib官网]

Zlib压缩算法:LZ77、LZ78、霍夫曼编码、滑动窗口、Rabin-Karp算法、哈希链、I/O缓冲区_rtoax的博客-CSDN博客 ...

zlib有2套压缩、解压缩函数,分别为:

(1)压缩函数 deflate() 和解压缩函数 inflate()

int deflate (z_streamp strm, int flush);

int inflate (z_streamp strm, int flush);

(2)压缩函数 compress() 和解压缩函数 decompress()

/**
 * 压缩函数,使用默认压缩等级 Z_DEFAULT_COMPRESSION
 * 返回:Z_OK 成功;Z_MEM_ERROR 没有足够的内存;Z_BUF_ERROR 没有足够的输出缓存
 */
int compress(Bytef *dest, uLongf *destLen,
             const Bytef *source, uLong sourceLen);

/**
 * 压缩函数2,可以手动指定压缩等级
 * 返回:Z_OK 成功;Z_MEM_ERROR 没有足够的内存;Z_BUF_ERROR 没有足够的输出缓存;
 *      Z_STREAM_ERROR 压缩 level 参数非法
 */
int compress2(Bytef *dest,   uLongf *destLen,
              const Bytef *source, uLong sourceLen,
              int level);

/**
 * 解压缩函数,需要预留足够的输出缓存(解压数据缓存)
 * 返回:Z_OK 成功;Z_MEM_ERROR 没有足够的内存;Z_BUF_ERROR 没有足够的输出缓存;
 *      Z_DATA_ERROR 输入数据错误或不完整
 */
int uncompress(Bytef *dest,   uLongf *destLen,
               const Bytef *source, uLong sourceLen);

/**
 * 解压缩函数2,sourceLen 为指针,表示解压时消耗的源字节数,此时可能由于输出缓存不足,导致数据并没有被解压完
 * 返回:Z_OK 成功;Z_MEM_ERROR 没有足够的内存;Z_BUF_ERROR 没有足够的输出缓存;
 *      Z_DATA_ERROR 输入数据错误或不完整
 */
int uncompress2(Bytef *dest,   uLongf *destLen,
                const Bytef *source, uLong *sourceLen);

2者区别:

(1)zlib的Deflate和inflate函数接收的是zlib库内自定义的一种数据流格式(z_stream)。

(2) compress和uncompress是对前者的封装,需要用户确保足够内存,一次调用完成操作。

(3)作者在inflate及deflate头文件的文档中说明了这两个文件提供的接口不应该被任何应用程序调用,所以需要使用压缩/解压相关功能的话直接调用compress()和uncompress()即可。

二、例子

1、bufferevent_zlib_server.cpp:


#include <event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#include <zlib.h>
#include <iostream>
#include <signal.h>
#include <string.h>

#define SPORT 5001

using namespace std;

/**
//文件传输模式:
//1. 先接收文件名称.
//2. 回复客户端“OK”.
//3. 再接收文件内容.
*/
typedef struct status{
	bool bstart;
	FILE *fp;
	z_stream *z_input;
 
	status(){
		bstart = false;
		z_input = NULL;
		fp = NULL;
	}
 
	~status(){
		if (fp){
			fclose(fp);
			fp = NULL;
		}
 
		if (z_input){
			inflateEnd(z_input);
			delete z_input;
			z_input = NULL;
		}
	}
 
} STATUS;
 
/*filter_in()函数是水平触发*/
bufferevent_filter_result filter_in(evbuffer *s,
									evbuffer *d,
									ev_ssize_t limit,
									bufferevent_flush_mode mode,
									void *arg) {
	cout << "filter_in()" << endl;
 
	STATUS* status = (STATUS*)arg;
	int nlen = 0;
 
	//接收文件名,不进行解压缩
	if (!status->bstart){
		char szData[1024] = {0};
		int nlen = evbuffer_remove(s, szData, sizeof(szData) - 1);
		evbuffer_add(d, szData, nlen);
 
		return BEV_OK;
	}
 
	//zlib上下文环境设置
	//取出buffer数据的"引用",不清理缓冲
	evbuffer_iovec v_in[1];
	
	// evbuffer_peek可以获得当前 evbuffer 中接收到了多少数据, 以及这些数据的内容.
	// 但不用从 evbuffer 中复制出 tcp 缓存中接收到的数据, 节省了大量的资源.
	nlen = evbuffer_peek(s, -1, NULL, v_in, 1);
	if (nlen <= 0){
		return BEV_NEED_MORE;
	}
 
	//设置输入数据的地址和大小
	status->z_input->avail_in = v_in[0].iov_len;
	status->z_input->next_in = (Byte*)v_in[0].iov_base;
 
	//设置输出数据的地址和大小
	//在d上申请一块内存,作为输出数据
	evbuffer_iovec v_out[1];
	evbuffer_reserve_space(d, 4096, v_out, 1);
 
	status->z_input->avail_out = v_out[0].iov_len; // 输出空间大小
	status->z_input->next_out = (Byte*)v_out[0].iov_base; // 输出空间地址
 
	//进行zlib解压缩
	int ret = inflate(status->z_input, Z_SYNC_FLUSH);
	if (ret != Z_OK){
		cout << "解压缩失败." << endl;
	}
 
	//解压缩的原始数据量大小 = 总大小-数据剩余大小
	int nRead = v_in[0].iov_len - status->z_input->avail_in;
	//将解压缩的数据从source evbuffer中剔除
	evbuffer_drain(s, nRead);
 
	//解压缩后的数据量大小 = 空间总大小-剩余空间大小
	int nWrite = v_out[0].iov_len - status->z_input->avail_out;
 
	//将压缩后的数据量大小传入destination evbuffer
	v_out[0].iov_len = nWrite;
	evbuffer_commit_space(d, v_out, 1);
 
	return BEV_OK;
}
 
// read_cb()函数是边沿触发: 只触发一次
void read_cb(bufferevent *bev, void *arg) {
	cout << "read_cb()" << endl;
 
	STATUS *status = (STATUS*)arg;
 
	//接收文件名
	if (!status->bstart) {
		char szData[1024] = {0};
		bufferevent_read(bev, szData, sizeof(szData) - 1);
 
		string out = "out/";
		out += szData;
 
		//打开文件
		status->fp = fopen(out.c_str(), "wb");
		if (!status->fp){
			cout << "打开文件失败." << endl;
			
			return;
		}
 
		//回复客户端“OK”信息
		bufferevent_write(bev, "OK", 2);
		status->bstart = true;
 
		return;
	}
 
	//写入文件内容
	do {
		char szData[1024] = {0};
		int nlen = bufferevent_read(bev, szData, sizeof(szData));
		if (nlen > 0) {
			fwrite(szData, 1, nlen, status->fp);
			fflush(status->fp);
		}
 
	} while (evbuffer_get_length(bufferevent_get_input(bev)));
}
 
void event_cb(bufferevent *bev,short events, void *arg){
	cout << "event_cb()" << endl;
 
	STATUS* status = (STATUS*)arg;
 
	int nError = 0;
 
	if (events & BEV_EVENT_EOF) {
		cout << "客户端断开连接." <<endl;
 
		nError = 1;
	} 
	else if(events & BEV_EVENT_READING && events & BEV_EVENT_TIMEOUT) {
		cout << "数据读入超时." <<endl;
 
		nError = 1;
	} else if(events & BEV_EVENT_CONNECTED) {
	    //测试发现客户端连接成功后,服务端不会收到BEV_EVENT_CONNECTED事件, 但是客户端会收到。
		cout << "客户端连接成功." << endl;
	} else if(events & BEV_EVENT_ERROR) {
		cout << "服务器遇到不可恢复的错误." << endl;
 
		nError = 1;
	}
 
	if (nError == 1){
		if (status) {
		    //释放资源
			delete status;
			status = NULL;
		}
 
		bufferevent_free(bev);
	}
}

// 建立连接时触发的回调
void listen_cb(struct evconnlistener *ev, evutil_socket_t s, struct sockaddr *addr, int socklen, void *arg){
	cout << "listen_cb()" << endl;
 
	event_base *base = (event_base*)arg;
 
	//创建bufferevent用来接收和发送数据
	bufferevent *bev = bufferevent_socket_new(base, s, BEV_OPT_CLOSE_ON_FREE);
 
	STATUS *status = new STATUS();
	
	//初始化zlib环境上下文
	status->z_input = new z_stream();
	inflateInit(status->z_input);
 
	//创建过滤bufferevent,并设置输入过滤函数
	bufferevent *bev_filter = bufferevent_filter_new(bev,
		filter_in, //输入过滤函数
		0,         //输出过滤函数(不设置)
		BEV_OPT_CLOSE_ON_FREE,//删除bev_filter同时删除bev
		0,         //清理回调函数
		status);   //传递参数
 
	//为过滤bufferevent设置读取回调函数
	bufferevent_setcb(bev_filter, read_cb, 0, event_cb, status);
 
	bufferevent_enable(bev_filter, EV_READ|EV_WRITE);
 
	//设置读入超时
	//timeval t = {10,0};
	//bufferevent_set_timeouts(bev_filter, &t, 0);
}
 
int main(int argc, char* argv[])
{
#ifdef _WIN32
	WSADATA wsa;
	WSAStartup(MAKEWORD(2,2), &wsa);
#else
	if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		return 1;
#endif
 
	//创建libevent环境上下文
	event_base *base = event_base_new();
	if (!base){
		cout << "event_base_new() fail." << endl;
 
		return 1;
	}
 
	//创建监听器并绑定端口
	sockaddr_in sin;
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SPORT);
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
 
	evconnlistener *ev = evconnlistener_new_bind(base,
		listen_cb, //监听回调函数
		base,      //监听回调函数的参数
		LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,//关闭ev同时关闭socket|地址重用
		10,                                     //同一时刻连接队列大小
		(sockaddr*)&sin,                        //绑定的端口和地址
		sizeof(sin));
 
	if(base){
		event_base_dispatch(base);
    }
	
	if (base){
		event_base_free(base);
    }
	
	return 0;
}
 

2、bufferevent_zlib_client.cpp:


#include <event.h>
#include <event2/bufferevent.h>
#include <zlib.h>
#include <iostream>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define SPORT 5001

using namespace std;
 
#define FILENAME "test.txt"
 
struct ClientStatus {
	FILE *fp;
	bool end;
	z_stream *z_output;
	bool startsend;
 
	ClientStatus() {
		fp = NULL;
		z_output = NULL;
		end = false;
		startsend = false;
	}
 
	~ClientStatus() {
		if (fp){
			fclose(fp);
			fp = NULL;
		}
 
		if (z_output){
			inflateEnd(z_output);
			delete z_output;
			z_output = NULL;
		}
	}
 
};
 
// 文件数据发送时, 先经过过滤函数, 再发送到服务器端
bufferevent_filter_result filter_out(evbuffer *s, evbuffer *d,
	ev_ssize_t limit, bufferevent_flush_mode mode, void *arg) {
	ClientStatus* status = (ClientStatus*)arg;
	int nlen = 0;
 
	//如果发送的是文件名称,不进行压缩
	if (!status->startsend) {
		char szData[1024] = {0};
		nlen = evbuffer_remove(s, szData, sizeof(szData));
		evbuffer_add(d, szData, nlen);
 
		return BEV_OK;
	}
 
	//开始发送文件内容,进行数据的压缩
	//取出buffer数据的"引用"
	evbuffer_iovec v_in[1];
	nlen = evbuffer_peek(s, -1, 0, v_in, 1);
	if (nlen <= 0) {
		//调用write回调 清理空间
		if (status->end) {
			return BEV_OK;
		}
		//没有数据 BEV_NEED_MORE 不会进入写入回调
		return BEV_NEED_MORE;
	}
 
	if (!status->z_output) {
		return BEV_ERROR;
	}
 
	//zlib上下文环境设置
	//设置输入数据的地址和大小
	status->z_output->avail_in = v_in[0].iov_len; //输入数据大小
	status->z_output->next_in = (Byte*)v_in[0].iov_base; //输入数据地址
 
	//设置输出数据的地址和大小
	//在d上申请一块内存,作为输出数据
	evbuffer_iovec v_out[1];
	evbuffer_reserve_space(d, 4096, v_out, 1);
 
	status->z_output->avail_out = v_out[0].iov_len; //输出空间大小
	status->z_output->next_out = (Byte*)v_out[0].iov_base; //输出空间地址
 
	//进行zlib压缩
	int ret = deflate(status->z_output, Z_SYNC_FLUSH);
	if (ret != Z_OK) {
		cout << "压缩失败." << endl;
	}
 
	//压缩的原始数据量大小 = 总大小-数据剩余大小 (读取的数据)
	int nRead = v_in[0].iov_len - status->z_output->avail_in;
	//将压缩的数据从source evbuffer中剔除
	evbuffer_drain(s, nRead);
 
	//压缩后的数据量大小 = 空间总大小-剩余空间大小 (压缩后的数据)
	int nWrite = v_out[0].iov_len - status->z_output->avail_out;
 
	//将压缩后的数据量大小传入destination evbuffer
	v_out[0].iov_len = nWrite;
	evbuffer_commit_space(d, v_out, 1);
 
	return BEV_OK;
}
 
void read_cb(struct bufferevent *bev, void *ctx) {
	ClientStatus* status = (ClientStatus*)ctx;
 
	//等待接收服务端发送的回复“OK”
	char szData[1024] = {0};
	int nlen = bufferevent_read(bev, szData, sizeof(szData) - 1);
 
	if (strcmp(szData, "OK") == 0) {
		status->startsend = true;
		//触发写入回调,开始发送文件内容
		bufferevent_trigger(bev, EV_WRITE, 0);
	} else {
		bufferevent_free(bev);
	}
}
 
void write_cb(struct bufferevent *bev, void *ctx) {
	cout << "write_cb" << endl;
 
	ClientStatus* status = (ClientStatus*)ctx;
 
	//发送结束,清理资源
	if (status->end) {
		//再判断缓冲区是否有数据
		bufferevent *be = bufferevent_get_underlying(bev);
		evbuffer *evb = bufferevent_get_output(be);
		int nlen = evbuffer_get_length(evb);
		
		if (nlen <= 0) {
			bufferevent_free(bev);
 
			delete status;
			status = NULL;
 
			return;
		}
 
		//刷新缓冲
		bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
 
		return;
	}
 
	if(!status->fp){
		return;
	}
 
	//读取本地文件
	char szData[1024] = {0};
	int nlen = fread(szData, 1, sizeof(szData), status->fp);
	if (nlen <= 0) {
		status->end = true;
 
		//刷新缓冲区
		bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
		return;
	}
 
	//发送文件内容
	bufferevent_write(bev, szData, nlen);
}
 
void event_cb(struct bufferevent *bev, short what, void *ctx) {
	if (what & BEV_EVENT_EOF) {
	    //服务端关闭连接
		printf("Connection closed.\n");
 
		bufferevent_free(bev);
	} else if (what & BEV_EVENT_ERROR) {
	    //操作发生错误
		
		char sz[100] = { 0 };
	    #ifdef _WIN32
		strerror_s(sz, 100, );
		#else
		strerror_r(errno, sz, 100);
		#endif
		printf("Got an error on the connection: %s\n",sz);
 
		bufferevent_free(bev);
	} else if (what & BEV_EVENT_TIMEOUT && what & BEV_EVENT_READING) {
	    //读取超时
		printf("Read timeout.\n");
 
		bufferevent_free(bev);
	} else if (what & BEV_EVENT_TIMEOUT && what & BEV_EVENT_WRITING) {
	    //写入超时
		printf("Write timeout.\n");
 
		bufferevent_free(bev);
	} else if (what & BEV_EVENT_CONNECTED) {
	    //连接已经完成
		printf("Connection already finished.\n");
 
		//1、发送文件名
		bufferevent_write(bev, FILENAME, strlen(FILENAME));
 
		ClientStatus* status = new ClientStatus();
		status->fp = fopen(FILENAME, "rb");
 
		//初始化zlib上下文
		status->z_output = new z_stream();
		deflateInit(status->z_output, Z_DEFAULT_COMPRESSION);
 
		//创建输出过滤bufferevent,延迟回调
		bufferevent *bev_filter = bufferevent_filter_new(bev, 0, filter_out, BEV_OPT_CLOSE_ON_FREE| BEV_OPT_DEFER_CALLBACKS, 0, status);
 
		//为过滤bufferevent设置读写权限和回调函数
		bufferevent_setcb(bev_filter, read_cb, write_cb, event_cb, status);
		bufferevent_enable(bev_filter, EV_WRITE|EV_READ);
	}
}
 
int main(int argc, char* argv[]) {
#ifdef _WIN32
	WSADATA wsa;
	WSAStartup(MAKEWORD(2,2), &wsa);
#else
	if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		return 1;
#endif
 
	//创建libevent环境上下文
	event_base *base = event_base_new();
	if (base) {
		bufferevent *bev = NULL;
		if ((bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE)) != NULL) {
			//在基于套接字的bufferevent上启动连接
			sockaddr_in sin;
			memset(&sin, 0, sizeof(sin));
			sin.sin_family = AF_INET;
			sin.sin_port = htons(SPORT);
			sin.sin_addr.s_addr = inet_addr("127.0.0.1");
 
			//设置读写权限和相应的回调函数
			bufferevent_enable(bev, EV_WRITE|EV_READ);
			bufferevent_setcb(bev, 0, 0, event_cb, 0);
 
			//启动连接
			bufferevent_socket_connect(bev, (sockaddr*)&sin, sizeof(sin));
		}
	}
 
	if(base){
		event_base_dispatch(base);
    }
	
	if(base){
	    event_base_free(base);
    }
	
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
libevent是一个事件驱动的网络编程库,它提供了高效的事件处理机制,可以在多种操作系统平台上运行。为了更好地利用libevent提供的优势,我们可以使用线程池来管理多个并发请求。 下面是一个基于libevent的简单线程池实现: ```c #include <event2/event.h> #include <event2/thread.h> #include <pthread.h> #define THREAD_POOL_SIZE 4 struct thread_pool { pthread_t threads[THREAD_POOL_SIZE]; struct event_base *base; }; void* thread_func(void *arg) { struct event_base *base = (struct event_base*)arg; event_base_dispatch(base); return NULL; } struct thread_pool* thread_pool_new() { struct thread_pool *pool = malloc(sizeof(struct thread_pool)); pool->base = event_base_new(); // Initialize libevent thread support evthread_use_pthreads(); // Create worker threads for (int i = 0; i < THREAD_POOL_SIZE; i++) { pthread_create(&pool->threads[i], NULL, thread_func, pool->base); } return pool; } void thread_pool_destroy(struct thread_pool *pool) { // Stop the event loop on all threads event_base_loopexit(pool->base, NULL); // Wait for all threads to exit for (int i = 0; i < THREAD_POOL_SIZE; i++) { pthread_join(pool->threads[i], NULL); } // Free the event base and the pool itself event_base_free(pool->base); free(pool); } void thread_pool_dispatch(struct thread_pool *pool, struct event *ev) { event_base_priority_init(pool->base, 2); event_base_set(pool->base, ev); event_add(ev, NULL); } ``` 这个线程池使用libevent提供的事件循环机制来处理多个并发请求。它启动了一个包含多个线程的事件循环,每个线程都可以处理事件。当一个事件需要处理时,线程池会将该事件加入到事件队列中,由空闲的线程来处理。 线程池的使用非常简单,只需要创建一个线程池对象,然后通过调用`thread_pool_dispatch`函数来将需要处理的事件加入到事件队列中即可。当不再需要使用线程池时,只需要调用`thread_pool_destroy`函数来释放线程池对象和相关资源。 需要注意的是,这个线程池的实现是比较简单的,适用于处理轻量级的请求。如果需要处理更加复杂的请求,可能需要进行更多的优化和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值