Transfer-Encoding,是一个 HTTP 头部字段,字面意思是「传输编码」。实际上,HTTP 协议中还有另外一个头部与编码有关:Content-Encoding(内容编码)。Content-Encoding 通常用于对实体内容进行压缩编码,目的是优化传输,例如用 gzip 压缩文本文件,能大幅减小体积。内容编码通常是选择性的,例如 jpg / png 这类文件一般不开启,因为图片格式已经是高度压缩过的,再压一遍没什么效果不说还浪费 CPU。

而 Transfer-Encoding 则是用来改变报文格式,它不但不会减少实体内容传输大小,甚至还会使传输变大,那它的作用是什么呢?本文接下来主要就是讲这个。我们先记住一点,Content-Encoding 和 Transfer-Encoding 二者是相辅相成的,对于一个 HTTP 报文,很可能同时进行了内容编码和传输编码。

Persistent Connection

暂时把 Transfer-Encoding 放一边,我们来看 HTTP 协议中另外一个重要概念:Persistent Connection(持久连接,通俗说法长连接)。我们知道 HTTP 运行在 TCP 连接之上,自然也有着跟 TCP 一样的三次握手、慢启动等特性,为了尽可能的提高 HTTP 性能,使用持久连接就显得尤为重要了。为此,HTTP 协议引入了相应的机制。

HTTP/1.0 的持久连接机制是后来才引入的,通过 Connection: keep-alive 这个头部来实现,服务端和客户端都可以使用它告诉对方在发送完数据之后不需要断开 TCP 连接,以备后用。HTTP/1.1 则规定所有连接都必须是持久的,除非显式地在头部加上 Connection: close。所以实际上,HTTP/1.1 中 Connection 这个头部字段已经没有 keep-alive 这个取值了,但由于历史原因,很多 Web Server 和浏览器,还是保留着给 HTTP/1.1 长连接发送 Connection: keep-alive 的习惯。

浏览器重用已经打开的空闲持久连接,可以避开缓慢的三次握手,还可以避免遇上 TCP 慢启动的拥塞适应阶段,听起来十分美妙。为了深入研究持久连接的特性,我决定用 Node 写一个最简单的 Web Server 用于测试,Node 提供了 http 模块用于快速创建 HTTP Web Server,但我需要更多的控制,所以用 net 模块创建了一个 TCP Server:

1 JSrequire('net').createServer(function(sock) {
2     sock.on('data', function(data) {
3         sock.write('HTTP/1.1 200 OK\r\n');
4         sock.write('\r\n');
5         sock.write('hello world!');
6         sock.destroy();
7     });
8 }).listen(9090, '127.0.0.1');

启动服务后,在浏览器里访问 127.0.0.1:9090,正确输出了指定内容,一切正常。去掉 sock.destroy() 这一行,让它变成持久连接,重启服务后再访问一下。这次的结果就有点奇怪了:迟迟看不到输出,通过 Network 查看请求状态,一直是 pending。

这是因为,对于非持久连接,浏览器可以通过连接是否关闭来界定请求或响应实体的边界;而对于持久连接,这种方法显然不奏效。上例中,尽管我已经发送完所有数据,但浏览器并不知道这一点,它无法得知这个打开的连接上是否还会有新数据进来,只能傻傻地等了。

Content-Length

要解决上面这个问题,最容易想到的办法就是计算实体长度,并通过头部告诉对方。这就要用到 Content-Length 了,改造一下上面的例子:

1 JSrequire('net').createServer(function(sock) {
2     sock.on('data', function(data) {
3         sock.write('HTTP/1.1 200 OK\r\n');
4         sock.write('Content-Length: 12\r\n');
5         sock.write('\r\n');
6         sock.write('hello world!');
7     });
8 }).listen(9090, '127.0.0.1');

可以看到,这次发送完数据并没有关闭 TCP 连接,但浏览器能正常输出内容并结束请求,因为浏览器可以通过 Content-Length 的长度信息,判断出响应实体已结束。那如果 Content-Length 和实体实际长度不一致会怎样?有兴趣的同学可以自己试试,通常如果 Content-Length 比实际长度短,会造成内容被截断;如果比实体内容长,会造成 pending。

