我是靠谱客的博主 明理黄豆,最近开发中收集的这篇文章主要介绍GSoap启用gzip压缩源码解析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、相关结构

typedef struct z_stream_s
{
z_const Bytef *next_in;
/* 待压缩数据 */
uInt
avail_in;
/* 待压缩数据大小 */
uLong
total_in;
/* 到目前为止,所有压缩过的数据的原始大小 */
Bytef
*next_out;
/* 下一次存储压缩后数据的起始位置,最开始与soap->z_buf指向相同位置 */
uInt
avail_out;
/* next_out所指向内存的大小,即soap->z_buf所指内存的空闲大小 */
uLong
total_out;
/* 到目前为止,所有数据压缩后的大小 */
......
} z_stream;

二、Soap启用gzip压缩总流程

// 响应数据包含多部分,包括Headers、各附件类型、附加中的数据等;
一、响应数据处理:
1、初始化压缩相关对象:soap->d_stream、soap->z_buf等。

soap->d_stream->next_out、soap->z_buf最开始指向同一块内存
区别是:
soap->z_buf,指向内存的开始位置
soap->d_stream->next_out,指向剩余空闲内存的起始位置,用于存储压缩数据
当内存使用完毕时,将整块内存数据soap->z_buf取出处理,并清空内存中数据,同时soap->d_stream->next_out重新指向内存开始位置,表示该内存未被使用

2、尝试将一部分响应数据添加至输出缓冲区soap->buf,缓冲区size=65536;
3、若缓冲区soap->buf的剩余空间可以容纳该部分响应数据,则将数据追加到缓冲区后,转到步骤2,否则继续;
4、对soap->buf内数据进行crc32校验(最终的校验结果会作为响应的一部分);
5、利用第三方库Zlib,对soap->buf内数据进行压缩,压缩后数据存储在soap->d_stream->next_out所指内存中;
6、若步骤5中,soap->d_stream->next_out不足以容纳压缩后数据或该缓冲区已满,则在链表soap->blist增加一个首结点,并将缓冲区内数据拷贝到结点中,否则转到步骤5,继续对soap->buf剩下的数据进行压缩处理。

二、数据发送:
1、将soap->buf所有数据,按照4)-6)进行压缩处理
2、在链表soap->blist增加一个首结点,存储8位数据,前四位表示crc32校验和,后4位表示压缩前整个响应包的大小;
3、依次遍历链表 soap->blist 各结点;
4、利用select(),检查套接字是否可写,若可写则利用send()将结点数据发送出去;
5、跳转到步骤3,直至链表 soap->blist 末尾。

三、源码解析

1、准备阶段,对象初始化

//初始化压缩相关对象:soap->d_stream、soap->z_buf等
//soap->d_stream->next_out、soap->z_buf最开始指向同一块内存
//区别是:
//soap->z_buf,指向内存的开始位置
//soap->d_stream->next_out,指向剩余空闲内存的起始位置,用于存储压缩数据
//当内存使用完毕时,将整块内存数据soap->z_buf取出处理,并清空内存中数据
//同时soap->d_stream->next_out重新指向内存开始位置,表示该内存未被使用
#ifndef PALM_1
SOAP_FMAC1
int
SOAP_FMAC2
soap_begin_send(struct soap *soap)
{
......
#ifdef WITH_ZLIB
soap->z_ratio_out = 1.0;
if ((soap->mode & SOAP_ENC_ZLIB) && soap->zlib_state != SOAP_ZLIB_DEFLATE)
{
//1、初始化结构体soap->d_stream,soap->d_stream用于存储压缩相关数据
if (!soap->d_stream)
{
soap->d_stream = (z_stream*)SOAP_MALLOC(soap, sizeof(z_stream));
soap->d_stream->zalloc = Z_NULL;
soap->d_stream->zfree = Z_NULL;
soap->d_stream->opaque = Z_NULL;
soap->d_stream->next_in = Z_NULL;
}
//2、初始化缓冲区soap->z_buf
//soap->d_stream->next_out,存储下一次压缩后的数据;
//soap->d_stream->avail_out:缓冲区soap->d_stream->next_out大小,即soap->z_buf的空闲大小
if (!soap->z_buf)
soap->z_buf = (char*)SOAP_MALLOC(soap, sizeof(soap->buf));
soap->d_stream->next_out = (Byte*)soap->z_buf;
soap->d_stream->avail_out = sizeof(soap->buf);
......
}
}

2、数据压缩

