﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-探索者</title><link>http://www.blogjava.net/zb-86/</link><description>深挖</description><language>zh-cn</language><lastBuildDate>Sat, 02 May 2026 06:23:28 GMT</lastBuildDate><pubDate>Sat, 02 May 2026 06:23:28 GMT</pubDate><ttl>60</ttl><item><title>Http chunked协议</title><link>http://www.blogjava.net/zb-86/archive/2010/11/25/338999.html</link><dc:creator>Bob.zhang</dc:creator><author>Bob.zhang</author><pubDate>Thu, 25 Nov 2010 02:51:00 GMT</pubDate><guid>http://www.blogjava.net/zb-86/archive/2010/11/25/338999.html</guid><wfw:comment>http://www.blogjava.net/zb-86/comments/338999.html</wfw:comment><comments>http://www.blogjava.net/zb-86/archive/2010/11/25/338999.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/zb-86/comments/commentRss/338999.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zb-86/services/trackbacks/338999.html</trackback:ping><description><![CDATA[<p>HTTP协议中的TRANFER-ENCODING：CHUNKED编码解析<br />
&nbsp;&nbsp;&nbsp; 通常情况下，Transfer-Encoding域的值应当为chunked,表明采用chunked编码方式来进行报文体的传输。chunked编码是HTTP/1.1 RFC里定义的一种编码方式，因此所有的HTTP/1.1应用都应当支持此方式。<br />
&nbsp;&nbsp;&nbsp; chunked编码的基本方法是将大块数据分解成多块小数据，每块都可以自指定长度，其具体格式如下（BNF文法）:<br />
&nbsp;&nbsp;&nbsp; Chunked-Body&nbsp;&nbsp; = *chunk&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //0至多个chunk<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; last-chunk&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //最后一个chunk <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; trailer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //尾部<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRLF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //结束标记符</p>
<p>&nbsp;&nbsp; chunk&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = chunk-size [ chunk-extension ] CRLF&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chunk-data CRLF<br />
&nbsp;&nbsp; chunk-size&nbsp;&nbsp;&nbsp;&nbsp; = 1*HEX<br />
&nbsp;&nbsp; last-chunk&nbsp;&nbsp;&nbsp;&nbsp; = 1*("0") [ chunk-extension ] CRLF</p>
<p>&nbsp;&nbsp; chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )<br />
&nbsp;&nbsp; chunk-ext-name = token<br />
&nbsp;&nbsp; chunk-ext-val&nbsp; = token | quoted-string<br />
&nbsp;&nbsp; chunk-data&nbsp;&nbsp;&nbsp;&nbsp; = chunk-size(OCTET)<br />
&nbsp;&nbsp; trailer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = *(entity-header CRLF)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 解释：<br />
&nbsp;&nbsp;&nbsp; Chunked-Body表示经过chunked编码后的报文体。报文体可以分为chunk, last-chunk，trailer和结束符四部分。chunk的数量在报文体中最少可以为0，无上限；每个chunk的长度是自指定的，即，起始的数据必然是16进制数字的字符串，代表后面chunk-data的长度（字节数）。这个16进制的字符串第一个字符如果是&#8220;0&#8221;，则表示chunk-size为0，该chunk为last-chunk,无chunk-data部分。可选的chunk-extension由通信双方自行确定，如果接收者不理解它的意义，可以忽略。<br />
&nbsp;&nbsp;&nbsp; trailer是附加的在尾部的额外头域，通常包含一些元数据（metadata, meta means "about information"），这些头域可以在解码后附加在现有头域之后。<br />
&nbsp;&nbsp;&nbsp; 实例分析：<br />
&nbsp;&nbsp;&nbsp; 下面分析用ethereal抓包使用Firefox与某网站通信的结果（从头域结束符后开始）：<br />
Address&nbsp; 0..........................&nbsp; f<br />
000c0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 31<br />
000d0&nbsp;&nbsp;&nbsp; 66 66 63 0d 0a ...............&nbsp;&nbsp; // ASCII码:1ffc\r\n, chunk-data数据起始地址为000d5<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很明显，&#8220;1ffc&#8221;为第一个chunk的chunk-size,转换为int为8188.由于1ffc后马上就是<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRLF,因此没有chunk-extension.chunk-data的起始地址为000d5, 计算可知下一块chunk的起始<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 地址为000d5+1ffc + 2=020d3,如下：<br />
020d0&nbsp;&nbsp;&nbsp; .. 0d 0a 31 66 66 63 0d 0a .... // ASCII码:\r\n1ffc\r\n<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 前一个0d0a是上一个chunk的结束标记符，后一个0d0a则是chunk-size和chunk-data的分隔符。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 此块chunk的长度同样为8188, 依次类推，直到最后一块<br />
100e0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0d 0a 31<br />
100f0&nbsp;&nbsp;&nbsp; 65 61 39 0d 0a......&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //ASII码：\r\n\1ea9\r\n<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 此块长度为0x1ea9 = 7849, 下一块起始为100f5 + 1ea9 + 2 = 11fa0,如下：<br />
100a0&nbsp;&nbsp;&nbsp; 30 0d 0a 0d 0a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //ASCII码：0\r\n\r\n<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;0&#8221;说明当前chunk为last-chunk, 第一个0d 0a为chunk结束符。第二个0d0a说明没有trailer部分，整个Chunk-body结束。<br />
&nbsp;&nbsp;&nbsp; 解码流程：<br />
&nbsp;&nbsp;&nbsp; 对chunked编码进行解码的目的是将分块的chunk-data整合恢复成一块作为报文体，同时记录此块体的长度。<br />
&nbsp;&nbsp;&nbsp; RFC2616中附带的解码流程如下：(伪代码）<br />
&nbsp;&nbsp;&nbsp; length := 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //长度计数器置0<br />
&nbsp;&nbsp;&nbsp; read chunk-size, chunk-extension (if any) and CRLF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //读取chunk-size, chunk-extension<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //和CRLF<br />
&nbsp;&nbsp;&nbsp; while(chunk-size &gt; 0 )&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //表明不是last-chunk<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read chunk-data and CRLF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //读chunk-size大小的chunk-data,skip CRLF<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; append chunk-data to entity-body&nbsp;&nbsp;&nbsp;&nbsp; //将此块chunk-data追加到entity-body后<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read chunk-size and CRLF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //读取新chunk的chunk-size 和 CRLF<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; read entity-header&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //entity-header的格式为name:valueCRLF,如果为空即只有CRLF<br />
&nbsp;&nbsp;&nbsp; while （entity-header not empty)&nbsp;&nbsp; //即，不是只有CRLF的空行<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; append entity-header to existing header fields<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read entity-header<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; Content-Length:=length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //将整个解码流程结束后计算得到的新报文体length<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //作为Content-Length域的值写入报文中<br />
&nbsp;&nbsp;&nbsp; Remove "chunked" from Transfer-Encoding&nbsp; //同时从Transfer-Encoding中域值去除chunked这个标记<br />
&nbsp;&nbsp;&nbsp; length最后的值实际为所有chunk的chunk-size之和，在上面的抓包实例中，一共有八块chunk-size为0x1ffc(8188)的chunk,剩下一块为0x1ea9(7849),加起来一共73353字节。<br />
&nbsp;&nbsp;&nbsp; 注：对于上面例子中前几个chunk的大小都是8188,可能是因为:"1ffc" 4字节，"\r\n"2字节，加上块尾一个"\r\n"2字节一共8字节，因此一个chunk整体为8196,正好可能是发送端一次TCP发送的缓存大小。</p>
<img src ="http://www.blogjava.net/zb-86/aggbug/338999.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zb-86/" target="_blank">Bob.zhang</a> 2010-11-25 10:51 <a href="http://www.blogjava.net/zb-86/archive/2010/11/25/338999.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>