本文是《HTTP权威指南》读书笔记;
几乎所有的HTTP通信都是通过TCP/IP承载的,当HTTP要传送一些报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。因此HTTP连接实际上就是TCP连接和一些使用连接的规则。
1 Connection首部
HTTP允许在客户端与最终的源服务器中存在一串的HTTP实体(代理,网关,高速缓存等)。HTTP的Connection首部字段中有一个由逗号分隔的连接标签列表,这些标签为此连接指定了一些不会传播到其他连接中的选项。因此如果HTTP报文的连接标签中包含了HTTP首部字段的名称,则在将报文转发出去前,必须删除所有连接标签中出现的首部字段。
2. HTTP优化连接的方法(除了串行连接)
2.1 并行连接
通过多条TCP连接发起并发的HTTP请求。HTTP允许客户端打开多条连接,并行地执行多个HTTP事务。但打开多条连接会消耗大量的内存资源 ,引发自身的性能问题,并且一个WEB页面可能存在数百个对象(事务),服务器要处理多个用户的连接请求,多条连接也会等造成服务器性能的下降。虽然并行连接看起来会快一些(多个对象同时在加载),但实际上由于网络带宽等的限制,速度也没什么提升,但基本上还是会比串行连接快。实际上,浏览器是使用了并行连接的,但会将连接的个数限制在一个较小的值(通常为4个),并且服务器可以随意关闭来自同一个客户端的超量连接。
2.2 持久连接
重用TCP连接,消除连接与关闭的时延。
Web客户端经常会打开到同一站点的连接(Web页面里的内容大部分是来自同一个Web服务器),因此初始了对某服务器的
HTTP请求的应用程序很可能在不久的将来发起对该服务器的更多请求 (如获取图片等),这种性质称为站点的本地性。因此HTTP /1.1(或HTTP/1.0的增强版本)允许HTTP设备在事务处理完毕后,将TCP连接保持在打开状态,以便为未来的HTTP请求重用现在的连接。我们将在事务处理完毕后仍保持TCP为打开状态的连接为持久连接,直到客户端或服务器决定将其关闭。非持久连接会在事务处理完毕后关闭连接。重用已对目标服务器打开的空闲持久连接,可以避免缓慢的连接建立过程,还可以避免慢启动的拥塞适应阶段,以便可以快速地传送数据。但持久连接的管理需要非常小心,否则可以会累积出大量的空闲持久连接,浪费客户端与服务器的资源。
现在的Web应用程序一般都会将持久连接与并行连接配合使用(打开少量的并行连接,其中的每一个都是持久连接),以充分利用这两者的优点.
持久连接可以节省建立连接需要的时延,串行连接与持久连接的时间耗费对比:
持久连接又分为两种:HTTP/1.0+“keep-alive”持久连接和HTTP/1.1的“persistent”连接。
2.2.1 HTTP/1.0+“keep-alive”持久连接
实现HTTP/1.0+“keep-alive”持久连接的客户端可以通过包含Connection:Keep-Alive首部将请求将连接保持在打开状态,如果服务器愿意为下一条请求将连接保持在打开状态,则在响应中包含相同的Connection首部,如果在响应报文里没有Connection:Keep-Alive首部,则客户端会认为服务器不支持keep-alive,则会在发回响应报文之后将连接关闭。客户端和
服务器可以在任意时刻关闭空闲的“keep-alive”持久连接,并可以随意限制keep-alive连接可以处理的事务的数量。并且客户端发送的希望保持持久连接的报文里一定需要含有Connection:Keep-Alive首部,否则服务器就会在没有这个首部的请求之后关闭这个持久连接。并且报文的主体部分必须的正确的Content-Length字段来说明主体的长度,否则就不能使用持久连接,因为没有这个字段,处理的那一端没有办法判断这条报文的结束和下一条报文的开始。代理和网关要执行Connection规则。
由于HTTP请求在到达目的服务器的中间可能会经过若干个代理或网关,这就可能存在不能理解Connection首部的代理,将整个报文不经过处理(需要将Connection首部中出现的字段删除,包括了Connection:Keep-Alive)就直接转发出去,这就会导致哑代理(客户端认为服务器支持Keep-Alive,服务器认为客户端请求:Keep-Alive,但实际上代理根本就识别Keep-Alive首部):
2.2.2 HTTP/1.1的“persistent”连接
HTTP/1.1的“persistent”连接在默认情况下是激活的,因此如果要关闭一条持久连接,需要在报文中显示在添加Connectin:close 首部,否则HTTP/1.1连接就仍将维持在打开状态,但服务器或客户端仍可随时关闭空闲的连接。
使用HTTP/1.1的持久连接需要遵循以下规则:
- 实体主体部分的长度与Content-Length一致,或用分块传输编码方式编码;
- 每个持久连接只适合一跳传输,也就是中间的代理与网关要能够分别管理客户端和服务器的持久连接;
- 一个客户端最多可以与同一个服务器建立2条持久连接
2.3管道化连接
通过共享的TCP连接发起并发的HTTP请求;
HTTP/1.1的持久连接允许可选地使用请求管道。在响应到达之前,可以将多条请求放入队列,当第一条请求发送后,第二条,第三条请求也可以开始发送了:
使用管道连接需要遵循的规则:
若无法确定连接是持久的,就不能用管道;必须按照与请求相同的顺序回送HTTP响应,若响应失序,则会没有办法将请求与响应对应起来;客户端需要做好连接会在任意时刻关闭的准备;客户端不应该用管道发送一些会产生副作用的请求(POST)
2.4 复用的连接
交替传送请求与响应报文(实验阶段)
3 HTTP连接的关闭
TCP连接是双向的,每一端都会有一个输入与输出信道,应用程序可以关闭这两个信道的任意一个,通过套接字调用close()会关闭输入与输出信道,这叫完全关闭,调用shutdown()会关掉其中的一个,这称为半关闭。
使用半关闭时,关闭输出信道总是安全的,另一端会在其缓冲区中读完所有数据后收到一个通知,说明流结束了,它就可以知道连接关闭了。但关闭输入信道是比较危险的。如果另一端向已经关闭的输入信道发送数据,操作系统会向另一端的的机器发送一个TCP“连接被对端重置”的报文,大多数操作系统会认为这是一个严重的错误,会删除对端还未读取的所有的缓冲区里的数据,这对于管道化连接来说是致命的(许多还没有被读取的正确的响应数据都被清空了);
因此,应用程序在关闭连接时,应先关闭输出信道,然后等待另一端的实体关闭它的输出信道,之后就可以关闭输入信道了。