您的位置 首页 java

TCP协议不为人知的那些事

TCP 相关机制

TCP通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输。

1.确认应答

TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一 个已收到消息的通知。这个消息叫做 确认应答(ACK(Positive Acknowled-gement)意指已经接收。)
TCP 通过肯定的 ACK 实现可靠的数据传输。当发送端将数据发出之后会等待对端的 确认应答 。如果有确认应答,说明数据已经成功到达对端。反之,则数据丢失的可能性很大。在一定时间内没有等到 确认应答 ,发送端就可以认为数据已经丢失,并进行重发。

2.序列号

上述这些 确认应答处理、重发控制以及重复控制 等功能都可以通过 序列号 实现。 序列号 是按顺序给发送数据的每一个字节(8位字节)都标上号码的编号(序列号的初始值并非为0。而是在建立连接以后由随机数生成。而后面的计算则是对每一字节加一) 。接收端查询接收数据 TCP首部 中的 序列号 数据的长度 ,将自己下一步应该接收的序号作为确认应答返送回去。就这样,通过 序列号和确认应答号 TCP 可以实现可靠传输。

TCP的数据长度 并未写入TCP首部。实际通信中求得TCP包的长度的计算公式是: IP首部 中的数据包长度-IP首部长度TCP首部长度。

TCP协议不为人知的那些事

3.重发超时

重发超时 是指在重发数据之前,等待确认应答到来的那个特定时间间隔。如果超过了这个时间仍未收到确认应答,发送端将进行数据重发。那么这个重发超时的具体时间长度又是如何确定的呢?

最理想的是,找到一个最小时间,它能保证“确认应答一定能在这个时间内返回”。然而这个时间长短随着数据包途径的网络环境的不同而有所变化。 TCP 要求不论处在何种网络环境下都要提供高性能通信,并且无论网络拥堵情况发生何种变化,都必须保持这一特性。为此,它在每次发包时都会计算往返时间(Round Trip Time也叫RTT,是指报文段的往返时间) 及其偏差(RTT时间波动的值、方差。有时也叫抖动)。将这个往返时间和偏差相加。

TCP协议不为人知的那些事

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

4.连接管理

TCP 提供面向有连接的通信传输。面向有连接是指在 数据通信 开始之前先做好通信两端之间的准备工作。它会在数据通信之前,通过 TCP首部 发送一个 SYN 包作为建立连接的请求等待确认应答(TCP中发送第一个SYN包的一方叫做客户端,接收这个的一方叫做服务端) 如果对端发来确认应答,则认为可以进行数据通信。如果对端的确认应答未能到达,就不会进行数据通信。此外,在通信结束时会进行断开连接的处理(通过发送 FIN 包)。

可以使用 TCP首部 用于控制的字段来管理TCP连接(也叫控制域)。一个连接的建立与断开,正常过程至少需要来回发送7个包才能完成(“三次握手”、“四次挥手”) 。

MSS

在建立TCP连接的同时,也可以确定发送数据包的单位,我们也可以称其为 “最大消息长度”(MSS:Maximum Segment Size) 。最理想的情况是, 最大消息长度 正好是 IP 中不会被分片处理的最大数据长度。

TCP 在传送大量数据时,是以 MSS 的大小将数据进行分割发送。进行重发时也是以 MSS 为单位。 MSS 是在三次握手的时候,在两端主机之间被计算得出。两端的主机在发出建立连接的请求时,会在 TCP首部 中写入 MSS选项 ,告诉对方自己的接口能够适应的 MSS 的大小。(为附加MSS选项,TCP首部将不再是20字节,而是4字节的整数倍)

TCP协议不为人知的那些事

5.窗口控制