由于 Content-Length 字段必须真实反映实体长度,但实际应用中,有些时候实体长度并没那么好获得,例如实体来自于网络文件,或者由动态语言生成。这时候要想准确获取长度,只能开一个足够大的 buffer,等内容全部生成好再计算。但这样做一方面需要更大的内存开销,另一方面也会让客户端等更久。

我们在做 WEB 性能优化时,有一个重要的指标叫 TTFB(Time To First Byte),它代表的是从客户端发出请求到收到响应的第一个字节所花费的时间。大部分浏览器自带的 Network 面板都可以看到这个指标,越短的 TTFB 意味着用户可以越早看到页面内容,体验越好。可想而知,服务端为了计算响应实体长度而缓存所有内容,跟更短的 TTFB 理念背道而驰。但在 HTTP 报文中,实体一定要在头部之后,顺序不能颠倒,为此我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界。

Transfer-Encoding: chunked

本文主角终于再次出现了,Transfer-Encoding 正是用来解决上面这个问题的。历史上 Transfer-Encoding 可以有多种取值,为此还引入了一个名为 TE 的头部用来协商采用何种传输编码。但是最新的 HTTP 规范里,只定义了一种传输编码:分块编码(chunked)。

分块编码相当简单,在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的 CRLF(\r\n),也不包括分块数据结尾的 CRLF。最后一个分块长度值必须为 0,对应的分块数据没有内容,表示实体结束。按照这个格式改造下之前的代码:

 1 JSrequire('net').createServer(function(sock) {
 2     sock.on('data', function(data) {
 3         sock.write('HTTP/1.1 200 OK\r\n');
 4         sock.write('Transfer-Encoding: chunked\r\n');
 5         sock.write('\r\n');
 6
 7         sock.write('b\r\n');
 8         sock.write('01234567890\r\n');
 9
10         sock.write('5\r\n');
11         sock.write('12345\r\n');
12
13         sock.write('0\r\n');
14         sock.write('\r\n');
15     });
16 }).listen(9090, '127.0.0.1');

上面这个例子中,我在响应头中表明接下来的实体会采用分块编码,然后输出了 11 字节的分块,接着又输出了 5 字节的分块,最后用一个 0 长度的分块表明数据已经传完了。用浏览器访问这个服务,可以得到正确结果。可以看到,通过这种简单的分块策略,很好的解决了前面提出的问题。

前面说过 Content-Encoding 和 Transfer-Encoding 二者经常会结合来用,其实就是针对进行了内容编码(压缩)的内容再进行传输编码(分块)。下面是我用 telnet 请求测试页面得到的响应,可以看到对 gzip 内容进行的分块:

 1 BASH> telnet 106.187.88.156 80
 2
 3 GET /test.php HTTP/1.1
 4 Host: qgy18.qgy18.com
 5 Accept-Encoding: gzip
 6
 7 HTTP/1.1 200 OK
 8 Server: nginx
 9 Date: Sun, 03 May 2015 17:25:23 GMT
