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 attachments\n"));

    //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, "\r\n--", 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 bytes\n", 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: %s\n", 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 remainder\n"));

                //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 bytes\n", 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: %s\n", 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=%lu\n", (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=%d\n", (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;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
带gsoap-2.8源码,基于服务器客户端的实例,带自动生成服务客户端代码的批处理程序,及如何使用。带自己学习参考的教程; 0.解压附件,soapInterface.bat所在路径不得含中文 空格 1.新建头文件 取soapInterface.bat文件的同名:soapInterface.h 文件内编写接口,具体说明看附件参考的教程 //gsoap ns service name: gservice //gsoap ns service style: rpc int ns__add(int num1, int num2, int* result ); int ns__sub(int num1, int num2, int* result ); int ns__mult( int num1, int num2, int *result); int ns__divid( int num1, int num2, int *result); 2.从附件内gsoap-2.8包中搜索复制stdsoap2.h,stdsoap2.cpp,soapcpp2.exe, 存放于soapInterface.bat同级目录 3.双击soapInterface.bat运行。生成gClientSoap,gServerSoap两个文件夹,分别复制到服务器工程与客户端工程中使用 4.gClientSoap,gServerSoap两个文件夹内用到的文件功能说明,更多参考附件教程 1)soapC.cpp , soapH.h//soap的序列和反序列代码,它已经包含了soapStub.h 2)soapServer.c ppsoapServerLib.cpp //服务器端代码(纯C代码是soapServer.c soapServerLib.c ),soapServerLib.cpp文件则只是简单地包含soapServer.cpp和soapC.cpp 3)soapClient.cpp soapClientLib.cpp//客户端代码(纯C代码是soapClient.csoapClientLib.c ),soapClientLib.cpp文件则只是简单地包含soapClient.cpp和soapC.cpp 4) soapStub.h // soap的存根文件,定义了我们编写的头文件里对应的远程调用模型 5) add.nsmap //XML服务命名空间 6)服务器端要载入的文件有:soapServer.cpp,soapC.cpp,stdsoap2.cpp; 要包含的文件有:gservice.nsmap,soapH.h; 客户端要输入的文件有: soapClient.cpp,soapC.cpp,stdsoap2.cpp; 要包含的文件有:gservice.nsmap,soapH.h
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值