网络协议-传输层协议-TCP协议

TCP 简介与报文格式

TCP 简介

前面学院君已经介绍过传输层有两个具有代表性的协议,分别是 UDP 协议和 TCP 协议,上一篇我们已经介绍过 UDP 协议,它是面向无连接的不可靠传输协议,但是没有复杂的设计也就意味着比较简单高效。今天我们开始介绍 TCP 协议,与 UDP 协议不同,TCP 协议提供的是面向连接的可靠性传输,比 UDP 协议要复杂很多。

TCP 的是英文 Transmission Control Protocol 的首字母缩写,中文含义是传输控制协议。互联网协议标准 TCP/IP 协议族就是取了前面介绍的网络层 IP 协议和这里的传输层 TCP 协议作为其中最为重要的两个协议为基础进行取名,足见 TCP 协议的重要性。

UDP 协议是一种没有复杂控制,提供面向无连接通信服务的一种协议,换句话说,它将部分控制转移给应用程序去处理,自己却只提供作为传输层协议的最基本功能。

TCP 协议与 UDP 协议区别很大,它充分实现了数据传输时的各种控制功能,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。此外,TCP 作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。

为了实现可靠性传输,TCP 协议需要考虑很多事情,比如数据的破坏、丢包、重复以及分片顺序混乱等问题。TCP 通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输。

综上,TCP 协议与 UDP 协议的主要区别如下:

  • TCP 提供面向连接的可靠传输,UDP 则提供面向无连接的不可靠传输,不保证不丢包,也不保证包按顺序到达,而 TCP 可以保证。
  • TCP 有拥塞控制,可以通过窗口控制对网络流量进行控制,避免网络拥塞,UDP 则不会。

由此,也决定了 TCP 协议主要用于可靠传输,而 UDP 主要用于对高速传输或者对实时性要求较高的通信或广播通信。我们日常比较熟悉的 HTTP 协议就是基于 TCP 协议的,此外,用于远程连接的 SSH 协议、发送邮件的 SMTP 协议、传输文件的 FTP 协议都是需要建立连接的可靠传输,也都是基于 TCP 协议。

关于 TCP 协议中连接的建立(三次握手)、重发机制、包顺序的保证、流量控制和拥塞控制的实现原理,放到下一节去详细展开,在此之前,我们先熟悉下 TCP 报文首部格式(包头),以便对相应的数据结构做到心中有数。

TCP 报文首部格式

TCP 报文首部格式比 UDP 要复杂的多:

TCP 中没有表示包长度和数据长度的字段,可由 IP 层获取 TCP 的包长度,由 TCP 的包长可获知数据的长度。

  • 源端口号:发送端端口号
  • 目标端口号:接收端端口号
  • 序列号:发送数据的位置,每发送一次数据,就累加一次该数据字节数的大小,序列号不会从0或1开始,而是在建立连接时由计算机生成的随机数作为初始值,通过 SYN 包发送给接收端主机。此外,建立连接和断开连接时虽然不传输数据,但也会作为一个字节增加对应的序列号
  • 确认应答号:下一次应该接收到的数据的序列号,实际上,它是指已经收到确认应答号减一为止的数据
  • 数据偏移:TCP 所传输的数据部分应该从 TCP 包的哪个位置开始计算,也可以将其看作 TCP 包的长度,单位为4字节
  • 保留:为了以后扩展用
  • 控制位:字段长8位,每一位从左到右分别是CWR、ECE、URG、ACK、PSH、RST、SYN、FIN
    -- CWR(Congestion Window Reduced):与后面的ECE都是用于IP首部的ECN字段,为1时通知对方已将拥塞窗口缩小
    -- ECE(ECN-Echo):置为1时通知通信对方,从对方到这边的网络有拥堵
    -- URG(Urgent Flag):该位为1时,表示包中有需要紧急处理的数据
    -- ACK(Acknowledgement Flag):该位为1时,确认应答的字段变为有效
    -- PSH(Push Flag):该位为1时,表示需要将收到的数据立即传给上层应用协议,为0时则表示先不上传而是缓存起来
    -- RST(Reset Flag):该位为1时,表示TCP连接中出现异常必须强制断开连接
    -- SYN(Synchronize Flag):该位为1时,表示希望建立连接,并在序列号字段进行序列号初始值的设定
    -- FIN(Fin Flag):该位为1时,表示今后不会再有数据发送,希望断开连接
  • 窗口大小:用于通知从相同 TCP 首部的确认应答号所指位置开始能够接收的数据大小
  • 校验和:TCP和UDP一样,在计算校验和时使用伪首部,主要用于检查路由器内存故障或程序漏洞导致的数据破坏
  • 紧急指针:只有在 URG 控制位为1时有效,该字段数值表示本报文段中紧急数据的指针
  • 选项:用于提高 TCP 的传输性能
  • 填充:选项长度不足32位整数倍时将其补齐到32位整数倍

