第四章:连接管理

传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。

IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。

4.1 TCP连接
世界上几乎所有的HTTP通信都是由TCP/IP承载的,TCP/IP是全球计算机及网络设备都在使用的一种常用的分组交换网络分层协议集。客户端应用程序可以打开一条TCP/IP连接,连接到可能运行在世界任何地方的服务器应用程序。一旦连接建立起来了,在客户端和服务器的计算机之间交换的报文就永远不会丢失、受损或失序。'

比如,你想获取Joe的五金商店最新的电动工具价目表:
http://www.joes-hardware.com:80/power-tools.html
浏览器收到这个URL时,会执行图4-1所示的步骤。第(1)~(3)步会将服务器的IP地址和端口号从URL中分离出来。在第(4)步中建立到Web服务器的TCP连接,并在第(5)步通过这条连接发送一条请求报文。在第(6)步读取响应,并在第(7)步关闭连接。

image.png

TCP为HTTP提供了一条可靠的比特传输管道。从TCP连接一端填入的字节会从另一端以原有的顺序、正确地传送出来(参见图4-2)。


image.png

4.1.2 TCP流是分段的、由IP分组传送
TCP的数据是通过名为IP分组(或IP数据报)的小数据块来发送的。这样的话,如图4-3a所示,HTTP就是“HTTP over TCP over IP”这个“协议栈”中的最顶层了。其安全版本HTTPS 就是在HTTP和TCP之间插入了一个(称为TLS或SSL的)密码加密层(图4-3b)。

HTTP要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。TCP收到数据流之后,会将数据流砍成被称作段的小数据块,并将段封装在P分组中,通过因特网进行传输(参见图4-4)。所有这些工作都是由TCP/IP软件来处理的,HTTP程序员什么都看不到。
每个TCP段都是由IP分组承载,从一个IP地址发送到另一个IP地址的。每个IP分组中都包括:
·一个IP分组首部(通常为20字节)﹔一个 TCP段首部(通常为20字节)﹔一个 TCP数据块(0个或多个字节)。
IP首部包含了源和目的IP地址、长度和其他一些标记。TCP段的首部包含了TCP端口号、TCP控制标记,以及用于数据排序和完整性检查的一些数字值。


image.png

4.1.3 保持TCP连接的正确运行
在任意时刻计算机都可以有几条TCP连接处于打开状态。TCP是通过端口号来保持所有这些连接的正确运行的。
端口号和雇员使用的电话分机号很类似。就像公司的总机号码能将你接到前台,而分机号可以将你接到正确的雇员位置一样,IP地址可以将你连接到正确的计算机,而端口号则可以将你连接到正确的应用程序上去。TCP 连接是通过4个值来识别的:
<源P地址、源端口号、目的IP地址、目的端口号>
这4个值一起唯一地定义了一条连接。两条不同的TCP 连接不能拥有4个完全相同的地址组件值(但不同连接的部分组件可以拥有相同的值)。


image.png

image.png

4.1.4用TCP套接字编程
操作系统提供了一些操纵其TCP连接的工具。为了更具体地说明问题,我们来看一个TCP编程接口。表4-2显示了套接字API提供的一些主要接口。这个套接字API向HTTP程序员隐藏了TCP和IP的所有细节。套接字API最初是为Unix操作系统开发的,但现在几乎所有的操作系统和语言中都有其变体存在。

image.png

4.2.1 HTTP事务的时延
我们来回顾一下,在HTTP请求的过程中会出现哪些网络时延,并以此开始我们的TCP性能之旅。图4-7描绘了HTTP事务主要的连接、传输以及处理时延。


image.png

HTTP事务的时延有以下几种主要原因。
1)客户端首先需要根据URI确定Web服务器的IP地址和端口号。如果最近没有对URI中的主机名进行访问,通过 DNS解析系统将URI中的主机名转换成一个IP地址可能要花费数十秒的时间,。
(2)接下来,客户端会向服务器发送一条 TCP连接请求,并等待服务器回送一个请
求接受应答。每条新的TCP连接都会有连接建立时延。这个值通常最多只有一两秒钟,但如果有数百个 HTTP事务的话,这个值会快速地叠加上去。
(3)一旦连接建立起来了,客户端就会通过新建立的TCP管道来发送HTTP请求。
数据到达时,Web服务器会从TCP连接中读取请求报文,并对请求进行处理。
(4)然后,Web服务器会回送 HTTP响应,这也需要花费时间。
这些TCP网络时延的大小取决于硬件速度、网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。TCP协议的技术复杂性也会对时延产生巨大的影响。

4.2.3 TCP连接的握手时延
建立一条新的TCP连接时,甚至是在发送任意数据之前,TCP软件之间会交换一系列的IP分组,对连接的有关参数进行沟通(参见图4-8)。如果连接只用来传送少量数据,这些交换过程就会严重降低HTTP的性能。


image.png

TCP连接握手需要经过以下几个步骤。
(1)请求新的TCP 连接时,客户端要向服务器发送一个小的TCP分组(通常是40~
60个字节)。这个分组中设置了一个特殊的SYN标记,说明这是一个连接请求。(参见图4-8a)。
(2)如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个
TCP分组,这个分组中的SYN和ACK标记都被置位,说明连接请求已被接受(参见图4-8b)。
(3)最后,客户端向服务器回送一条确认信息,通知它连接已成功建立(参见图4-8c)。
现代的TCP栈都允许客户端在这个确认分组中发送数据。

