HTTP CHUNKED C实现

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]\r\n内容\r\n长度[HEX]\r\n内容\r\n\0\r\n\r\n

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

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\r\n\r\n",5,&end);
69     if (ret == 0)    //信息不完整
70         return 0;
71 
72     ret = _find_key(data,data_length,"\r\n\r\n",4,&begin);
73     begin = begin + 4;    //移动到数据起点
74 
75     while(memcmp(data+begin,"0\r\n\r\n",5) != 0)
76     {
77         //获得当前块长度
78         ret = _find_key(data+begin,CHUNKED_MAX_LEN,"\r\n",2,&i);
79         if (ret == 0)    //信息不完整
80             return 0;
81         memcpy(chunked_hex,data+begin,i);
82         chunked_hex[i] = '\0';
83         chunked_len = htoi(chunked_hex);
84         //移动到当前块数据段
85         begin = begin + i + 2;
86         //获得当前块数据
87         if (memcmp(data+begin+chunked_len,"\r\n",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 }

 

posted @ 2014-03-31 11:23  葫芦娃Vs奥特曼  阅读(445)  评论(0)    收藏  举报