HTTP中的分块传输编码是怎么回事?

By liuzhijun, 2016-10-05, 分类: Http

http, Transfer-Encoding

通常情况下,HTTP的响应消息体 message body 是作为整包发送到客户端的,用头『Content-Length』 来表示消息体的长度, 这个长度对客户端非常重要,因为对于持久连接TCP并不会在请求完立马结束,而是可以发送多次请求/响应,客户端需要知道哪个位置才是响应消息的结束,以及后续响应的开始,因此Content-Length显得尤为重要,服务端必须精确地告诉客户端 message body 的长度是多少, 如果Content-Length 比实际返回的长度短,那么就会造成内容截断,如果比实体内容长,客户端就一直处于pendding状态,直到所有的 message body 都返回了请求才结束。

Web2.0的出现使得网页变得丰富多彩,内容也比早期的网页复杂很多,这样就会遇到一个问题,对于一个复杂的页面来说,如果是等到消息体完全创建好之后再计算出Content-Length返回给客户端的话,在客户端那边会有一个漫长的等待过程,而对于用户来说,一个页面的所能容忍的等待时间不超过3秒,因此如何让响应内容尽可能早的让用户看到是HTTP协议要考虑的问题。

分块传输编码(Transfer-Encoding)就是这样一种解决方案:它把数据分解成一系列数据块,并以多个块发送给客户端,服务器发送数据时不再需要预先告诉客户端发送内容的总大小,只需在响应头里面添加Transfer-Encoding: chunked,以此来告诉浏览器我使用的是分块传输编码,这样就不需要 Content-Length 了,这就是分块传输编码 Transfer-Encoding 的作用。

分块传输编码带来的好处:

在消息头中指定Transfer-Encoding: chunked 就表示整个response将使用分块传输编码来传输内容,一个完整的消息体由n个块组成,并以最后一个大小为0的块为结束。每个非空的块包括两部分,分别为:块的长度(用十六进制表示)后面跟一个CRLF (回车及换行),长度并不包括结尾的回车换行符。第二部分就是数据本身,同样以CRLF (回车及换行)结束。最后一块是单行,只由块大小(0)以及CRLF组成,不包含任何数据。

现在就可以用Python实现一个简单的 HTTP Server 来指定 Response 的头 Transfer-Encoding。

# -*- coding:utf-8 -*-

import socket

if __name__ == '__main__':
    PORT = 8000
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('127.0.0.1', PORT))
    sock.listen(1)
    print 'Serving HTTP on port %s ...' % PORT

    while 1:
        conn, addr = sock.accept()
        print conn, addr
        request = conn.recv(1024)
        # HTTP响应消息
        conn.sendall("HTTP/1.1 200 OK\r\n")  # status line

        conn.sendall("Content-Type: text/plain\r\n")
        conn.sendall("Transfer-Encoding: chunked\r\n")  # 声明分块传输编码
        conn.sendall("\r\n")  # 空行

        conn.sendall("b\r\n")  # 11个字节的长度
        conn.sendall("hello world\r\n")  # 消息体
        conn.sendall("0\r\n")  # 最后一块0长度
        conn.sendall("\r\n")
        conn.close()

用telnet请求测试:

telnet localhost 8000
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying fe80::1...
telnet: connect to address fe80::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
get HTTP/1.1 /              # 发送GET请求
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

b
hello world
0

那么什么时候会用到分块传输编码呢?Stack Overflow 上有一句非常精辟的总结:

Transfer-Encoding: chunked is needed when the total content length is unknown before the first bytes are sent.

相关参考: 一次完整的HTTP请求过程


关注公众号「Python之禅」(id:vttalk)获取最新文章 python之禅

猜你喜欢

2017-04-12
爬虫入门系列(二):优雅的HTTP库requests
2017-05-23
爬虫入门系列(五):正则表达式完全指南(上)
2016-11-09
HTTPS的中那些加密算法
2017-04-26
爬虫入门系列(四):HTML文本解析库BeautifulSoup
2016-07-12
一次完整的HTTP请求过程
2016-01-28
HTTP 缓存策略
2017-04-06
爬虫入门系列(一):快速理解HTTP协议
2017-04-16
爬虫入门系列(三):用 requests 构建知乎 API