网络知识详解之:TCP连接原理详解

557次阅读
没有评论

共计 11777 个字符,预计需要花费 30 分钟才能阅读完成。

TCP 连接

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

网络知识详解之:TCP 连接原理详解

三次握手

TCP 协议目的是为了保证数据能在两端准确连续的流动 ,可以想象两个建立起 TCP 通道的设备就如同接起了一根水管,数据就是水管中的水由一头流向另一头。然而 TCP 为了能让一个设备连接多根“水管”, 让一个设备能同时与多个设备交互信息 它必须要保证不同水管之间不会产生串联或相互影响

为了确保数据能够正确分发,TCP 用一种叫做 TCB,也叫传输控制块的数据结构把发给不同设备的数据封装起来 ,我们可以把该结构看做是信封。 一个 TCB 数据块包含了数据发送双方对应的 socket 信息以及拥有装载数据的缓冲区。

在两个设备要建立连接发送数据之前,双方都必须要做一些准备工作,分配内存建立起 TCB 数据块就是连接建立前必须要做的准备工作。

网络知识详解之:TCP 连接原理详解

一、准备工作

最开始的时候客户端和服务器都是处于 CLOSED 状态。主动打开连接的为客户端,被动打开连接的是服务器。

TCP 服务器进程先创建传输控制块 TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了 LISTEN(监听)状态

二、一次握手

TCP 客户进程也是先创建传输控制块 TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位 SYN=1,同时选择一个初始序列号 seq=x

此时,TCP 客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP 规定,SYN 报文段(SYN= 1 的报文段)不能携带数据,但需要消耗掉一个序号。

三、二次握手

TCP 服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是 ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP 服务器进程进入了 SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。

ACK 为 1 表示确认号有效,为 0 表示报文中不包含确认信息  
  • 1

四、三次握手

TCP 客户进程收到确认后,还要向服务器给出确认。确认报文的 ACK=1,ack=y+1,自己的序列号 seq=x+1,此时,TCP 连接建立,客户端进入 ESTABLISHED(已建立连接)状态。TCP 规定,ACK 报文段可以携带数据,但是如果不携带数据则不消耗序号。

当服务器收到客户端的确认后也进入 established 状态,此后双方就可以开始通信了。

注:tcp 建立连接需要三次握手,SYN 是发送标志位,ACK 是确认标志位.

为什么 TCP 客户端最后还要发送一次确认呢?

主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于 TCP 的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

为什么要 3 次握手?

换个易于理解的视角来看为什么要 3 次握手。

客户端和服务端通信前要进行连接, 3 次握手的作用就是双方都能明确自己和对方的收、发能力是正常的。

第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。

  • 从客户端的视角来看,我接到了服务端发送过来的响应数据包,说明服务端接收到了我在第一次握手时发送的网络包,并且成功发送了响应数据包,这就说明,服务端的接收、发送能力正常。
  • 而另一方面,我收到了服务端的响应数据包,说明我第一次发送的网络包成功到达服务端,这样,我自己的发送和接收能力也是正常的。

第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力,服务端的发送、接收能力是正常的。第一、二次握手后,服务端并不知道客户端的接收能力以及自己的发送能力是否正常。而在第三次握手时,服务端收到了客户端对第二次握手作的回应。从服务端的角度,我在第二次握手时的响应数据发送出去了,客户端接收到了。所以,我的发送能力是正常的。而客户端的接收能力也是正常的

因此,三次握手的主要目的就是收发双方为了确认彼此是否可以正常通信而进行的。只有能够正常通信,才会后续发送请求报文。同时也是为了建立收发双发的确认号 Ack 和序号 Seq 的值,在后续的数据传输阶段,都是在彼此 Ack 和 Seq 值的基础上进行计算的,这样做保证了 TCP 报文传输的连贯性

网络知识详解之:TCP 连接原理详解

三次握手也是客户端和服务端建立通信连接的过程。

