1.TCP连接
几乎所有的HTTP通信都有由TCP/IP承载的,TCP/IP是全球计算机及网络设备都在使用的一种常用的分组交换网络分层协议集。一旦连接建立起来,客户端和服务器之间交换的报文就永远不会丢失、受损或失序。但计算机或网络崩溃,会使通信终端。
1.1.TCP可靠数据管道
TCP为HTTP提供了一条可靠的比特传输管道。
浏览器接受到一个URL的时候,会执行以下步骤:
#1:客户端解析出主机名;
#2:客户端查询这个主机名的IP地址(DNS);
#3:客户端解析出端口号;
#4:客户端发起到对应IP和端口的连接;
#5:客户端向服务器发送一条HTTP GET请求报文;
#6:客户端等待响应,响应回送客户端,客户端读取HTTP响应报文;
#7:关闭连接。
1.2.数据分段传送
HTTP要传送报文时,会以流的形式将报文数据内容通过已经打开的TCP连接按序传输。TCP收到数据流后,会将数据流切割成小数据块,封装在IP分组中,通过Internet传输。所有这些工作都由TCP/IP软件来处理。
每个IP分组都包括:
#1:一个IP分组首部(通常20字节);
#2:一个TCP段首部(通常20字节);
#3:一个TCP数据块(0到n个字节)。
1.3.TCP连接识别
TCP连接是通过4个值来识别的:
<源IP地址 , 源端口号 , 目的IP地址 , 目的端口号>
俩条不同的TCP连接不能拥有4个完全相同的地址组件值。
2.TCP性能
HTTP位于TCP上层,所以HTTP的性能很大程度上取决于底层TCP通道的性能。
2.1.HTTP事务时延原因
#1:客户端根据URL确定web服务器的IP地址和端口。通过DNS解析系统将URL的主机名换成一个IP地址可能需要耗费数十秒的时间,如果近期有有访问过,则客户端会缓存起来,这时候就不需要访问DNS了,但这个DNS缓存不大;
#2:客户端向服务器发送一条TCP连接请求。每条TCP连接都会有连接建立时延;
#3:因特网传输请求报文以及处理请求报文;
#4:服务器回送HTTP响应。
2.2.TCP时延
#1:TCP连接建立握手;
#2:TCP慢启动拥塞控制;
#3:数据聚集的Nagle算法;
#4:用于捎带确认的TCP的延迟确认算法;
#5:TIME_WAIT时延和端口耗尽。
2.3.TCP连接建立握手时延
#1:请求新的TCP连接时,客户端会向服务器发送一个小的TCP分组(通常40~60字节),这个分组设置一个特殊的SYN标记;
#2:如果服务器接受了连接,会想客户端回送TCP分组,这二个分组中的SYN和ACK标记都会被置位,说明请求已被接受;
#3:客户端向服务器回送一条确认信息,通知连接已成功建立。
NOTE:小的HTTP事务都会在TCP建立耗费50%或者以上的时间。
2.4.捎带确认的TCP的延迟确认算法
#1:每个TCP段都有一个序列号和数据完整性校验和。每个段的接收者收到完好的段时,都会向发送者回送确认报文。
#2:如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已被破坏或毁坏,并重发数据。
#3:由于确认报文很小,所以TCP允许在发往相同方向的输出数据分组中对其进行“捎带”,如果在特定的时间窗口(通常100~200毫秒)没有输出数据分组,就将确认信息放在单独的分组中传送。
2.5.TCP慢启动
#1:TCP连接的使用期(age)是TCP数据传输性能的影响因数之一。
#2:起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的数据。这种调谐被称为TCP慢启动,用于防止Internet的突然过载和拥堵。
#3:TCP慢启动限制了一个TCP端点再任意时刻可以传输的分组数。
#4:由于存在这种拥塞控制特性,所以新的连接的传输速度会比已经交换过一定量数据的、”已调谐“连接慢一些。
2.6.Nagle算法与TCP_NODELAY
#1:Nagle算法试图发送一个分组之前,将大量TCP数据绑定在一起,以提高网络效率。鼓励发送全尺寸(LAN最大尺寸分组大约1500字节,Internet上是几百字节),只有当所有其他分组都被确认,或者缓存积累一个全尺寸的分组数据之后,才允许发送;
#2:Nagle算法会阻塞数据的发送。若小HTTP报文无法填满一个分组,可能会因为等待那些永远不会到来的额外数据而产生时延;阻塞数据的发送,但本身分组确认延迟100~200毫秒;
#3:TCP_NODELAY可以设置禁用Nagle算法,提供性能,但要确保TCP写入大块数据。
2.7.TIME_WAIT时延和端口耗尽
#1:在TCP端点关闭连接时,会在内存中维护一个用来记录最近所关闭连接的IP地址和端口号的控制块,通常只会维持最大分段试用期的俩倍(2MSL,通常为2分钟)左右,以确保这段时间内不会创建具有相同地址和端口号的新连接;
#2:有些操作系统会将2MSL设置为一个较小的值,但超过这个值,要小心,分组确实会被复制,如果来自之前连接的复制分组插入到具有相同连接值的新TCP流,会破坏TCP数据;
#3:客户端每次连接到服务器时,都会获得一个新的源端口,以实现连接的唯一性。但源端口有限,而且在2MSL内连接是无法重用,连接率就被限制住了;
#4:在有大量打开连接或控制块的情况下,有些操作系统的速度会严重减缓。
3.HTTP性能
3.1.http串行处理时延
对连接进行简单的管理,TCP的性能时延可能会叠加。
假如一个包含3个嵌入图片的web页面,浏览器需要发起4个HTTP事务来显示此页面,每个事务都需要(串行地创建)一条新的连接,那么连接时延和慢启动时延就会叠加起来。
提高http连接性能:
#1:并行连接:多条TCP连接发起并发的http请求;
#2:持久连接:重用TCP连接,消除连接及关闭时延;
#3:管道化连接:共享TCP连接发起并发的HTTP请求;
#4:复用的连接:交替传送请求和响应报文(实验阶段)。
3.2.并行连接
#1:并行可能会提高页面的加载速度
并行连接,克服单条连接的空载时间和带宽限制,加速速度也会有所提高。
#2:并行连接不一定更快
1)客户端的网络带宽不足时,大部分时间都在用来传数据,带来的提升不大;
2)打开大量的连接,会造成服务器性能严重下降;
3)实际上,浏览器使用并行连接,一般连接总数限制通常4个,服务器可以随意关闭来自特定客户端的超量连接。
3.3.持久连接
客户端经常要打开同一个站点的连接,重用对目标服务器打开的空闲持久连接,就可以避免缓慢的连接建立阶段和慢启动的拥塞适应阶段,以便更快地进行数据的传输。
持久连接有俩种类型:HTTP/1.0+ "keep-alive"(较老)和HTTP/1.1 ”persistent“
#1:HTTP/1.0+ keep-alive连接
早期的HTTP/1.0+ keep-alive持久性连接,受到了一些互操作行设计方面问题的困扰,问题在HTTP/1.1修正,单很多客户端和服务器仍然使用1.0版本。
1>Keep-Alive操作
实现HTTP/1.0 keep-alive连接的客户端,通过包含Connection: Keep-Alive首部请求将一条连接保持在打开状态。
如果响应没有Connection: Keep-Alive首部,客户端就认为服务器不支持Keep-Alive。
2>Keep-Alive选项
Note:
1)发出Keep-Alive请求,客户端和服务器不一定会同意;
2)客户端和服务器可以在任意时刻可以关闭空闲的Keep-Alive连接;
3)可以随意限制Keep-Alive连接所处理事务的数量。
Keep-Alive首部可以调节以上行为:
格式:
Connection: Keep-Alive
Keep-Alive: (["max""="n] [",""timeout""="seconds ][header[value]])
服务器最多会为另外5个事务保持连接打开状态,或者打开状态保持到连接空闲了2分钟,例子:
Connection: Keep-Alive
Keep-Alive: max=5, timeout=120
参数:
max:估计服务器还希望为多少个事务保持此连接活跃状态。非承诺值。
timeout:估计服务器还希望将连接保持在活跃状态的时间。非承诺值。
header:任意为经处理的属性,用于诊断和调试。
3>Keep-Alive连接的限制和规则
1)HTTP/1.0中,Keep-Alive不是默认使用的,需要Connection: Keep-Alive;
2)Connection: Keep-Alive必须随所有希望持久连接的报文一起发送,否则会在那条请求后关闭连接;
3)响应报文中没有Connection: Keep-Alive,则服务器会在发出响应后关闭持久连接;
4)实体部分必须有正确的Content-Length,否则无法精确一条报文的结束和另一条报文的开始,不能持久连接;
5)代理或网关必须在将报文转发出去或将其高速缓存之前,删除Connection首部中命名的所有首部字段以及Connection首部;
6)不应该与无法确定是否支持Connection首部的代理服务器建立Keep-Alive连接。
4>Keep-Alive和哑代理
1)不支持Connection首部的代理盲中继
step-1:客户端发送一条包含Connection: Keep-Alive首部的请求报文;
step-2:哑代理不理解Connection首部,只是沿着链路将该首部发送到服务器;
step-3:服务器收到Connection: Keep-Alive,认为对方希望进行Keep-Alive对话,则回送包含Connection: Keep-Alive的响应报文;
step-4:哑代理回送来自服务器的响应报文到客户端,然后等待源端服务器关闭连接,但服务器不会关闭连接;
step-5:客户端收到含有Connection: Keep-Alive首部的响应报文,就认为代理同意进行Keep-Alive对话;
step-6:客户端在Keep-Alive连接上向代理发送请求报文,而代理并不认为同一条连接上会有其他请求到来,请求被忽略;
step-7:客户端一直挂起状态,直到连接超时,并将其关闭为止。
2)代理与逐跳首部
为了避免以上问题发生,现代代理都绝不能转发Connection首部和所有名字出现在Connection值中的首部。
Proxy-Authenticate/Proxy-Connection/Transfer-Encoding/Upgrade同样不能被代理转发或作为缓存响应使用的首部。
5>插入Proxy-Connection
聪明的代理(能理解持久连接的握手动作),就用一个Connection首部来取代Proxy-Connection首部,将其转发到服务器达到预期的效果。
但是对于多层次代理,Proxy-Connection仍然不能解决问题。
#2:HTTP/1.1 持久连接
HTTP/1.1用了持久连接(persistent connection)取代了Keep-Alive,目的与Keep-Alive一样;
HTTP/1.1默认情况下激活持久连接,要在事务处理结束之后关闭连接,要在响应报文包含Connection: close;
响应报文不包含Connection: close,并不意味着服务器承诺永远将连接保持打开,客户端和服务器仍然可以随时关闭空闲的连接。
#3:持久连接的限制和规则
1)发送了Connection: close,客户端无法在那条连接上发送更多请求;
2)连接保持,连接上的所有报文都需要正确、自定义报文长度正确;
3)HTTP/1.1的代理服务器不应该与HTTP/1.0客户端建立连接;
4)客户端对任务服务器或代理最多只能维持2条持久连接,防止服务器过载。
3.4.管道化连接
HTTP/1.1允许在持久连接上可选使用请求管道。在响应到达前,可以将多条请求放入队列。可以降低网络的换回时间,提高性能。
管道化限制:
#1:确定客户端支持持久连接;
#2:必须按照与请求顺序回送HTTP响应;
#3:客户端做好任意时刻连接关闭、重发所有未完成管道化的请求准备。
3.5.关闭连接
#1:“任意时刻”解除连接
管道化持久连接很常见。持久连接空闲一段时间,服务器可能会决定关闭连接。但是服务器无法确定关闭的那一刻,连接的另一头有没有报文要发送,若客户端此时在写入半截请求报文,会发生报错。
#2:Content-Length及截尾操作
连接关闭依据正确的Content-Length首部或包含Connection: close的响应报文。
#3:连接关闭容限、重试以及幂等性
如果一个事务,不管是执行多少次,得到的结果都是相同的,那么这个事务就是幂等的。
在非错误的情况下,连接会在任意时刻关闭,客户端要做好正确处理非预期关闭的准备,对于幂等事务可以重新请求,但是对于非幂等事务要另作考虑(如果只是响应报文没有返回,那再次请求会造成非预想结果)。
#4:正常关闭连接
TCP连接是双向的,每一段都有一个输入队列和一个输出队列。
1>TCP关闭及重置错误
简单的http应用程序可以只使用全关闭。
很多客户端、服务器和代理通信已经开始使用管道化持久连接,使用半关闭来防止对等实体收到非预期的写入错误。
若另一端往你已关闭的输入信道发送数据,操作系统就会向另一端回送一条TCP"连接被对端重置"的报文。大部分系统都会作为很严重的错误处理,清空对端还未读取的所有缓存数据。
2>正常关闭
先关闭自己的输出信道,然后等待另一端的对等实体关闭它的输出信道。
如果在一段时间内对端没有关闭输入信道,应用程序可以强制关闭连接。
附录:
HTTP/1.1官网规范
持久连接HTTP实例
P-HTTP与TCP之间实现性能交互
TCP慢启动、避免拥塞、快速重传以及快速恢复算法
TCP确认和延迟确认
Nagle算法
传输控制协议