//数据压缩和发送
#ifndef PALM_1
SOAP_FMAC1
int
SOAP_FMAC2
soap_end_send(struct soap *soap)
{
......
err = soap_putmime(soap);
// 数据压缩
......
return soap_end_send_flush(soap);
// 数据发送
}
//----------------------------------------------------------------
//遍历所有MIME附件,对数据进行压缩
#ifndef WITH_LEANER
#ifndef PALM_1
SOAP_FMAC1
int
SOAP_FMAC2
soap_putmime(struct soap *soap)
{
struct soap_multipart *content;
if (!(soap->mode & SOAP_ENC_MIME) || !soap->mime.boundary)
return SOAP_OK;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sending MIME attachmentsn"));
//1、遍历所有MIME附件
for (content = soap->mime.first; content; content = content->next)
{
void *handle;
//2、若指定了读取MIME附件的回调函数
if (soap->fmimereadopen && ((handle = soap->fmimereadopen(soap, (void*)content->ptr, content->id, content->type, content->description)) != NULL || soap->error))
{
......
}
else
{
//3、soap_putmimehdr():往soap->buf(输入/输出缓冲区)添加MIME Headers的各种信息,
//包括附件的类型等信息,会多次调用soap_send_raw()
if (soap_putmimehdr(soap, content)
|| soap_send_raw(soap, content->ptr, content->size))
//4、处理附件中的数据
return soap->error;
}
}
return soap_send3(soap, "rn--", soap->mime.boundary, "--");
}
#endif
#endif
//----------------------------------------------------------------
//s:待发送的数据
//n:待发送数据的大小
#ifndef PALM_1
SOAP_FMAC1
int
SOAP_FMAC2
soap_send_raw(struct soap *soap, const char *s, size_t n)
{
......
else if (soap->mode & SOAP_IO)
{
//1、计算soap->buf空闲大小
//soap->buf:发送/接收缓冲区,字符数组,size=SOAP_BUFLEN(65536)
//soap->bufidx:soap->buf已用空间
size_t i = sizeof(soap->buf) - soap->bufidx;
//2、剩余空间不够用
while (n >= i)
{
//2.1、从s所指向的内存中拷贝i个字节,追加到soap->buf中,保证soap->buf已填满
soap_memcpy((void*)(soap->buf + soap->bufidx), i, (const void*)s, i);
//2.2、修改缓冲区buf已用大小,为完全使用
soap->bufidx = sizeof(soap->buf);
//2.3、发送缓冲区内数据
if (soap_flush(soap))
return soap->error;
//2.4、修改s位置、剩余未处理内存大小和缓冲区buf空闲大小(65536)
s += i;
n -= i;
i = sizeof(soap->buf);
}
//3、剩余空间够用时 将s所指向内存追加到缓冲区buf中,soap_memcpy()第二个参数表示缓冲区空闲大小
soap_memcpy((void*)(soap->buf + soap->bufidx), sizeof(soap->buf) - soap->bufidx, (const void*)s, n);
//4、修改缓冲区buf已用大小
soap->bufidx += n;
}
......
}
//----------------------------------------------------------------
//数据压缩
#ifndef PALM_1
SOAP_FMAC1
int
SOAP_FMAC2
soap_flush(struct soap *soap)
{
//1、判断缓冲区 soap->buf 中是否有数据
size_t n = soap->bufidx;
if (n)
{
......
//1、将soap->buf的已用空间标记为0
soap->bufidx = 0;
#ifdef WITH_ZLIB
if (soap->mode & SOAP_ENC_ZLIB)
{
//2、标记本次待压缩的数据
soap->d_stream->next_in = (Byte*)soap->buf; //标记待压缩的数据,d_stream:压缩流
soap->d_stream->avail_in = (unsigned int)n; //标记待压缩数据的大小
#ifdef WITH_GZIP
//3、对缓冲区数据进行crc32校验
soap->z_crc = crc32(soap->z_crc, (Byte*)soap->buf, (unsigned int)n);
#endif
do
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Deflating %u bytesn", soap->d_stream->avail_in));
//4、利用第三方库Zlib,对数据(soap->d_stream->next_in)进行压缩
//压缩后的数据存储在soap->d_stream->next_out所指向的缓冲区,
//soap->d_stream->avail_out表示缓冲区空闲大小
if (deflate(soap->d_stream, Z_NO_FLUSH) != Z_OK)
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Unable to deflate: %sn", soap->d_stream->msg ? soap->d_stream->msg : SOAP_STR_EOS));
return soap->error = SOAP_ZLIB_ERROR;
}
//5、若存储压缩后数据的缓冲区已满 则soap->z_buf所指向数据添加至list 否则等待下次压缩
//soap->d_stream->next_out:存储压缩后数据的缓冲区,soap->d_stream->avail_out:soap->d_stream->next_out的空闲大小
if (!soap->d_stream->avail_out)
{
//soap_flush_raw():每次往链表soap->blist中新增一个结点,
//同时将soap->z_buf所指数据拷贝到结点内
if (soap_flush_raw(soap, soap->z_buf, sizeof(soap->buf)))
return soap->error;
soap->d_stream->next_out = (Byte*)soap->z_buf;
//将指针移到缓冲区最开始
soap->d_stream->avail_out = sizeof(soap->buf);
//修改存储压缩后数据的缓冲区大小
}
} while (soap->d_stream->avail_in);
}
else
#endif
return soap_flush_raw(soap, soap->buf, n);
}
return SOAP_OK;
}
#endif

