• 笔记整理——C语言-http


    C语言 HTTP GZIP数据解压 - 大烧饼的实验室 - 博客园 - Google Chrome (2013/4/10 18:22:26)

    C语言 HTTP GZIP数据解压

     

    这个代码在http://www.j286.com/有更新详情访问。

    好吧~我承认..我是为了写博文在拆分工程- -!!!

    在想要对gzip进行解压之前请确定你的数据是否需要做这个处理http://www.cnblogs.com/dsblab/articles/2328890.html

    HTTP 头中标识数据经过gzip的如上面第二个框。

    对gzip的处理我们可以用强大的zlib库进行处理

    C语言的zlib编译

    http://www.cnblogs.com/dsblab/articles/2328883.html

    http://www.cnblogs.com/dsblab/articles/2328880.html

    我不是在推销...没这你做不下去,如果你是linux下作开发的话编译应该那应该是小菜了。

    在使用前需要包含相关文件和库

    #include "zlib/zlib.h"
    #pragma comment(lib, "zlib.lib")

    当然解压数据长度你可以分配一个稍微大一些的空间来存储再realloc还是索性一个大数组,或者用其他zlib提供的函数就出在分配,这个要看你自己了。

    注意,使用的时候不要把头文件穿进去,把DATA段传进去就行了,否则会怎么样我不知道。。。没试过C#的stream

    复制代码
     1 /* HTTP gzip decompress */
    2 /* 参数1.压缩数据 2.数据长度 3.解压数据 4.解压后长度 */
    3 int httpgzdecompress(Byte *zdata, uLong nzdata, Byte *data, uLong *ndata)
    4 {
    5 int err = 0;
    6 z_stream d_stream = {0}; /* decompression stream */
    7 static char dummy_head[2] =
    8 {
    9 0x8 + 0x7 * 0x10,
    10 (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
    11 };
    12 d_stream.zalloc = (alloc_func)0;
    13 d_stream.zfree = (free_func)0;
    14 d_stream.opaque = (voidpf)0;
    15 d_stream.next_in = zdata;
    16 d_stream.avail_in = 0;
    17 d_stream.next_out = data;
    18 if(inflateInit2(&d_stream, 47) != Z_OK) return -1;
    19 while (d_stream.total_out < *ndata && d_stream.total_in < nzdata)
    20 {
    21 d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
    22 if((err = inflate(&d_stream, Z_NO_FLUSH)) == Z_STREAM_END) break;
    23 if(err != Z_OK )
    24 {
    25 if(err == Z_DATA_ERROR)
    26 {
    27 d_stream.next_in = (Bytef*) dummy_head;
    28 d_stream.avail_in = sizeof(dummy_head);
    29 if((err = inflate(&d_stream, Z_NO_FLUSH)) != Z_OK)
    30 {
    31 return -1;
    32 }
    33 }
    34 else return -1;
    35 }
    36 }
    37 if(inflateEnd(&d_stream) != Z_OK) return -1;
    38 *ndata = d_stream.total_out;
    39 return 0;
    40 }
    复制代码



    如果对平台没要求的话还是建议用C#来做好了,省力省时

    复制代码
            HttpWebResponse getRespone = (HttpWebResponse)get.GetResponse();
    if (getRespone != null && getRespone.StatusCode == HttpStatusCode.OK)
    {
    recv = getRespone.ContentEncoding;
    System.IO.Stream resStream = getRespone.GetResponseStream();
    if (recv == "gzip")
    {
    resStream = new GZipStream(resStream, CompressionMode.Decompress);
    }
    System.IO.StreamReader sr = new System.IO.StreamReader(resStream, Encoding.UTF8);
    recv = sr.ReadToEnd();
    sr.Close();
    }
    getRespone.Close();
    复制代码

    C语言 HTTP中的chunked解码实现 - 大烧饼的实验室 - 博客园 - Google Chrome (2013/4/10 18:08:07)

    C语言 HTTP中的chunked解码实现

     

    C语言不像C#一样有很多很多高度的模块化的东西可以使用,在通讯过程中特别是与http相关的通讯过程中可能要对网站返回的数据做一定处理,而且有不少网站的回应是强制性的,例如向网站请求deflate有个能会返回的是gzip的数据。在这过程中与web特性有关的,在服务器构造消息之前可能并不知道或者不方便知道消息的长度,于是就会将消息分为一段段进行传送。

    例如如下回应:

    不难发现,chunked正式实现这一思想的方式。

    每个HTTP头部含有Transfer-Encoding: chunked则表明此包Data是分块传输的。关于他的介绍痿基百科上说的很多,有兴趣可以看看http://en.wikipedia.org/wiki/Chunked_transfer_encoding

    实质上我们做的工作就是要对HTTP返回的头部含有Transfer-Encoding: chunked的数据做解码工作。

    数据结构如下:长度[HEX] 内容 长度[HEX] 内容

    解码过程当然很简单,没涉及任何数学知识,或者更本谈不上解码,知识做合并工作罢了。。。

    C语言能用的找了10分钟百度不到,除了一段满是奇怪参缺少函数的数狗啃的,索性干脆自己写一个算了。。。

    以下是C(C++)实现代码,在1块长度112下测试通过,应该没什么BUG。有的话烦请只出,代码应该算得上严谨了。

    复制代码
     1 /*
    2 * 十六进制表示的字符串转换为相应的十进制值 传入"7f"返回127
    3 */
    4 int htoi(unsigned char *s)
    5 {
    6 int i;
    7 int n = 0;
    8 if (s[0] == '0' && (s[1]=='x' || s[1]=='X')) //判断是否有前导0x或者0X
    9 {
    10 i = 2;
    11 }
    12 else
    13 {
    14 i = 0;
    15 }
    16 for (; (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z');++i)
    17 {
    18 if (tolower(s[i]) > '9')
    19 {
    20 n = 16 * n + (10 + tolower(s[i]) - 'a');
    21 }
    22 else
    23 {
    24 n = 16 * n + (tolower(s[i]) - '0');
    25 }
    26 }
    27 return n;
    28 }
    29
    30 /*
    31 * 查找关键数据串在长数据中出现的位置
    32 * 参数:1长数据指针,2搜索最大长度,3关键字指针,4关键字长度,5搜索起始位置(返回出现位置,若未找到则不变)
    33 * 返回:返回1 成功 返回 0 未找到
    34 */
    35 int _find_key(unsigned char *data,int data_length,unsigned char *key,int key_length,int *position)
    36 {
    37 int i = *position;
    38 if(key == NULL || i<0)
    39 {
    40 return 0;
    41 }
    42 for(; i <= data_length-key_length; i++)
    43 {
    44 if( memcmp(data+i, key, key_length) == 0 )
    45 {
    46 *position = i;
    47 return 1;
    48 }
    49 }
    50 return 0;
    51 }
    52
    53 /*
    54 * 对HTTP的chunked消息进行合块
    55 * 参数:1待处理数据,2数据长度(分配的长度即可,不一定要求出实际有效长度),3返回合块后的数据,4合块长度
    56 * 算法具有前驱性,返回和传入data可以是同一块内存区域(不建议)
    57 */
    58 int de_chunked(unsigned char *data,int data_length,unsigned char *dest,int *dest_length)
    59 {
    60 char chunked_hex[CHUNKED_MAX_LEN + 1]; // 十六进制的块长度
    61 int chunked_len; // 块长度
    62 int ret;
    63 int begin = 0;
    64 int end = 0;
    65 int i = 0;
    66 int index = 0;
    67
    68 ret = _find_key(data,data_length,"0 ",5,&end);
    69 if (ret == 0) //信息不完整
    70 return 0;
    71
    72 ret = _find_key(data,data_length," ",4,&begin);
    73 begin = begin + 4; //移动到数据起点
    74
    75 while(memcmp(data+begin,"0 ",5) != 0)
    76 {
    77 //获得当前块长度
    78 ret = _find_key(data+begin,CHUNKED_MAX_LEN," ",2,&i);
    79 if (ret == 0) //信息不完整
    80 return 0;
    81 memcpy(chunked_hex,data+begin,i);
    82 chunked_hex[i] = '';
    83 chunked_len = htoi(chunked_hex);
    84 //移动到当前块数据段
    85 begin = begin + i + 2;
    86 //获得当前块数据
    87 if (memcmp(data+begin+chunked_len," ",2) != 0)
    88 return 0; //信息有误
    89 memcpy(dest+index,data+begin,chunked_len);
    90 index = index + chunked_len;
    91 //移动到下一块块长度
    92 begin = begin + chunked_len + 2;
    93 i = begin;
    94 if(begin > end) //结构错误
    95 return -1;
    96 }
    97 *dest_length = index;
    98 return 1;
    99 }
    复制代码

    页面内url提取版本1.0 (2013/4/9 15:11:06)

    大体流程
    1. 申请存储空间,永不释放。未解压的页面内容存储空间,解压的页面内容存储空间,套接字,http头信息,缓冲数据区等
    2. 解析url的域名和路径
    3. 将域名转化为ip(gethostbyname),但需要一个建立一个缓冲区,来统计已经被解析的,并设置超时
    4. 创建套接字,发送连接请求
    5. 发送http头请求
    6. 接收http应答
    7. 判断应答状态和判断传输方式(chunked和 other)
    8. 根据状态判断是否接收,根据传输方式选择接收方式
    9. 关闭套接字
    10. 解压,声称明文
    11. 提取页面中的url。排除特殊形式例如,css、js、xml、ico、javascript和拼接形式生成的url等,处理相对路径的连接(#、/、./、../、以字母开始的等等),对url进行正则匹配,对URl进行惟一行确认,保存
    http://wenda.tianya.cn/tag/6efcf78d5abecc77/count/180

    HTTP协议之Chunked解析 - LeadBBS.COM 极速ASP论坛 - Google Chrome (2013/4/2 11:51:32)

    HTTP协议之Chunked解析
    在网上找了好一会,始终没发现有解析Chunked编码的文章,那就自己写一个吧,呵呵。
    网上使用Chunked编码的网站似乎并不是很多,除了那些使用GZip压缩的网站,例:google.com,还有就是大部分打开GZip压缩的PHP论坛。
    根据本人的理解,使用Chunked编码的主要好处就在于一些程序的运算出过程中,可以动态的输出内容。
    例如,要在后台处理一个小时的运算,但又不希望用户等一个小时才能看到结果。这时就可采用Chunked编码将内容分块输出,用户随时都可以接收到最新的处理结果。
    ASP关闭了缓存的输出模式,就是Chunked编码的。(Response.Buffer = false)
    而每一次的Response.Write,都是一个Chunked,所以不要使用的太频繁哦,否则Chunk数量太多,额外的数据太浪费空间了。
    若想了解Chunked的具体编码结构,用ASP关闭缓存调试蛮方便的。:)

    我们先来看看RFC2616中对Chunked的定义:
    Chunked-Body = *chunk
    last-chunk
    trailer
    CRLF

    chunk = chunk-size [ chunk-extension ] CRLF
    chunk-data CRLF
    chunk-size = 1*HEX
    last-chunk = 1*("0") [ chunk-extension ] CRLF

    chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
    chunk-ext-name = token
    chunk-ext-val = token | quoted-string
    chunk-data = chunk-size(OCTET)
    trailer = *(entity-header CRLF)

    我们来模拟一下数据结构:
    [Chunk大小][回车][Chunk数据体][回车][Chunk大小][回车][Chunk数据体][回车][0][回车]

    注意chunk-size是以十六进制的ASCII码表示的,比如86AE(实际的十六进制应该是:38366165),计算成长度应该是:34478,表示从回车之后有连续的34478字节的数据。
    跟踪了www.yahoo.com的返回数据,发现在chunk-size中,还会多一些空格。可能是固定长度为7个字节,不满7个字节的,就以空格补足,空格的ASCII码是0x20。

    以下是解码过程的伪代码:
    length := 0//用来记录解码后的数据体长度
    read chunk-size, chunk-extension (if any) and CRLF//第一次读取块大小
    while (chunk-size > 0) {//一直循环,直到读取的块大小为0
    read chunk-data and CRLF//读取块数据体,以回车结束
    append chunk-data to entity-body//添加块数据体到解码后实体数据
    length := length + chunk-size//更新解码后的实体长度
    read chunk-size and CRLF//读取新的块大小
    }
    read entity-header//以下代码读取全部的头标记
    while (entity-header not empty) {
    append entity-header to existing header fields
    read entity-header
    }
    Content-Length := length//头标记中添加内容长度
    Remove "chunked" from Transfer-Encoding//头标记中移除Transfer-Encoding


    有空再研究一下GZip+Chunked是如何编码的,估计是每个Chunk块进行一次GZip独立压缩。

    使用了Chunked,自然会在性能上稍微打点折扣,因为比正常的数据体多出了一些额外的消耗。
    但是有一些情况下,必需要使用分块输出,这也是不得已而为之~^_^


    进行Chunked编码传输的HTTP Response会在消息头部设置:

    Transfer-Encoding: chunked

    表示Content Body将用Chunked编码传输内容。

    Chunked编码使用若干个Chunk串连而成,由一个标明长度为0的chunk标示结束。每个Chunk分为头部和正文两部分,头部内容指定下一段正文的字符总数(十六进制的数字)和数量单位(一般不写),正文部分就是指定长度的实际内容,两部分之间用回车换行(CRLF)隔开。在最后一个长度为0的Chunk中的内容是称为footer的内容,是一些附加的Header信息(通常可以直接忽略)。具体的Chunk编码格式如下:

      Chunked-Body = *chunk
    "0" CRLF
    footer
    CRLF 
    chunk = chunk-size [ chunk-ext ] CRLF
    chunk-data CRLF

      hex-no-zero = <HEX excluding "0">

      chunk-size = hex-no-zero *HEX
    chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] )
    chunk-ext-name = token
    chunk-ext-val = token | quoted-string
    chunk-data = chunk-size(OCTET)

      footer = *entity-header

    RFC文档中的Chunked解码过程如下:
    length := 0
    read chunk-size, chunk-ext (if any) and CRLF
    while (chunk-size > 0) {
    read chunk-data and CRLF
    append chunk-data to entity-body
    length := length + chunk-size
    read chunk-size and CRLF
    }
    read entity-header
    while (entity-header not empty) {
    append entity-header to existing header fields
    read entity-header
    }
    Content-Length := length
    Remove "chunked" from Transfer-Encoding

    最后提供一段PHP版本的chunked解码代码:

    $chunk_size = (integer)hexdec(fgets( $socket_fd, 4096 ) );
    while(!feof($socket_fd) && $chunk_size > 0) {
        $bodyContent .= fread( $socket_fd, $chunk_size );
        fread( $socket_fd, 2 ); // skip
        $chunk_size = (integer)hexdec(fgets( $socket_fd, 4096 ) );
    }





    附件列表

  • 相关阅读:
    思达BI软件Style Intelligence实例教程—食品平均价格变动情况分析
    思达BI软件Style Intelligence实例教程—原盐产量分析
    思达BI软件Style Intelligence实例教程—综合能源分析
    思达BI软件Style Intelligence实例教程—交通事故分析
    思达BI软件Style Intelligence实例教程—科技经费投入情况分析
    思达BI软件Style Intelligence实例教程—某高校录取情况分析
    80端口被系统占用,无法kill
    面试题目: PHP 有哪些优缺点?
    面试题目: 获取服务器IP和客户端IP
    Wordpress 忘记密码怎么办?
  • 原文地址:https://www.cnblogs.com/stlong/p/6289086.html
Copyright © 2020-2023  润新知