10 Content-Type: text/html
11 Transfer-Encoding: chunked
12 Connection: keep-alive
13 Content-Encoding: gzip
14
15 1f
16 �H���W(�/�I�J
17
18 0

转载于:https://www.cnblogs.com/linghu-java/p/11193886.html

Http Header的Transfer-Encoding相关推荐

  1. php transfer-encoding: chunked,php – 使用chunked transfer encoding和gzip

    我最近开始在我的网站上使用gzip,它在除Opera之外的所有浏览器上都起到了一种魅力作用,它给出了一个错误,即由于数据损坏而无法解压缩内容.从我可以从测试和谷歌搜索中收集到的,使用gzip和chun ...

  2. php transfer-encoding: chunked,“Transfer-Encoding: chunked” header in PHP

    问题 i want to add Transfer-Encoding: chunked header to the file that i'm outputing (its just generate ...

  3. HTTP协议header标头详解

    本文根据RFC2616(HTTP/1.1规范),参考 http://www.w3.org/Protocols/rfc2068/rfc2068 http://www.w3.org/Protocols/r ...

  4. python中的header_python中header是什么意思

    通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息.这两种类型的消息由一个起始行,一个或者多个头域,一个只是头域结束的空行和可选的消息体组成.HTTP的头域包括通用头,请求头,响应 ...

  5. http请求头部(header)详解

    通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息.这两种类型的消息由一个起始行,一个或者多个头域,一个只是头域结束的空行和可 选的消息体组成.HTTP的头域包括通用头,请求头,响 ...

  6. python header location_python中header是什么意思

    通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息.这两种类型的消息由一个起始行,一个或者多个头域,一个只是头域结束的空行和可选的消息体组成.HTTP的头域包括通用头,请求头,响应 ...

  7. python爬虫入门教程--快速理解HTTP协议(一)

    http协议是互联网里面最重要,最基础的协议之一,我们的爬虫需要经常和http协议打交道.下面这篇文章主要给大家介绍了关于python爬虫入门之快速理解HTTP协议的相关资料,文中介绍的非常详细,需要 ...

  8. 微软服务器应用软件,HTTP 服务器示例应用程序

    HTTP 服务器示例应用程序 05/31/2018 本文内容 下面的示例应用程序演示如何使用 HTTP 服务器 API 来执行服务器端任务. 第一个示例中包含的 "precomp" ...

  9. 再谈HTTP2性能提升之背后原理—HTTP2历史解剖

    即使千辛万苦,还是把网站升级到http2了,遇坑如<phpcms v9站http升级到https加http2遇到到坑>. 因为理论相比于 HTTP 1.x ,在同时兼容 HTTP/1.1 ...

  10. HTTP Content-Length与分块传输

    HTTP Content-Length与分块传输 Content-Length是HTTP 请求用来告知body实体大小的一个字段. 比如Content-Length:1076表示 body数据量为10 ...

最新文章

  1. 中国科学技术大学 中科大(USTC)UBUNTU源Linux镜像站IPV4/IPV6
  2. 【学习笔记】2019-Learning_UVOS_Through_Visual_Attention
  3. ArcMap导出属性表为Excel文件
  4. Delphi十进制和十六进制互转 Delphi 自带函数 IntToHex
  5. 对于前端js框架对于事件处理的应用场景探讨
  6. html div画三角,css画三角形
  7. 20年,只有谷歌曾经不同
  8. 安卓蓝牙键盘切换输入法_采用国产机械轴,三种连接模式轻松切换,TT G521上手体验...
  9. SpringBoot + Vue 开发前后端分离的旅游管理系统
  10. 矩池云Jupyterlab支持download as pdf
  11. 深入浅出设计模式之工厂模式
  12. 软件测试人员必备的7种思维方式
  13. 12.测试字体的练习
  14. 双十一淘宝抢购脚本,seleium模块
  15. PHPwebshell2022免杀bypass阿里云盾等所有安全设备
  16. photoshop中怎么绘制虚线
  17. 计算机术语我喜欢你,摩斯密码表白高级表白密码 摩斯密码我喜欢你数字
  18. 用anaconda启动Spyder报错
  19. 10分钟读懂技术分析经典—《日本蜡烛图技术》
  20. 步步扎进Java-泛型与集合

热门文章

  1. 将视图转为image_OpenShift 4.3 之 直接将Web应用当成Serverless运行
  2. php function curl token_最全PHP代码规范
  3. java8 内部类_Java8基础知识(五)内部类
  4. 用户文件夹安全权限设置只能复制即上传不能删除和修改名称和修改数据或写入数据等
  5. 了解 object.defineProperty 的基本使用方法(数据双向绑定的底层原理)
  6. BGD-py实现学习【1】[转载]
  7. python装饰器使用
  8. 2.Elasticsearch插件推荐及安装
  9. 【博客美化】09.评论带头像,且支持旋转
  10. Part4:Citrix 3D 技术Step by Step配置指导手册