4.2.5 TCP慢启动
TCP数据传输的性能还取决于TCP连接的使用期((age)。TCP连接会随着时间进行自我“调谐”,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐被称为TCP慢启动(slow start),用于防止因特网的突然过载和拥塞。
TCP慢启动限制了一个 TCP端点在任意时刻可以传输的分组数。简单来说,每成功接收一个分组,发送端就有了发送另外两个分组的权限。如果某个HTTP事务有大量数据要发送,是不能一次将所有分组都发送出去的。必须发送一个分组,等待确认。然后可以发送两个分组,每个分组都必须被确认,这样就可以发送四个分组了,以此类推。这种方式被称为“打开拥塞窗口”。
由于存在这种拥塞控制特性,所以新连接的传输速度会比已经交换过一定量数据的、“已调谐”连接慢一些。由于已调谐连接要更快一些,所以HTTP中有一些可以重用现存连接的工具。本章稍后会介绍这些HTTP“持久连接”。

4.2.6 Nagle算法与TCP_NODELAY
TCP有一个数据流接口,应用程序可以通过它将任意尺寸的数据放入TCP栈中—--即使一次只放一个字节也可以!但是,每个TCP段中都至少装载了40个字节的标记和首部,所以如果TCP发送了大量包含少量数据的分组,网络的性能就会严重下降。5

Nagle算法(根据其发明者John Nagle命名)试图在发送一个分组之前,将大量TCP数据绑定在一起,以提高网络效率。RFC 896“IPTCP互连网络中的拥塞控制”对此算法进行了描述。
Nagle算法鼓励发送全尺寸(LAN上最大尺寸的分组大约是1500字节,在因特网上是几百字节)的段。只有当所有其他分组都被确认之后,Nagle算法才允许发送非全尺寸的分组。如果其他分组仍然在传输过程中,就将那部分数据缓存起来。只有当挂起分组被确认,或者缓存中积累了足够发送一个全尺寸分组的数据时,才会将缓存的数据发送出去。6
Nagle算法会引发几种 HTTP性能问题。首先,小的HTTP报文可能无法填满一个分组,可能会因为等待那些永远不会到来的额外数据而产生时延。其次,Nagle算法与延迟确认之间的交互存在问题——Nagle算法会阻止数据的发送,直到有确认分组抵达为止,但确认分组自身会被延迟确认算法延迟100~200毫秒。7
HTTP应用程序常常会在自己的栈中设置参数TCP_NODELAY,禁用Nagle算法,提高性能。如果要这么做的话,一定要确保会向TCP写入大块的数据,这样就不会产生一堆小分组了。

4.4并行连接
如前所述,浏览器可以先完整地请求原始的 HTML页面,然后请求第一个嵌入对象,然后请求第二个嵌入对象等,以这种简单的方式对每个嵌入式对象进行串行处理。但这样实在是太慢了!
如图4-11所示,HTTP 允许客户端打开多条连接,并行地执行多个HTTP事务。在这个例子中,并行加载了四幅嵌入式图片,每个事务都有自己的 TCP连接。12


image.png

4.5持久连接
Web客户端经常会打开到同一个站点的连接。比如,一个Web页面上的大部分内嵌图片通常都来自同一个Web站点,而且相当一部分指向其他对象的超链通常都指向同一个站点。因此,初始化了对某服务器HTTP请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求(比如,获取在线图片)。这种性质被称为站点本地性(site locality)。
因此,HTTP/1.1(以及HTTP/1.0的各种增强版本)允许HTTP设备在事务处理结束之后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现存的连接。在事务处理结束之后仍然保持在打开状态的TCP连接被称为持久连接。非持久连接会在每个事务结束之后关闭。持久连接会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。
重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。

4.5.2 HTTP/1.0+ keep-alive连接
大约从1996年开始,很多HTTP/1.0浏览器和服务器都进行了扩展,以支持一种被称为keep-alive 连接的早期实验型持久连接。这些早期的持久连接受到了一些互操作性设计方面问题的困扰,这些问题在后期的HTTP/1.1版本中都得到了修正,但很多客户端和服务器仍然在使用这些早期的keep-alive连接。
图4-13显示了keep-alive连接的一些性能优点,图中将在串行连接上实现4个·HTTP事务的时间线与在一条持久连接上实现同样事务所需的时间线进行了比较。由于去除了进行连接和关闭连接的开销,所以时间线有所缩减。16


image.png

4.6管道化连接
HTTP/1.1允许在持久连接上可选地使用请求管道。这是在 keep-alive连接上的进一步性能优化。在响应到达之前,可以将多条请求放入队列。当第一条请求通过网络流向地球另一端的服务器时,第二条和第三条请求也可以开始发送了。在高时延网络条件下,这样做可以降低网络的环回时间,提高性能。
图4-18a-c显示了持久连接是怎样消除TCP连接时延,以及管道化请求(参见图4-18c)是如何消除传输时延的。
对管道化连接有几条限制。
·如果HTTP客户端无法确认连接是持久的,就不应该使用管道。
·必须按照与请求相同的顺序回送 HTTP响应。HTTP报文中没有序列号标签,因此如果收到的响应失序了,就没办法将其与请求匹配起来了。
.HTTP客户端必须做好连接会在任意时刻关闭的准备,还要准备好重发所有未完
成的管道化请求。如果客户端打开了一条持久连接,并立即发出了10条请求,服务器可能在只处理了,比方说,5条请求之后关闭连接。剩下的5条请求会失败,客户端必须能够应对这些过早关闭连接的情况,重新发出这些请求。
HTTP客户端不应该用管道化的方式发送会产生副作用的请求(比如 POST)。总之,出错的时候,管道化方式会阻碍客户端了解服务器执行的是一系列管道化请求中的哪一些。由于无法安全地重试POST这样的非幂等请求,所以出错时,就存在某些方法永远不会被执行的风险。

image.png

你可能感兴趣的:(第四章:连接管理)