一、zlib库中的API
zlib是非常著名的压缩工具,其官网:[zlib官网]
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;
}