经历了上面的三次握手过程,客户端和服务端都确认了自己的接收、发送能力是正常的。之后就可以正常通信了。

握手中的 SYN 超时重试

如果 server 端接到了 clien 发的 SYN 后回了 SYN-ACK 后 client 掉线了,server 端没有收到 client 回来的 ACK,那么,这个连接处于一个中间状态,即没成功,也没失败。于是,server 端如果在一定时间内没有收到的 TCP 会重发 SYN-ACK。在 Linux 下,默认重试次数为 5 次,重试的间隔时间从 1s 开始每次都翻售,5 次的重试时间间隔为 1s, 2s, 4s, 8s, 16s,总共 31s,第 5 次发出后还要等 32s 都知道第 5 次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP 才会把断开这个连接。

TCP 协议缺陷

DDOS 又称为分布式拒绝服务,全称是 Distributed Denial of Service。DDOS 本是利用合理的请求造成服务器资源过载,导致服务不可用。常见的 DDOS 攻击有 SYN flood(SYN flood)、UDP flood、ICMP、flood 等,其中 SYN flood 是一种最为经典的 DDOS 攻击。SYN flood 如此猖獗是因为它利用了 TCP 协议设计中的缺陷,而 TCP/IP 协议是整个互联网的基础,牵一发而动全身,如今想要修复这样的缺陷几乎成为不可能的事情。

SYN flood 攻击原理:

  1. SYN flood 在攻击时,首先伪造大量的源 IP 地址,分别向服务器端发送大量的 SYN 包
  2. 服务器端返回 SYN/ACK 包,因为源地址是伪造的,所以伪造的 IP 并不会应答
  3. 服务器端没有收到伪造 IP 的回应,会重试 3~5 次并且等待一个 SYN Time(—般为 30 秒至 2 分钟),如果超时则丢弃这个连接
  4. 攻击者大量发送这种伪造源地址的 SYN 请求,服务器端将会消耗非常多的资源来处理这种半连接,同时还要不断地对这些 IP 进行 SYN+ACK 重试。
  5. 最后的结果是服务器无暇理睬正常的连接请求,导致拒绝服务。

四次挥手

原理

数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于 established(表示连接已经建立)状态,然后客户端主动关闭,服务器被动关闭。