TCP 以1个 为单位,每发一个段进行一次确认应答的处理这样的传输方式有一个缺点。那就是包的往返时间越长通信性能就越低。为解决这个问题, TCP 引入了 窗口 这个概念。即使在往返时间较长的情况下,它也能控制网络性能的下降。 确认应答 不再是以每个分段,而是以更大的单位进行确认,转发时间将会被大幅度的缩短。也就是说,发送端主机,在发送了一个段以后不必要一直等待确认应答,而是继续发送。

窗口大小 就是指无需等待 确认应答 而可以继续发送数据的最大值。这个机制实现使用了大量的缓冲区。

TCP 提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量。这就是所谓的 流控制 。它的具体操作是,接收端主机向发送端主机通知自己可以接收数据的大小,于是发送端会发送不超过这个限度的数据。该大小限度就被称作 窗口大小 。窗口大小的值就是由接收端主机决定的。 TCP首部中,专门有一个字段用来通知窗口大小。接收主机将自己可以接收的缓冲区大小放入这个字段中通知给发送端。这个字段的值越大,说明网络的 吞吐量 越高。

TCP协议不为人知的那些事

收到确认应答的情况下,将窗口滑动到确认应答中的序列号的位置。这样可以顺序地将多个段同时发送提高通信性能。这种机制也被称为 滑动窗口控制

窗口控制下的重发控制

重发的情况就两种:一种是数据收到了,应答没有收到,第二种是数据没有收到。

先考虑 确认应答 未能返回的情况。在这种情况下,数据已经到达对端,是不需要再进行重发的。然而,在没有使用窗口控制的时候,没有收到 确认应答 的数据都会被重发。

TCP协议不为人知的那些事

其次,考虑一下某个报文段丢失的情况。当某一报文段丢失后,发送端会一直收到序号为1001的确认应答,这个确认应答好像在提醒发送端“我想接收的是从1001开始的数据”。因此,在窗口比较大,又出现报文段丢失的情况 下,同一个序号的确认应答将会被重复不断地返回。而发送端主机如果 连续3次 收到同一个确认应答(之所以连续收到3次而不是两次的理由是因为,即使数据段的序号被替换两次也不会触发重发机制) ,就会将其所对应的数据进行重发。这种机制比之前提到的超时管理更加高效,因此也被称作 高速重发控制。

TCP协议不为人知的那些事

接收端如果没有收到自己所期望的数据时,会将之前收到的数据进行确认应答,发送端一旦连续3次收到相同的确认应答,就会进行数据的重发。

拥塞控制

有了 TCP 窗口控制 ,收发主机之间即使不再以一个数据段为单位发送确认应答,也能够连续发送大量数据包。然而,如果在通信刚开始时就发送大量数据,也可能会引发其他问题。 一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。在网络出现拥堵时,如果突然发送一个较大量的数据,极有可能会导致整个网络的瘫痪。 TCP为了防止该问题的出现,在通信一开始时就会通过一个叫做 慢启动的算法 得出的数值,对发送数据量进行控制。

此外, TCP 中为了提高网络的利用率,经常使用一个叫做 Nagle的算法

TCP首部格式

TCP协议不为人知的那些事