3、数据发送

//发送链表soap->blist所有结点数据
#ifndef PALM_1
SOAP_FMAC1
int
SOAP_FMAC2
soap_end_send_flush(struct soap *soap)
{
if (soap->mode & SOAP_IO) /* need to flush the remaining data in buffer */
{
//1、将soap->buf所有数据,进行压缩处理
if (soap_flush(soap))
......
#ifdef WITH_ZLIB
if ((soap->mode & SOAP_ENC_ZLIB) && soap->d_stream)
{
int r;
soap->d_stream->avail_in = 0;
do
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Deflating remaindern"));
//2、告诉Zlib,所有数据已经提交完毕,请输出zlib数据尾
//如果deflate()返回Z_STREAM_END则表示数据尾也已经输出了
r = deflate(soap->d_stream, Z_FINISH);
if (soap->d_stream->avail_out != sizeof(soap->buf))
{
//3、将未处理的压缩后的数据 添加至链表 soap->blist
if (soap_flush_raw(soap, soap->z_buf, sizeof(soap->buf) - soap->d_stream->avail_out))
{
soap->zlib_state = SOAP_ZLIB_NONE;
//4、若失败,则取消压缩,并释放所有压缩相关数据
deflateEnd(soap->d_stream);
return soap->error;
}
soap->d_stream->next_out = (Byte*)soap->z_buf;
soap->d_stream->avail_out = sizeof(soap->buf);
}
} while (r == Z_OK);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Deflated total %lu->%lu bytesn", soap->d_stream->total_in, soap->d_stream->total_out));
//5、设置最终压缩率
soap->z_ratio_out = (float)soap->d_stream->total_out / (float)soap->d_stream->total_in;
soap->mode &= ~SOAP_ENC_ZLIB;
soap->zlib_state = SOAP_ZLIB_NONE;
//6、停止压缩,并释放所有压缩相关数据
if (deflateEnd(soap->d_stream) != Z_OK || r != Z_STREAM_END)
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Unable to end deflate: %sn", soap->d_stream->msg ? soap->d_stream->msg : SOAP_STR_EOS));
return soap->error = SOAP_ZLIB_ERROR;
}
#ifdef WITH_GZIP
if (soap->zlib_out != SOAP_ZLIB_DEFLATE)
{
//7、响应中增加crc校验结果
soap->z_buf[0] = soap->z_crc & 0xFF;
soap->z_buf[1] = (soap->z_crc >> 8) & 0xFF;
soap->z_buf[2] = (soap->z_crc >> 16) & 0xFF;
soap->z_buf[3] = (soap->z_crc >> 24) & 0xFF;
//8、响应中增加数据压缩前大小
soap->z_buf[4] = soap->d_stream->total_in & 0xFF;
soap->z_buf[5] = (soap->d_stream->total_in >> 8) & 0xFF;
soap->z_buf[6] = (soap->d_stream->total_in >> 16) & 0xFF;
soap->z_buf[7] = (soap->d_stream->total_in >> 24) & 0xFF;
if (soap_flush_raw(soap, soap->z_buf, 8))
return soap->error;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "gzip crc32=%lun", (unsigned long)soap->z_crc));
}
#endif
......
//9、遍历链表 soap->blist,将各结点数据发送出去
for (p = soap_first_block(soap, NULL); p; p = soap_next_block(soap, NULL))
{
DBGMSG(SENT, p, soap_block_size(soap, NULL));
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Send %u bytes to socket=%d/fd=%dn", (unsigned int)soap_block_size(soap, NULL), soap->socket, soap->sendfd));
//发送结点数据
soap->error = soap->fsend(soap, p, soap_block_size(soap, NULL));
if (soap->error)
{
soap_end_block(soap, NULL);
return soap->error;
}
}
//10、清空链表 soap->blist
soap_end_block(soap, NULL);
......
}
#endif
//----------------------------------------------------------------
//发送链表soap->blist中单个结点数据
#ifndef WITH_NOIO
#ifndef PALM_1
static int
fsend(struct soap *soap, const char *s, size_t n)
{
int nwritten, err;
SOAP_SOCKET sk;
......
sk = soap->sendsk;
if (!soap_valid_socket(sk))
sk = soap->socket;
while (n)
{
if (soap_valid_socket(sk))
{
if (soap->send_timeout)
{
for (;;)
{
int r;
//1、检查套接字sk是否可写
r = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, soap->send_timeout);
if (r > 0)
break;
}
}
......
//2、发送数据
nwritten = send(sk, s, (int)n, soap->socket_flags);
if (nwritten <= 0)
{
......
//发送失败
}
n -= nwritten;
s += nwritten;
}
}
}

最后

以上就是明理黄豆为你收集整理的GSoap启用gzip压缩源码解析的全部内容,希望文章能够帮你解决GSoap启用gzip压缩源码解析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部