HTTP协议的chunked编码:
一般的HTTP Headers 应该包含Content-Length来指名报文的长度。
但是在有的时候,服务器无法确定HTTP回应的消息的大小,比如非常大的文件的下载,或者处理的逻辑比较复杂,需要一边处理一边实时生成消息(如果全部处理完再生成消息就会有很多缺点,比如用户收到响应的时间就会很长),这个时候服务器一般都使用chunked编码。此时,服务器不会带上Content-Length这个响应头,带上了两外一个头:Transfer-encoding:chunked。
chunked编码使用若干个Chunk组成,由一个标明长度为0的chunk结束,每个Chunk有两部分组成,每个部分用回车换行隔开。在最后一个长度为0的Chunk中的内容是称为footer的内容,是一些没有写的头部内容。所以所谓的chunked编码是如下的格式:
格式:
如果一个HTTP消息(请求消息或应答消息)的Transfer-Encoding消息头的值为chunked,那么,消息体由数量未定的块组成,并以最后一个大小为0的块为结束。
每一个非空的块都以该块包含数据的字节数(字节数以十六进制表示)开始,跟随一个CRLF (回车及换行),然后是数据本身,最后块CRLF结束。在一些实现中,块大小和CRLF之间填充有白空格(0x20)。
最后一块是单行,由块大小(0),一些可选的填充白空格,以及CRLF。最后一块不再包含任何数据,但是可以发送可选的尾部,包括消息头字段。消息最后以CRLF结尾。
第一个chunk数据的字节数+/r/n+第一块chunk的数据 +/r/n+第二个chunk的数据的字节数+/r/n+第二块chunk的数据+n个chunk+/r/n+0+/r/n。
因此,接收数据的时候,需要首先获取每一个chunk数据的字节长度,然后,跳过2个字节(/r/n),取出数据,然后,再跳过2个字节(/r/n),获取下一个chunk的长度,直到最后一个chunk,最后一个chunk一定是0,并且字节的长度都是十六进制形式传输,需要进行相应的转化成十进制,如果是gzip格式的数据,那么,在最后完成所有数据组合之后,需要再解压。
def decode_chunked(content): content = content.lstrip('\r') content = content.lstrip('\n') temp = content.find('\r\n') strtemp = content[0:temp] readbytes = int(strtemp, 16) newcont = '' start = 2 offset = temp + 2 newcont = '' while(readbytes > 0): newcont += content[offset:readbytes + offset] offset += readbytes endtemp = content.find('\r\n', offset + 2) if(endtemp > -1): strtemp = content[offset + 2:endtemp] readbytes = int(strtemp, 16) if(readbytes == 0): break else: offset = endtemp + 2 content = newcont return content def chunk_download(url, dest): fd = urllib.urlopen(url) chunk = decode_chunked(fd.read()) fd.close() f = open(dest, 'w') f.write(chunk) f.close()
部分内容参考:
http://zh.wikipedia.org/wiki/%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81
http://blog.csdn.net/xiqi8144/article/details/4413626