可以说世界上所有的http通信都是由tcp/ip承载的。tcp连接是可靠的连接,也就是说它会将数据没有丢失的、有序的发送到客户端。TCP的数据是通过IP分组(或IP数据报)分成多块进行发送的。HTTP的安全版本HTTPS就是在HTTP和TCP之间插入了一个TLS或SSL的加密层。

HTTP要传送一条报文时,会以流的形式将报文数据的通过一条打开的TCP连接按序传输到传输层。传输层收到数据流之后,会将数据流分成小的TCP段,然后将TCP段传输到网络层。网络层又将TCP段封装在IP分组中,然后通过因特网传输。 每个TCP段都是IP分组封装从一个ip地址发送到另一个ip地址,因此分割ip分组中都包含了源IP地址、目的IP地址等信息。

任意时刻计算机都可以有多条TCP连接,但是任意两条TCP连接的源IP地址、源端口、目的IP地址、目的端口四个参数不会完全一样,否则计算机就不知道谁是谁了。 操作系统提供了进行TCP操作的接口,也就是套接字API。高级语言的套接字编程都是对操作系统套接字API的封装。

TCP对HTTP性能的影响

HTTP是利用TCP进行传输数据的,所以HTTP传输数据的性能很大成语上受TCP连接的影响。 常见的影响HTTP连接性能的TCP相关的原因包括:

  • TCP连接建立时的握手时延
  • TCP慢启动拥塞
  • 数据聚集的Nagle算法
  • 用于捎带确认的TCP延迟确认算法;
  • TIME_WAIT时延和端口耗尽。

TCP连接建立时的握手时延

TCP建立连接时需要进行三步握手,所以在发送HTTP数据之前,TCP要传送两个TCP段来建立连接(三步握手的第三步已经可以发送数据了)。 显而易见,如果HTTP传输的数据很小,那么TCP建立连接花费的时间占总的HTTP连接的时间就会很大。

TCP延迟确认

TCP段的接收方收到TCP段后都需要发送确认信息,由于确认报文很小,所以TCP允许在发往相同方向的输出分组中对其进行“捎带”。为了增加确认报文找到同向传输数据分组的可能性,TCP栈实现了一种“延迟确认算法”。

延迟确认算法会在一个特定的窗口时间内(通常是100ms~200ms)将输出确认存放在缓冲区中,以寻找能够捎带它的输出数据分组。如果在窗口时间内没有输出数据分组,就将确认信息放在单独的分组中发送。

所以如果TCP每次都在等窗口时间,但是偏偏没有可以捎带的输出报文,那就尴尬了。

TCP慢启动

TCP慢启动决定了TCP在一开始一个TCP段能够传输的数据很少,所以新的TCP连接的传输速度会比已经交换过一定数据的连接慢。因此HTTP可以尝试重用已经交换过数据的TCP连接来提高性能。

Nagle算法与TCP_NODELAY

如果大量TCP发送了包含少量数据的分组,网络性能就会严重下降。nagle算法在发送一个分组之前,将大量TCP分组数据绑定在一起,以提高网络效率,(参考RFC 896)。 Nagle算法鼓励发送全尺寸的段(1500字节),只有当所有其他分组都被确认之后,Nagle算法才允许发送非全尺寸的分组。

Nagle算法可能会引发几个问题:

  1. 小的HTTP报文可能很久不发填满一个TCP段,因为等待不会到来的数据产生延迟。
  2. Nagle会组织数据的发送,直到有确认分组抵达为止,但确认分组会被延迟确认算法延迟100ms~200ms。

TIME_WAIT累积与端口耗尽

当某个TCP端点关闭TCP连接时,会在内存中维护一个小的控制快用来记录最近关闭连接的IP地址和端口号。该信息会维持一小段时间,通常是所顾及的最大份段使用期的两倍左右(2MSL),以确保在这段时间内不会创建具有相同地址和端口的新连接。 这导致,在使用一个客户端和一台web服务器来测试性能时,在2MSL时间内只能有限数量(客户端的端口数量)的连接。

优化HTTP连接

串行连接

同一时间只有一个http连接

并行连接

同一时间有多条http连接。并行连接会使用户看到多个资源在同时加载,会让人觉得页面加载的很快。

并行连接会受到以下几个方面的限制:

  • 带宽
  • 内存资源
  • 服务器性能

持久连接

重用已经打开的连接,节省TCP三次握手时间和慢启动时间。 想要使用持久连接只需在http请求头中加上:Connection: keep-alive。如果服务器支持持久连接,在相应头中也会有Connection: keep-alive
keep-alive首部只是请求将连接保持在活跃状态,客户端和服务器仍然可以在任意时刻关闭空闲的keep-alive,并随意限制keep-alive连接的http事务的数量。

  1. 使用Connection: keep-alive后,服务器可以在响应头中的添加Keep-alive来设置持久连接的行为。 ex:Keep-Alive: timeout=5, max=100表示服务器希望博打开状态保持到连接空闲了5秒,最多还会为100个事务保持连接的打开状态。
  2. 一个http持久连接会发送多个http事务,所以客户端和服务器必须有能力判断报文实体主体部分的长度。这就要求Content-Length头部的正确性。
  3. 代理和网关必须执行Connection首部的规则,代理/网关在将报文转发出去之前,会删除Connection首部中命名的所有首部字段以及Connection首部自身。

在HTTP/1.1中逐渐停止了对Keep-Alive的支持,使用persistent connection代替。并且HTTP/1.1的持久连接在默认情况下是打开的,除非特别指明,HTTP/1.1假定所有连接都是持久的,要在事务处理结束后必须显式地添加一个Connection:close将连接关闭。当然服务器端可以在任意时刻关闭连接。

管道化连接

HTTP/1.1允许在持久连接上可选的使用请求管道,也就是可以连续的发送请求,无需等待前一条请求的响应到达。用的不多。