http1.1的chunked解码解析.doc_第1页
http1.1的chunked解码解析.doc_第2页
http1.1的chunked解码解析.doc_第3页
http1.1的chunked解码解析.doc_第4页
http1.1的chunked解码解析.doc_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

HTTP1.1中CHUNKED编码解析一般HTTP通信时,会使用Content-Length头信息性来通知用户代理(通常意义上是浏览器)服务器发送的文档内容长度,该头信息定义于HTTP1.0协议RFC 1945 10.4章节中。浏览器接收到此头信息后,接受完Content-Length中定义的长度字节后开始解析页面,但如果服务端有部分数据延迟发送吗,则会出现浏览器白屏,造成比较糟糕的用户体验。解决方案是在HTTP1.1协议中,RFC 2616中14.41章节中定义的Transfer-Encoding: chunked的头信息,chunked编码定义在3.6.1中,所有HTTP1.1 应用都支持此使用trunked编码动态的提供body内容的长度的方式。进行Chunked编码传输的HTTP数据要在消息头部设置:Transfer-Encoding: chunked表示Content Body将用chunked编码传输内容。根据定义,浏览器不需要等到内容字节全部下载完成,只要接收到一个chunked块就可解析页面.并且可以下载html中定义的页面内容,包括js,css,image等。采用chunked编码有两种选择,一种是设定Server的IO buffer长度让Server自动flush buffer中的内容,另一种是手动调用IO中的flush函数。不同的语言IO中都有flush功能:l php: ob_flush(); flush(); l perl: STDOUT-autoflush(1); l java: out.flush(); l python: sys.stdout.flush() l ruby: stdout.flush 采用HTTP1.1的Transfer-Encoding:chunked,并且把IO的buffer flush下来,以便浏览器更早的下载页面配套资源。当不能预先确定报文体的长度时,不可能在头中包含Content-Length域来指明报文体长度,此时就需要通过Transfer-Encoding域来确定报文体长度。Chunked编码一般使用若干个chunk串连而成,最后由一个标明长度为0的chunk标示结束。每个chunk分为头部和正文两部分,头部内容指定下一段正文的字符总数(非零开头的十六进制的数字)和数量单位(一般不写,表示字节).正文部分就是指定长度的实际内容,两部分之间用回车换行(CRLF)隔开。在最后一个长度为0的chunk中的内容是称为footer的内容,是一些附加的Header信息(通常可以直接忽略)。上述解释过于官方,简而言之,chunked编码的基本方法是将大块数据分解成多块小数据,每块都可以自指定长度,其具体格式如下(BNF文法):Chunked-Body = *chunk /0至多个chunklast-chunk /最后一个chunktrailer /尾部CRLF /结束标记符chunk = chunk-size chunk-extension CRLFchunk-data CRLFchunk-size = 1*HEXlast-chunk = 1*(0) chunk-extension CRLFchunk-extension= *( ; chunk-ext-name = chunk-ext-val )chunk-ext-name = tokenchunk-ext-val = token | quoted-stringchunk-data = chunk-size(OCTET)trailer = *(entity-header CRLF)解释:l Chunked-Body表示经过chunked编码后的报文体。报文体可以分为chunk, last-chunk,trailer和结束符四部分。chunk的数量在报文体中最少可以为0,无上限;l 每个chunk的长度是自指定的,即,起始的数据必然是16进制数字的字符串,代表后面chunk-data的长度(字节数)。这个16进制的字符串第一个字符如果是“0”,则表示chunk-size为0,该chunk为last-chunk,无chunk-data部分。l 可选的chunk-extension由通信双方自行确定,如果接收者不理解它的意义,可以忽略。l trailer是附加的在尾部的额外头域,通常包含一些元数据(metadata, meta means about information),这些头域可以在解码后附加在现有头域之后下面分析用ethereal抓包使用Firefox与某网站通信的结果(从头域结束符后开始):Address 0. f000c0 31000d0 66 66 63 0d 0a . / ASCII码:1ffcrn, chunk-data数据起始地址为000d5显然,“1ffc”为第一个chunk的chunk-size,转换为int为8188。由于1ffc后,马上就是CRLF,因此没有chunk-extension。chunk-data的起始地址为000d5, 计算可知下一块chunk的起始地址为000d5+1ffc + 2=020d3,如下:020d0 . 0d 0a 31 66 66 63 0d 0a . / ASCII码:rn1ffcrn前一个0d0a是上一个chunk的结束标记符,后一个0d0a则是chunk-size和chunk-data的分隔符。此块chunk的长度同样为8188, 依次类推,直到最后一块100e0 0d 0a 31100f0 65 61 39 0d 0a. /ASII码:rn1ea9rn此块长度为0x1ea9 = 7849, 下一块起始为100f5 + 1ea9 + 2 = 11fa0,如下:11fa0 30 0d 0a 0d 0a /ASCII码:0rnrn“0”说明当前chunk为last-chunk, 第一个0d 0a为chunk结束符。第二个0d0a说明没有trailer部分,整个Chunk-body结束。解码流程:对chunked编码进行解码的目的是将分块的chunk-data整合恢复成一块作为报文体,同时记录此块体的长度。RFC2616中附带的解码流程如下:(伪代码)length := 0 /长度计数器置0read chunk-size, chunk-extension (if any) and CRLF /读取chunk-size, chunk-extension和CRLFwhile(chunk-size 0 ) /表明不是last-chunkread chunk-data and CRLF /读chunk-size大小的chunk-data,skip CRLFappend chunk-data to entity-body /将此块chunk-data追加到entity-body后read chunk-size and CRLF /读取新chunk的chunk-size 和 CRLFread entity-header /entity-header的格式为name:valueCRLF,如果为空即只有CRLFwhile (entity-header not empty) /即,不是只有CRLF的空行append entity-header to existing header fieldsread entity-headerContent-Length:=length /将整个解码流程结束后计算得到的新报文体length,作为Content-Length域的值写入报文中Remove chunked from Transfer-Encoding /同时从Transfer-Encoding中域值去除chunked这个标记length最后的值实际为所有chunk的chunk-size之和,在上面的抓包实例中,一共有八块chunk-size为0x1ffc(8188)的chunk,剩下一块为0x1ea9(7849),加起来一共73353字节。 注:对于上面例子中前几个chunk的大小都是8188,可能是因为:1ffc 4字节,rn2字节,加上块尾一个rn2字节一共8字节,因此一个chunk整体为8196,正好可能是发送端一次TCP发送的缓存大小。另附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 rn $chunk_size = (integer)hexdec(fgets( $socket_fd, 4096 ) );如果为0表明最后一个块,后面跟两个结束符结束符又是一个块3749字节Chunk后有个结束符这个块为3749字节,块数据结束后rn表明这个块已经结束自己指定长度,十六进制数ea5表明这个数据块有3749字节ea5 rn rn ea5 rn.rn 0rnrn 这个块为3749字节,块数据结束后rn表明这个块已经结束其C语言的解码如下,java思路相同int nBytes;char* pStart = a; / a中存放待解码的数据char* pTemp;char strlength10; /一个chunk块的长度chunk : pTemp =strstr(pStart,rn); if(NULL=pTemp) free(a); a=NULL; fclose(fp); return -1; length=pTemp-pStart; COPY_STRING(strlength,pStart,length); pStart=pTemp+2; nBytes=Hex2Int(strlength); /得到一个块的长度,并转化为十进制 if(nBytes=0)/如果长度为0表明为最后一个chunk free(a); fclose(fp); return 0; fwrite(pStart,sizeof(char),nBytes,fp);/将nBytes长度的数据写入文件中 pStart=pStart+nBytes+2; /跳过一个块的数据以及数据之后两个字节的结束符 fflush(fp); goto chunk; /goto到chunk继续处理如何将一个十进制数转化为十六进制char *

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论