TCP数据段格式

  • 源端口号: 表示发送端端口号,字段长16位。
  • 目标端口号: 表示接收端端口号,字段长度16位。
  • 序列号: 字段长32位。序列号是指发送数据的位置。每发送一次数据,就累加一次该数据字节数的大小(用来标记数据段的顺序)。序列号不会从0或1开始,而是在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机。然后再将每转发过去的字节数累加到初始值上表示数据的位置。此外,在建立连接和断开连接时发送的 SYN 包和 FIN 包虽然并不携带数据,但是也会作为一个字节增加对应的序列号。
  • 确认应答号: 字段长度32位。是指下一次应该收到的数据的序列号。 实际上,它是指已收到 确认应答号 减一为止的数据。发送端收到这个 确认应答 以后可以认为在这个序号以前的数据都已经被正常接收。因此当前报文段最后一个字节的编号+1即为 确认应答号
  • 数据偏移: 该字段表示 TCP 所传输的数据部分应该从 TCP 包的哪个位开始计算,当然也可以把它看作 TCP首部 的长度。该字段长4位,单位为4字节。(比如该值为4就表示TCP所传输的数据从16个字节的地方开始);如果不包括选项字段的话,此 数据偏移 字段可以设置为5。反之,如果该字段的值为5,那说明从 TCP 包的最一开始到20字节为止都是 TCP 首部,余下的部分为 TCP 数据。
  • 保留: 该字段主要是为了以后扩展时使用,其长度为4位,一般设置为0。
  • 窗口大小: 该字段长为16位。用于通知从相同TCP首部的确认应答号所指位置开始能够接收的数据大小,TCP不允许发送超过此处所示大小的数据。不过,如果窗口为0,则表示可以发送窗口探测,以了解最新的窗口大小。
  • 紧急指针: 该字段长为16位。只有在 URG 控制位为1时有效。该字段的数值表示本 报文 段中紧急数据的指针。
  • 选项: 用于提高 TCP 的传输性能。

控制位

字段长为8位,每一位从左至右分别为 CWR、ECE、URG、ACK、 PSH、RST、SYN、FIN 。这些控制标志也叫做控制位。

TCP协议不为人知的那些事

  • CWR(Congestion Window Reduced:拥塞窗口减少): CWR 标志与后面的 ECE 标志都用于 IP首部 ECN 字段。 ECE 标志为1时,则通知对方已将拥塞窗口缩小。
  • ECE: 表示ECNEcho。置为1会通知通信对方,从对方到这边的网络有拥塞。在收到数据包的 IP首部 ECN 为1时将 TCP首部 中的 ECE 设置为1。

TCP协议不为人知的那些事

校验和

TCP 和UDP一样在计算校验和的时候使用 TCP伪首部 。为了让其全长为16位的整数倍,需要在数据部分的最后填充0。首先将 TCP校验和 字段设置为0。然后以16位为单位进行1的补码和计算,再将它们总和的1的补码和放入校验和字段。 接收端在收到 TCP数据段 以后,从 IP首部 获取IP地址信息构造 TCP 伪首部 ,再进行 校验和 计算。由于校验和字段里保存着除本字段以外其他部分的和的补码值,因此如果计算 校验和 字段在内的所有数据的16位和以后,得出的结果是“16位全部为1(1的补码中该值为0(负数0)、 二进制中为1111111111111111,十六进制中为FFFF,十进制中则为正整 数65535) ”说明所收到的数据是正确的。

TCP协议不为人知的那些事

TCP三次握手

首先需要清楚的一点,不论握手多少次都不能确认一条信道一定是“可靠”的,但通过3次握手可以至少确认它是“可用”的,再往上加握手次数不过是提高它是“可用”的这个结论的可信度。也就是说任意次的握手都是“不可靠”的, 握手成功只能说明握手时的通信是正常的 ,并不能保证握手后的通信是正常的。握手只能保证尽可能的可靠,而不可能保证绝对可靠。

TCP通过三次握手来建立可靠的连接。

TCP协议不为人知的那些事

三次握手示意图

第一次握手:

客户端向服务端发送 连接请求 报文段。该报文段的头部中 SYN=1 ACK=0 ,同时选择一个初始序号 seq=x 。请求发送后,客户端便进入 SYN-SENT 状态。

第二次握手:

服务端收到连接请求报文段后,如果同意连接,会发送一个应答: SYN=1,ACK=1,seq=y,ack=x+1 。发送完应答后服务端进入 SYN-RCVD 状态。

第三次握手:

客户端收到服务端连接同意的应答后,还会向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。该报文段的头部为: ACK=1,seq=x+1,ack=y+1 。该报文发送完毕后,客户端和服务器端都进入 ESTABLISHED 状态,完成 TCP三次握手

相关问题

1.为什么是三次握手,而不是两次或四次?