网络知识详解之:TCP 连接原理详解

  1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1(FIN 表示关闭连接,SYN 表示建立连接),其序列号为 seq=u(等于前面已经传送过来的数据的最后一个字节的序号加 1 ),此时,客户端进入 FIN-WAIT-1(终止等待 1)状态。TCP 规定,FIN 报文段即使不携带数据,也要消耗一个序号。
  2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1(确认序号为收到的序号加 1),并且带上自己的序列号 seq=v,此时,服务端就进入了 CLOSE-WAIT(关闭等待)状态。TCP 服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间。
  3. 客户端收到服务器的确认请求后,此时,客户端就进入 FIN-WAIT-2(终止等待 2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
  4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为 seq=w,此时,服务器就进入了 LAST-ACK(最后确认)状态,等待客户端的确认。
  5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是 seq=u+1,此时,客户端就进入了 TIME-WAIT(时间等待)状态。注意此时 TCP 连接还没有释放,必须经过 2 *MSL(最长报文段寿命)的时间后,当客户端撤销相应的 TCB 后,才进入 CLOSED 状态。
  6. 服务器只要收到了客户端发出的确认,立即进入 CLOSED 状态。同样,撤销 TCB 后,就结束了这次的 TCP 连接。可以看到,服务器结束 TCP 连接的时间要比客户端早一些。FIN_WAIT_1 和 FIN_WAIT_2 状态的真正含义都是表示等待对方的 FIN 报文。而这两种状态的区别是:FIN_WAIT_1 状态实际上是当 SOCKET 在 ESTABLISHED 状态时,它想主动关闭连接,向对方发送了 FIN 报文,此时该 SOCKET 即进入到 FIN_WAIT_1 状态。而当对方回应 ACK 报文后,则进入到 FIN_WAIT_2 状态

总结

  1. TCP 客户端发送一个 FIN,用来关闭客户到服务器的数据传送。
  2. 服务器收到这个 FIN,它发回一个 ACK,确认序号为收到的序号加 1。和 SYN 一样,一个 FIN 将占用一个序号。
  3. 服务器关闭客户端的连接,发送一个 FIN 给客户端。
  4. 客户端发回 ACK 报文确认,并将确认序号设置为收到序号加 1。

为什么客户端最后还要等待 2MSL?

MSL(Maximum Segment Lifetime),TCP 允许不同的实现可以设置不同的 MSL 值。

去向 ACK 消息最大存活时间(MSL) + 来向 FIN 消息的最大存活时间(MSL)。这恰恰就是 **2MSL(Maximum Segment Life)。
  • 1

第一,保证客户端发送的最后一个 ACK 报文能够到达服务器,因为这个 ACK 报文可能丢失,站在服务器的角度看来,我已经发送了 FIN+ACK 报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个 2MSL 时间段内收到这个重传的报文,接着给出回应报文,并且会重启 2MSL 计时器。

第二,等待 2MSL 时间,客户端就可以放心地释放 TCP 占用的资源、端口号。如果不等,释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的 TCP 报文可能与新 TCP 连接报文冲突,造成数据冲突,为避免此种情况,需要耐心等待网络老的 TCP 连接的活跃报文全部死翘翘,2MSL 时间可以满足这个需求(尽管非常保守)!

为什么建立连接是三次握手,关闭连接确是四次挥手呢?

建立连接的时候,服务器在 LISTEN 状态下,收到建立连接请求的 SYN 报文后,把 ACK 和 SYN 放在一个报文里发送给客户端。

关闭连接时,服务器收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必已经将全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送 FIN 报文给对方来表示同意现在关闭连接,因此,己方 ACK 和 FIN 一般都会分开发送,从而导致多了一次

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP 还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为 2 小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔 75 秒发送一次。若一连发送 10 个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

TCP 数据传输

传输原理

TCP 通过“发送 — 应答(ACK 确认)”来确保传输的可靠性,它是端到端传输的。

TCP 传输是分段的,一个 HTTP 响应报文会被操作系统切成多个 MSS(Maximum Segment Size)大小的段,直到接收端接受到完整的报文为止。 在此过程中,报文分段按照顺序进行发送,每个报文段在发送时,会做顺序编号,以便能够完整正确地组装。

MSS:Maximum Segment Size 最大报文段长度, 是 TCP 协议的一个选项,用于在 TCP 连接建立时,收发双方协商通信时每一个报文段所能承载的最大数据长度(不包括文段头)。如果 MSS 选项数据为 512,则表示该报文段的发送方可以处理的最大报文段长度为 512 字节(不包括 TCP 与 IP 协议头长度)。主机一般默认 MSS 为 536 字节

网络知识详解之:TCP 连接原理详解

端口号

表示同一个计算机上的不同进程

源端口号和目标端口号都是占用了两个字节

TCP 的源端口号和目标端口号预计 IP 报文中的源 IP 和目标 IP 确认一条唯一的 TCP 连接

序号

Seq,4 个字节

确认序号

Ack,占四个字节

控制位

URG、ACK、PSH、RST、SYN、FIN

TCP 的数据是通过名为 IP 分组(或 IP 数据报)的小数据块来发送的。HTTP 就是“HTTP over TCP over IP”这个“协议栈”中的最顶层了。其安全版本 HTTPS 就是在 HTTP 和 TCP 之间插入了一个(称为 TLS 或 SSL 的)密码加密层。

HTTP 要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的 TCP 连接按序传输。TCP 收到数据流之后,会将数据流砍成被称作段的小数据块,并将段封装在 IP 分组中,通过因特网进行传输。 所有这些工作都是由 TCP/IP 软件来处理的,HTTP 程序员什么都看不到

每个 TCP 段都是由 IP 分组承载,从一个 IP 地址发送到另一个 IP 地址的。每个 IP 分组中都包括:

  • 一个 IP 分组首部(通常为 20 字节)
  • 一个 TCP 段首部(通常为 20 字节)
  • 一个 TCP 数据块(0 个或多个字节)

IP 首部包含源和目的 IP 地址、长度和其他一些标记。TCP 段的首部包含了 TCP 端口号、TCP 控制标记,以及用于数据排序和完整性检查的一些数字值。

网络知识详解之:TCP 连接原理详解

TCP 连接是通过 4 个值来识别的:
< 源 IP 地址、源端口号、目的 IP 地址、目的端口号 >

这 4 个值一起唯一地定义了一条连接。两条不同的 TCP 连接不能在同一时刻拥有 4 个完全相同的地址组件值

网络知识详解之:TCP 连接原理详解

有些连接共享了相同的目的端口号(C 和 D 都使用目的端口号 80)。有些连接使用了相同的源 IP 地址(B 和 C)。有些使用了相同的目的 IP 地址(A 和 B,C 和 D)。但没有两个不同连接所有的 4 个值都一样。

滑动窗口协议

将 TCP 与 UDP 这样的简单传输协议区分开来的两种协议不同的传输数据的质量。TCP 对于发送数据进行跟踪,这种数据管理需要协议有以下两大关键功能:

  • 可靠性: 保证数据确实到达目的地。如果未到达,能够发现并重传。
  • 数据流控: 管理数据的发送速率,以使接收设备不致于过载

要完成这些任务,整个协议操作是围绕滑动窗口确认机制来进行的。因此,理解了滑动窗口,也就是理解了 TCP。

在我们滑动窗口协议之前,我们如何来保证发送方与接收方之间,每个包都能被收到,并且是按次序的呢?

网络知识详解之:TCP 连接原理详解

问题

吞吐量非常的低。我们发完包 1,一定要等确认包 1,我们才能发送第二个包。

那么我们就不能先连发几个包等他一起确认吗?这样的话速度更快,吞吐量更高

网络知识详解之:TCP 连接原理详解

问题

如果过多的源同时以很快的速度发送大量的数据包,而此时接收方并没有如此高的接收数据的能力,因此极易导致网络的拥塞。

滑动窗口协议(Sliding Window Protocol)

该协议是 TCP 协议 的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。** 该协议允许发送方在停止并等待确认前发送多个数据分组。** 由于发送方不必每发一个分组就停下来等待确认。因此该协议可以加速数据的传输,提高网络吞吐量。滑动窗口算法其实和这个是一样的,只是用的地方场景不一样。

如果我们在任一时间点对于这一过程做一个“快照”,那么我们可以将 TCP buffer 中的数据分为以下四类,并把它们看作一个时间轴:

  1. 已发送已确认 数据流中最早的字节已经发送并得到确认。这些数据是站在发送设备的角度来看的。
  2. 已发送但尚未确认 已发送但尚未得到确认的字节。发送方在确认之前,不认为这些数据已经被处理。
  3. 未发送而接收方已 Ready 设备尚未将数据发出,但接收方根据最近一次关于发送方一次要发送多少字节确认自己有足够空间。发送方会立即尝试发送。
  4. 未发送而接收方 Not Ready 由于接收方 not ready,还不允许将这部分数据发出。

网络知识详解之:TCP 连接原理详解

说明

  • 灰色 1 号 2 号 3 号包已经发送完毕,并且已经收到 Ack。这些包就已经是过去式。
  • 4、5、6 号包是黄色的,表示已经发送了。但是并没有收到对方的 Ack,所以也不知道接收方有没有收到。
  • 7、8、9 号包是淡蓝色的。是我们还没有发送的。这些淡蓝色也就是我们接下来马上要发送的包。
  • 后面的 10-15 还没有被读进内存。要等 4 号 - 9 号包有接下来的动作后,我们的包才会继续往下发送。

正常情况

网络知识详解之:TCP 连接原理详解

可以看到 4 号包对方已经被接收到,所以被涂成了灰色。“窗口”就往右移一格。我们就把 11 号包读进了我们的缓存。进入了“待发送”的状态。8、9 号包已经变成了黄色,表示已经发送出去了。接下来的操作就是一样的了,确认包后,窗口往后移继续将未发送的包读进缓存,把“待发送“状态的包变为”已发送“。

丢包情况

有可能我们包(5-11)发过去,对方的 Ack 丢了。也有可能我们的包并没有发送过去。从发送方角度看就是我们没有收到 Ack。

网络知识详解之:TCP 连接原理详解

发生的情况:一直在等 Ack。如果一直等不到的话,我们也会把读进缓存的待发送的包也一起发过去。但是,这个时候我们的窗口已经发满了。所以并不能把 12 号包读进来,而是始终在等待 5 号包的 Ack。

问题

如果我们这个 Ack 始终不来怎么办呢?

超时重发 / 重传

原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的 ACK 报文,那么就重新发送数据,直到发送成功为止。

影响超时重传机制协议效率的一个关键参数是重传超时时间(RTO,Retransmission TimeOut)。RTO 的值被设置过大过小都会对协议造成不利影响。

  • RTO 设长了,重发就慢,没有效率,性能差。
  • RTO 设短了,重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。
  • 连接往返时间(RTT,Round Trip Time),指发送端从发送 TCP 包开始到接收它的立即响应所消耗的时间。

在 Unix 以及 Windows 系统中,最初其重发超时的默认值一般设置为 6 秒(重发时间必须是 0.5 秒的倍数)左右。数据被重发之后若还是收不到确认应答,则进行再次发送。此时,等待确认应答的时间将会以 2 倍、4 倍的指数函数延长。

此外,数据也不会被无限、反复地重发。达到一定重发次数之后,如果仍没有任何确认应答返回,就会判断为网络或对端主机发生了异常,强制关闭连接,并且通知应用通信异常强行终止。

TCP 性能

HTTP 紧挨着 TCP,位于其上层,所以 HTTP 事务的性能在很大程度上取决于底层 TCP 通道的性能。

网络知识详解之:TCP 连接原理详解

HTTP 事务的时延有以下几种主要原因

  1. 通过 DNS 解析系统将 URI 中的主机名转换成一个 IP 地址要花费对应的时间
  2. 每条新的 TCP 连接都会有连接建立时延,但如果有数百个 HTTP 事务的话,这个时间消耗值会快速地叠加上去。
  3. 网络传输请求报文及服务器处理请求报文都需要时间
  4. Web 服务器会回送 HTTP 响应的花费时间

这些网络时延的大小取决于硬件速度、网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。TCP 协议的技术复杂性也会对时延产生巨大的影响

性能聚焦区域

  • TCP 连接建立握手;
  • TCP 慢启动拥塞控制;
  • TCP 延迟确认算法;
  • Nagle 算法;

TCP 连接的握手时延

网络知识详解之:TCP 连接原理详解

TCP 连接握手需要经过以下几个步骤。

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

延迟确认(ACK)

由于网络自身无法确保可靠的分组传输(如果网络设备超负荷的话,可以随意丢弃分组),所以 TCP 实现了自己的确认机制来确保数据的成功传输。每个 TCP 段都有一个序列号和数据完整性校验和 服务端收到完好的 TCP 段时,都会向发送者回送小的确认报文。如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已损毁或丢失,并重发数据。

由于确认报文很小,所以 TCP 允许在发往相同方向的输出数据分组中对其进行“捎带”。TCP 将返回的确认信息与输出的数据分组结合在一起,可以更有效地利用网络。为了增加确认报文找到同向传输数据分组的可能性,很多 TCP 栈都实现了一种“延迟确认”算法。延迟确认算法会在一个特定的窗口时间(通常是 100 ~ 200 毫秒)内将输出确认存放在缓冲区中,以寻找能够捎带它的输出数据分组。如果在那个时间段内没有输出数据分组,就将确认信息放在单独的分组中传送。

但是当希望有相反方向回传分组的时候,偏偏没有那么多。通常,延迟确认算法会引入相当大的时延。

慢启动

慢启动算法思路

主机开发发送数据报时,如果立即将大量的数据注入到网络中,可能会出现网络的拥塞。慢启动算法就是在主机刚开始发送数据报的时候先探测一下网络的状况,如果网络状况良好,发送方每发送一次文段都能正确的接受确认报文段。那么就从小到大的增加拥塞窗口的大小,即增加发送窗口的大小,用于防止因特网的突然过载和拥塞。

TCP 慢启动限制了一个 TCP 端点在任意时刻可以传输的分组数。简单来说,每成功接收一个分组,发送端就有了发送另外两个分组的权限。如果某个 HTTP 事务有大量数据要发送,是不能一次将所有分组都发送出去的。必须发送一个分组,等待确认;然后可以发送两个分组,每个分组都必须被确认,这样就可以发送四个分组了,以此类推。这种方式被称为慢启动。

网络知识详解之:TCP 连接原理详解

拥塞避免

由于上述慢启动方法中,拥塞窗口的扩大方式采用指数型扩大,因此在窗口大到一定程度后(达到慢启动门限 ssthresh),减慢增加的速度,转成线性扩大窗口的方式,也就是每次收到新的 ACK 没有丢包的话只比上次窗口增大 1。整个过程看起来就像这样:

网络知识详解之:TCP 连接原理详解

超时重传和快速重传

随着窗口进一步缓慢增加,终于有一天,网络还是遇到了丢包的情况,我们就会假定这是拥塞造成的。这个时候会进行 超时重传 或者 快速重传

  • 超时重传:往往拥塞情况严重,采取策略也会更激进一些,会直接将 ssthresh 设置为重传发生时窗口大小的一半,而窗口大小直接重置为 0,再进入慢启动阶段。

网络知识详解之:TCP 连接原理详解

  • 快速重传: 如果我们连续 3 次收到同样序号的 ACK,包还能回传,说明这个时候可能只是碰到了部分丢包,网络阻塞还没有很严重,此时就会采用柔和一点的策略,也就是快速重传策略。我们会先把拥塞窗口变成原来的一半,ssthresh 也就设置成当前的窗口大小,然后开始执行拥塞避免算法。有些实现也会把拥塞窗口直接设置为 ssthresh+3,本质上区别不大。

网络知识详解之:TCP 连接原理详解

串行事务处理

如果只对连接进行简单的管理,TCP 的性能时延可能会叠加起来。比如,假设有一 个包含了 3 个嵌入图片的 Web 页面。浏览器需要发起 4 个 HTTP 事务来显示此页面:1 个用于顶层的 HTML 页面,3 个用于嵌入的图片。如果每个事务都需要(串行地建立)一条新的连接,那么连接时延和慢启动时延就会叠加起来。

网络知识详解之:TCP 连接原理详解

除了串行加载引入的实际时延之外,加载一幅图片时,页面上其他地方都没有动静也会让人觉得速度很慢。用户更希望能够同时加载多幅图片。

串行加载的另一个缺点是,有些浏览器在对象加载完毕之前无法获知对象的尺寸,而且它们可能需要尺寸信息来决定将对象放在屏幕的什么位置上,所以在加载了足够多的对象之前,无法在屏幕上显示任何内容。在这种情况下,可能浏览器串行装载对象的进度很正常,但用户面对的却是一个空白的屏幕,对装载的进度一无所知。

还有几种现存和新兴的方法可以提高 HTTP 的连接性能:

  • 并行连接通过多条 TCP 连接发起并发的 HTTP 请求
  • 持久连接重用 TCP 连接,以消除连接及关闭时延
  • 管道化连接通过共享的 TCP 连接发起并发的 HTTP 请求

并行连接

如前所述,浏览器可以先完整地请求原始的 HTML 页面,然后请求第一个嵌入对象,然后请求第二个嵌入对象等,以这种简单的方式对每个嵌入式对象进行串行处理。但这样实在是太慢了!

HTTP 允许客户端(浏览器)打开多条 TCP 连接,并行地执行多个 HTTP 事务(每个 TCP 连接处理一个 HTTP 事务)。在这个例子中,并行加载了四幅嵌入式图片,每个事务都有自己的 TCP 连接。页面上的每个组件都包含一个独立的 HTTP 事务

网络知识详解之:TCP 连接原理详解

网络知识详解之:TCP 连接原理详解

并行连接不一定更快

打开大量连接会消耗很多内存资源,从而引发自身的性能问题。复杂的 Web 页面可能会有数十或数百个内嵌对象。客户端可能可以打开数百个连接,但 Web 服务器通常要同时处理很多其他用户的请求,所以很少有 Web 服务器希望出现这样的情况。一百个用户同时发出申请,每个用户打开 100 个连接,服务器就要负责处理 10 000 个连接。这会造成服务器性能的严重下降。对高负荷的代理来说也同样如此。

实际上,浏览器确实使用了并行连接,但它们会将向同一个域名请求的并行连接的总数限制为一个较小的值。

浏览器同域名请求的最大并发数限制:

网络知识详解之:TCP 连接原理详解

持久连接

Web 客户端经常会打开到同一个站点的连接。比如,一个 Web 页面上的大部分内嵌图片通常都来自同一个 Web 站点,而且相当一部分指向其他对象的超链通常都指向同一个站点。因此,初始化了对某服务器 HTTP 请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求(比如,获取在线图片)。这种性质被称为站点局部性(site locality)。

HTTP/1.1(以及 HTTP/1.0 的各种增强版本)允许 HTTP 设备在事务处理结束之后将 TCP 连接保持在打开状态,以便为未来的 HTTP 请求重用现存的连接。在事务处理结束之后仍然保持在打开状态的 TCP 连接被称为持久连接。非持久连接会在每个事务结束之后关闭。持久连接会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。

重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。

Connection: Keep-Alive

网络知识详解之:TCP 连接原理详解

在持久连接中,每一个 HTTP 请求都是串行的。每个请求的发送都必须等待上一个请求的响应。

管道化连接

HTTP/1.1 允许在持久连接上使用管道化(pipeline)技术。这是相对于 keepalive 连接的又一性能优化。

在响应到达之前,可以将多条请求放入队列。当第一条请求通过网络流向另一端的服务器时,第二条和第三条请求也可以开始发送了。在高时延网络条件下,这样做可以降低网络的环回时间,提高性能。

网络知识详解之:TCP 连接原理详解

对管道化连接有几条限制

  • 必须按照与请求相同的顺序回送 HTTP 响应(如果顺序发送了请求 1 /2/3,那么无论服务器处理哪个请求更快,相应的时候必须按照请求的顺序响应)。HTTP 报文中没有序列号标签,因此如果收到的响应失序了,就没办法将其与请求匹配起来了。此时容易引起一个问题:头部阻塞。
  • HTTP 客户端必须做好连接会在任意时刻关闭的准备。如果客户端打开了一条持久连接,并立即发出了 10 条请求,服务器可能在只处理了 5 条请求之后关闭连接,剩下的 5 条请求会失败,客户端必须能够应对这些过早关闭连接的情况,重新发出这些请求。
  • 只有幂等的请求能够被管线化。HTTP 客户端不应该用管道化的方式发送会产生副作用的请求(比如 POST)。由于无法安全地重试 POST 这样的非幂等请求,所以出错时,就存在某些方法永远不会被执行的风险。

本文转载自 CDSN

正文完
 
lucky
版权声明:本站原创文章,由 lucky 2023-02-04发表,共计11777字。
转载说明:转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)