三次握手与可靠传输

TCP 的三次握手

TCP 提供面向有连接的通信传输,面向有连接是指在数据通信开始之前先做好通信两端之间的准备工作。

TCP 的连接建立,我们常常称为三次握手。三次握手的目的是为了确保对连接双方来说,链路都是通的。

三次握手除了双方建立连接外,主要还是为了沟通一件事情,就是 TCP 包序列号的问题。每个连接都要有不同的序列号,这个序列号的起始序号是随着时间变化的。

建立了连接之后,为了维护这个连接,双方都要维护一个状态机,在连接建立的过程中,双方的状态变化时序图如下:

注:在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做确认应答(ACK)。

一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

至此,就建立起了客户端与服务端的连接,双方可以进行数据传输了。

如何实现可靠的传输

TCP 通过肯定的确认应答(ACK)实现可靠的数据传输,当发送端将数据发出之后会等待对端的确认应答。如果确认应答,说明数据已经成功达到对端。反之,则数据有可能丢失。在一定时间内没有等到确认应答,发送端就认为数据已经丢失,并进行重发。因此,即使产生了丢包,仍然能够保证数据能够到达对端,实现可靠传输。

未收到确认应答不一定是数据一定丢失,有可能是对方已经收到数据,只是返回的确认应答在途中丢失,这种情况也会导致发送端因为没有收到确认应答而重新发送数据。

此外,也有可能因为一些其他原因导致确认应答延迟到达,在源主机重发数据以后才到达的情况也屡见不鲜,此时,对源主机没啥影响,但是对目标主机来说,它会反复收到同样的数据。而为了对上层应用提供可靠的传输,必须放弃重复的数据包。为此,就必须引入一种机制,能够识别是否已经接收数据,又能够判断是否需要接收。

上述这些确认应答处理、重发控制以及重复控制等功能都可以通过序列号实现,序列号是按顺序给发送数据的每一个字节都标上号码的编号。

接收端查询接收数据 TCP 首部中的序列号和数据长度,将自己下一步应该接收的序列号作为确认应答发送回去,就这样,通过序列号和确认应答号,TCP 就可以实现可靠传输:

如果在指定的时间内,发送到目标主机的 TCP 包没有得到相应的确认应答,则会进行重发,这个等待的时间就是重发超时时间。这个重发超时的具体计算逻辑如下:

TCP 在每次发包时都会计算往返时间(RTT)及偏差(抖动),将这个往返时间和偏差相加,重发超时的时间,就是比这个总和稍微大一点的值。

在 BSD Unix 和 Windows 中,超时都是按照 0.5 秒为单位进行控制,因此,重发超时都是 0.5 秒的整数倍。不过由于最初的数据包还不知道往返时间,因此其重发超时一般设置为 6 秒左右。

数据被重发之后若还是收不到确认应答,则再次发送。此时等待确认应答的时间将会以 2 倍、4 倍的指数函数增长。

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

TCP 除了提供面向连接的可靠传输之外,还能对流量进行控制,从而在空闲时加大传输数据量提高通信效率,在繁忙时减少传输数据量避免网络拥塞。关于 TCP 的流量控制实现我们将在下一篇介绍。

流量控制和拥塞控制

TCP 数据传输单位

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

TCP 在传输大量数据时,是以 MSS 的大小将数据分割发送。重发时也是以 MSS 为单位。

MSS 是在三次握手时,在两端主机之间被计算得出,两端主机在发出建立连接的请求时,会在 TCP 首部写入 MSS 选项,告诉对方自己的接口能够适应的 MSS 的大小,然后会在两者之间选一个比较小的值投入使用。

TCP 以一个段(Segment)为单位,每发送一个段进行一次确认应答的处理,这样的传输方式有一个显著的缺点,就是包往返的时间越长通信性能就越低。