为什么不是两次握手: 是为了防止已失效的连接请求报文段突然又传送到了服务端,造成服务端资源的浪费。

在一次TCP连接中,客户端A向服务端B发送连接 请求SYN报文段 ,假如这个报文段没有及时被服务端B接收,而是 滞留 在网络的某处,于是客户端A超时重传,再次发送请求连接并且顺利与服务端B建立了连接,交换数据后断开连接。滞留在网络中的某处的陈旧报文就变成了失效的连接请求报文。

但如果这个失效的 请求SYN报文段 ,现在又突然传送到了服务端B处,设想这时是使用两次握手而不是三次握手,服务端B就以为客户端A现在建立请求连接,于是服务端B发出确认,新的连接就建立了,服务端B分配资源,等待客户端A传送数据,但客户端A并没有想要建立 TCP连接 ,不会理会服务端B发送的应答,也不会向服务端B传送数据,于是服务端B就白白等待,空耗资源。

使用 三次握手 可以避免这个情况。服务端B收到客户端A的失效的 陈旧SYN报文段 ,向客户端A发送SYN报文段,选择自己的序号 seq=y ,确认收到客户端A的SYN报文段,确认号 ack=x+1 。第三次握手客户端A收到B的SYN报文段后,从确认号就可得知不应理睬这个 SYN报文段 (因为A现在并没有发送 seq=x 的报文段)。这时,客户端A会发送复位报文段,这个复位报文段中, RST=1,ACK=1,确认号ack=y+1 。服务端B收到A的复位报文,就知道不建立TCP连接,不会分配资源等待A发送数据。

为什么不是四次握手: 因为三次握手已经能说明握手时的通信是正常的,四次握手、五次握手就显得浪费了。

TCP四次挥手

TCP连接 是双向的,在四次挥手中, 前两次 挥手用于断开一个方向的连接, 后两次 挥手用于断开另一方向的连接。

TCP协议不为人知的那些事

第一次挥手

客户端数据发送完成,则它向服务端发送连接释放请求。该请求只有报文头,头中携带的主要参数为: FIN=1,seq=u 。此时,客户端将进入 FIN-WAIT-1 状态。TCP规定, FIN报文段 即使不携带数据,也要消耗一个序号。

第二次挥手

服务器收到客户端连接释放报文,通知相应的高层应用进程,告诉它客户端向服务器这个方向的连接已经释放了。此时服务端进入了 CLOSE-WAIT(关闭等待) 状态,并向客户端发出连接释放的应答,其报文头包含: ACK=1,ack=u+1,seq=v。

客户端收到该应答后,进入 FIN-WAIT-2 状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。

第二次挥手完成后, 客户端到服务端方向的连接已经释放 ,服务端不会再接收客户端的数据,客户端也没有数据要发送了。但服务端到客户端方向的连接仍然存在,服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间。

第三次挥手

服务端将最后的数据发送完毕后,就向客户端发送连接释放报文,其报文头包含: FIN=1,ack=u+1 ,由于在 CLOS-WAIT状态 ,服务端很可能又发送了一些数据,假定此时的序列号为 seq=w ,此时,服务器就进入了 LAST-ACK(最后确认)状态 ,等待客户端的确认。

第四次挥手

客户端收到服务器的连接释放报文后,向服务端发出确认应答,报文头: ACK=1,ack=w+1,seq=u+1 ,此时,客户端就进入了 TIME-WAIT(时间等待)状态 。该状态会持续 2MSL(最长报文段寿命)时间 ,这个期间TCP连接还未释放,若该时间段内没有服务端的重发请求的话,客户端就进入 CLOSED状态 ,服务端只要收到了客户端发出的确认,立即进入 CLOSED状态 。就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

文章来源:智云一二三科技

文章标题:TCP协议不为人知的那些事

文章地址:https://www.zhihuclub.com/179394.shtml

关于作者: 智云科技

热门文章

网站地图