利用窗口控制提高速度

为了解决上述问题,TCP 引入了窗口的概念。在此机制下,确认应答不再以每个分段,而是以更大单位进行确认,也就是说,发送端主机在发送完一个段之后,不必一直等待确认应答,而是继续发送。

窗口的大小就是指无需等待确认应答而可以继续发送数据的最大值,这个机制实现了使用大量的缓冲区(临时保存收发数据的场所),通过对多个段同时进行确认应答的功能,而不是每个分段都返回确认应答,从而大幅度缩短发送时间。

如下图所示,白色高亮的方框就是窗口,在这个窗口内的数据即使没有收到确认应答也可以立即发送出去(在得到确认应答之前,需要在缓冲区保存这部分发送的数据,以便于重发数据):

在滑动窗口以外的部分包括尚未发送的数据以及已经确认对端已经收到的数据,当数据发出后若如期收到确认应答就不再进行重发,此时数据就会从缓冲区中清除,否则丢失的数据需要进行重发(从缓冲区取)。

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

流量控制

发送端会根据自己的实际情况发送数据,但是,接收端可能收到的是一个毫无关系的数据包又可能会在处理其它问题上耗费一些时间,甚至在高负荷的情况下无法接收任何数据。如此一来,如果接收端将本该接收的数据丢弃的话,就又会触发重发机制,从而导致网络流量的无端浪费。

为了防止这种现象的发生,TCP 提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量。这就是所谓的流量控制。

它的具体操作是,接收端主机向发送端主机通知自己可以接收数据的大小,于是发送端会发送不超过这个限度的数据,该大小限制被称作窗口大小。

TCP 首部中,专门有一个字段用来通知窗口大小,接收主机将自己可以接收的缓冲区大小放入这个字段中通知给发送端。这个字段值越大,说明网络的吞吐量越高。

不过,接收端的这个缓冲区一旦面临数据溢出风险,窗口大小的值也会随之被设置为一个更小的值通知给发送端,从而控制数据发送量。也就是说,发送端会根据接收端的指示,对发送数据的量进行控制。这个形成了一个完整的 TCP 流量控制。

拥塞控制

有了 TCP 的窗口控制,收发主机之间即使不再以一个数据段为单位发送确认应答,也能连续发送大量数据包。然而,如果在通信一开始就发送大量数据,容易引发其它问题。

一般来说,计算机网络都处在一个共享的环境,因此,也有可能因为其它主机之间的通信导致网络拥堵,在网络拥堵时,如果发送一个较大量的数据,极有可能导致整个网络的瘫痪。

TCP 为了防止该问题的出现,在通信一开始就会通过一个叫做慢启动的算法得出的值,对发送数据量进行控制。

首先,为了在发送端调节所要发送数据的量,定义了一个叫做「拥塞窗口」的概念,于是在慢启动的时候,将这个拥塞窗口的大小设置为1个数据段(1MSS)发送数据,之后每收到一个确认应答(ACK),就将拥塞窗口的值加1,在发送数据包时,将拥塞窗口的大小与接收端主机通知的窗口大小做比较,然后按照它们当中较小的那个值,发送比它还要小的数据量。

有了这种机制,就可以有效减少通信开始时连续发包导致的网络拥堵。此外,为了防止超时重发时,随着包往返导致的拥塞窗口快速增长(指数增长),引入了慢启动阀值的概念,只要拥塞窗口的大小超过这个阀值,每收到一次确认应答时,只允许以下面这种方式放大拥塞窗口:

拥塞窗口越大,确认应答的数目也会增加,不过每收到一个确认应答,其涨幅也会逐渐减小,甚至小过比一个数据段还要小的字节数。

需要注意的是,TCP 开始通信时,并没有设置慢启动阀值,只有超时重发时才会设置该值,初始值为当时拥塞窗口的一半。

总结起来,用于流量控制的滑动窗口是为了防止把接收端缓存塞满,用于拥塞控制的拥塞窗口是为了防止把网络塞满,导致网络拥塞。TCP 的拥塞控制主要来避免两种现象,包丢失和超时重发。一旦出现了这些现象就说明,发送速度太快了,要慢一点。

猜你喜欢

转载自www.cnblogs.com/stringarray/p/12960593.html