• TCP 协议简析


    基础知识

    作为网络基础知识,相信大家对 TCP/IP 5层协议模型 不会感到陌生:

    • 应用层(应用层,表示层,会话层):具体的应用逻辑
    • 传输层:提供进程间的通信服务
    • 网络层:提供端系统之间的数据透明传送
    • 链路层:数据在具体物理设备上的表示(设备驱动、网卡)
    • 物理层:具体物理设备之间的链接(电缆)

    其中的 TCP 与 IP 协议是互联网的基石。

    IP 协议

    IP Internet Protocol 协议解决了大规模、异构网络的互联互通问题:

    1. 将不同物理设备的帧转换为统一的 IP 数据报 datagram,实现异构物理设备间的通信
    2. 为每个设备分配一个唯一的逻辑地址,屏蔽物理地址的差异,实现异构物理设备间的寻址

    IP 协议提供不可靠 Unreliable、无连接 Connectionless 的数据报传输功能:

    • 不可靠:不能保证IP数据报能成功地到达目的地
    IP 协议仅保证提供 尽力而为Best Effort 的输服务。

    当发生某种错误时,如某个路由器暂时用完了缓冲区,IP有一个简单的错误处理算法:丢弃数据报,然后发送ICMP消息报给信源端。

    • 无连接:不维护任何关于后续数据报的状态信息
    每个数据报的处理是相互独立的。

    如果一信源向相同的信宿发送两个连续的数据报(先是A,然后是B),每个数据报都是独立地进行路由选择,可能选择不同的路线,因此B可能在A到达之前先到达。

    受限于协议的特性,IP 数据报可能出现 丢包乱序。因此基于IP协议的互联网络必然是一个不可靠的网络。

    TCP 协议

    TCP Transport Control Protocol 协议是一个面向连接的、可靠的、基于字节流的传输层协议。其主要目的是在不可靠的互联网络上提供可靠的端到端字节流传输。

    互联网络的不同部分可能有截然不同的拓扑结构、带宽、延迟、数据包大小和其他参数。
    TCP的设计目标是能够动态地适应互联网络的这些特性,而且具备面对各种故障时的健壮性。

    其核心的功能点可以概括为两方面:

    • 流量控制:保证数据传输效率

      • 数据分片:在发送端对用户数据进行分片,在接收端进行重组,由TCP确定分片的大小并控制分片和重组
      • 滑动窗口:TCP连接每一方的接收缓冲空间大小都固定,接收端只允许另一端发送接收端缓冲区所能接纳的数据,TCP在滑动窗口的基础上提供流量控制,防止较快主机致使较慢主机的缓冲区溢出
    • 可靠传输:保证数据传输正确性

      • 到达确认:接收端接收到分片数据时,根据分片数据序号向发送端发送一个确认
      • 超时重发:发送方在发送分片时启动超时定时器,如果在定时器超时之后没有收到相应的确认,重发分片
      • 失序处理:作为IP数据报来传输的TCP分片到达时可能会失序,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层
      • 重复处理:作为IP数据报来传输的TCP分片会发生重复,TCP的接收端必须丢弃重复的数据
      • 数据校验:TCP将保持它首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到分片的检验和有差错,TCP将丢弃这个分片,并不确认收到此报文段导致对端超时并重发

    TCP 细节

    报文结构

    TCP 的基本传输单位是报文段 segment,其结构如下:

    • 端口号是 16bit 的无符号数,每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。
      这两个值加上 IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。

    • 序号是 32bit 的无符号数,用来标识由发送端发出的数据字节流,它表示在这个报文段中的的第一个数据字节。 TCP用序号对每个字节进行计数。

    • 确认序号也是 32bit 的无符号数,用于标识发送确认的一端所期望收到的下一个序号。确认序号应当是上次已成功收到数据字节序号加1。

    • 首部长度给出首部中32bit字的数目。需要这个值是因为任选字段的长度是可变的。然而,没有任选字段,正常的长度是20字节。这个字段占4bit,因此TCP最多有60字节的首部。

    • 特殊标记位。它们中的多个可同时被设置为1。

      • URG 紧急指针
      • ACK 确认序号有效
      • PSH 接收方应该尽快将这个报文段交给应用层
      • RST 重建连接
      • SYN 同步序号用来发起一个连接
      • FIN 发端完成发送任务
    • 窗口大小是一个16bit字段,其单位为字节,因而窗口大小最大为65535字节。发送端可以通过这个字段来声明接受窗口大小,告知对方自己期望接受的数据量,从而实现流量控制。

    • 选项部分是用于支持 TCP 的一些扩展功能:数据分片、窗口放大、时间戳

    数据分片与重组

    路径MTU

    目前最常见的局域网协是以太网Ethernet,其网络拓扑为总线型拓扑,即多个计算机共享同个信道。计算机要发送信息时,会通过共享线路将信息广播到其他计算机上。当多个计算机同时发送消息时,彼此之间会有干扰,导致数据发送失败。

    因此以太网引入了一个冲突检测collision detection的功能,保证通过时刻只有一个计算机在网络上发送消息。

    这造成了两个问题:

    • 每次发送都要执行冲突检测,需要额外的通信开销(数据封装、设备资源)。如果每次只传输一个字节,传输性能会十分低下,因此应该尽可能减少传输次数,即一次尽可能传输更多的数据。

    • 信道是共享的,同一时刻只能有一个计算机在发送消息。如果每次都传输完整的数据,那么其他计算机会等待很久才能响应,这对于一些实时通信任务来说是个噩梦。

    以太网定义其基本的通信单位是数据帧 frame,其最基本的格式如下:

    为了在效率与公平上达到一个平衡,以太网定义每个数据帧的最大为 1518。除去固定 14 字节的头部与 4 字节的尾部,数据负载的长度最多为 1500 字节。这个 1500 就是我们常说的以太网的最大传输单元 —— MTU

    [lhop@localhost ~]$ netstat -i
    Kernel Interface table
    Iface       MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
    eth0       1500   0   947894      0      0      0   597731      0      0      0 BMRU
    lo        65536   0        0      0      0      0        0      0      0      0 LRU
    

    MTU 是由底层网络的特性所决定的,如果两台主机之间的通信要通过多个网络,那么每个网络的链路层就可能有不同的 MTU。通信路径中的最小 MTU,决定了整个通信链路的 MTU 上限,它被称作 路径MTU

    IP 分片

    IP包头中用2个字节描述 IP 报文总长度,因此IP数据包的最大长度是64K字节(65535)。如果IP层有一个数据报要传,而且数据的长度比链路层的MTU还大,那么IP层就需要进行分片fragmentation,把数据报分成若干片并保证每一片都小于MTU。

    IP层接收到一份要发送的IP数据报时,它要判断向本地哪个接口发送数据(选路),并查询该接口获得其MTU。IP把MTU与数据报长度进行比较,如果需要则进行分片。

    数据分片 可以发生在原始发送端主机上,也可以发生在中间路由器上。数据重组 由目的端的IP层来完成,其目的是使分片和重新组装过程对传输层 (TCP和UDP) 是透明的

    当IP数据报被分片后,每一片都成为一个分组,具有自己的IP首部,并在选择路由时与其他分组独立。这样,当数据报的这些片到达目的端时有可能会失序,但是在IP首部中有足够的信息让接收端能正确组装这些数据报片。

    尽管IP分片过程看起来是透明的,但有一点让人不想使用它:即使只丢失一片数据也要重传整个数据报。其原因如下:

    如果对数据报分片的是中间路由器,而不是起始端系统,那么起始端系统就无法知道数据报是如何被分片的,无法只重传数据报中的一个数据报片。

    传输层分片

    当在以太网上传输数据时,TCP 协议在建立连接时,会根据以太网的 MTU 与 IP 层的头部长度来协商一个最大报文段长度 MSS = MTU - IP头部长度 = 1472。

    • 发送端向 IP 层发送报文前,会按照 MSS 分割成多个报文段,然后逐个向 IP 层发送。
    • 接收端从 IP 层接收到报文后,会将分片进行重组。

    整个过程在传输层完成,避免了 IP 层的分片处理。

    当使用 UDP 协议时,用户发送的报文会被原封不动的发送给 IP 层。


    UDP包头内有总长度字段,同样为两个字节,因此UDP数据包的总长度被限制为 65535 从而保证这样恰好可以放进一个IP包内,简化了 UDP/IP 的实现。

    当 IP 层需要发送大于 MTU 的数据报时,IP 层会对数据进行分片—重组处理。如果 UDP 数据报较大,并且网络状况较差的话,频繁的重传会造成传输效率的低下。

    因此,使用 UDP 进行数据传输时,发送端最好将 UDP 数据报的大小控制在 MSS 以内。

    到达确认与超时重传

    前面说过,TCP 提供一种面向连接的、可靠的字节流服务。而 IP 层本身不存在连接的概念,因此TCP 需要在进程间维护一个传输字节流的连接。此外,为了保证消息不会产生乱序,TCP 会为在连接中传输的每个字节分配一个唯一的序列号,用于保证字节直接的顺序不会被打乱。

    三次握手

    建立连接是由 Client 向 Server 发起的:

    1. Client 会给 Server 发送一个 SYN
    2. Server 每收到一个新的 SYN 包都会创建一个半连接,然后向 Client 发送 SYNACK
    3. Client 在收到 Server 的 SYN/ACK 包后会发出 ACK,Server 收到该 ACK 后,三次握手就完成并产生了一个 TCP 全连接

    在建立连接时,双方会在 SYN 报文中协商一个初始序号 ISN

    • 发送端每次发送新数据时,会递增这个序号seq,表示该报文段首字节的字节流编号。
    • 接受方接收到报文后,会返回一个确认序号ack,表明这个序号之前的数据已经收到。

    SYN 报文的特殊之处在于:

    虽然其不附带有字节数据,但是其本身需要占用一个字节序号,因此 ACK 包中返回的确认序号需要在 ISN 的基础上递增一个字节。

    四次挥手

    断开连接可以由 Client 或 Server 任意一端发起,也可以双方同时发起:

    • 主动关闭:主动向对端发送 FIN 包关闭链接,然后会接收 ACK
    • 被动关闭:接收到对端的 FIN 包后再发送 FIN 包,也会向对端回 ACK

    主动关闭方维持 TIME_WAIT 状态的意义:

    最后发送的这个 ACK 包可能会被丢弃掉或者有延迟,这样对端就会再次发送 FIN 包。如果不维持 TIME_WAIT 这个状态,那么再次收到对端的 FIN 包后,本端就会回一个 Reset 包,这可能会产生一些异常

    超时重传

    前面提到,TCP 数据报可能会在传输过程中丢失:

    • 发送方发送的分组丢失
    • 接收方响应确认的分组丢失

    上面两种丢失情况,对于发送方来说结果是相同的:接收不到确认分组。因此 TCP 引入了超时重传机制来解决报文丢失的问题:

    发送端每发送一个报文段,TCP会为其保留一个副本并设定一个计时器并等待确认信息。如果计时器超时,而发送的报文段中的数据仍未得到确认,则重传这一报文段,直到发送成功为止。

    影响超时重传机制协议效率的一个关键参数是重传超时时间 RTO:

    • RTO 过大将会使发送端经过较长时间的等待才能发现报文段丢失,降低了连接数据传输的吞吐量
    • RTO 过小可能将一些延迟大的报文段误认为是丢失,造成不必要的重传,浪费了网络资源

    TCP协议采用自适应算法记录数据包的往返时延 RRT,并根据往返时延设定 RTO 的取值:

    RTT = 传播时延 + 排队时延(路由器和交换机)+数据处理时延(应用程序)

    一般来说,RTO 的取值会略大于 RTT 以保证数据包的正常传输。

    拥塞窗口与接收窗口

    拥塞窗口

    在实际的应用中,一个 TCP 连接传输数据时不可能独占网络,因此网络的可用带宽是动态变化的。为了最大化的利用网络带宽,TCP 实现了一个动态调整传输速率的拥塞控制机制:

    • 网络空闲时提升传输速度,提高数据传输效率
    • 网络拥塞时降低传输速度,避免丢包触发不必要的超时重传

    该机制的核心是发送端的拥塞窗口

    在发送端设置一个大小为 cwnd 发送窗口结构,cwnd 根据网络的拥塞情况(TCP 重传率)动态变化,发送端只能发送小于或等于拥塞窗口大小的数据。

    拥塞控制是 TCP 协议的核心,并且对传输性能有着至关重要的影响。其中一个著名的拥塞算法是 TCP Reno,大致可将其分为 4 个阶段:

    • 慢启动
    TCP 连接建立好后,发送方就进入慢速启动阶段。
    初始时 cwnd 为 1,然后逐渐地增大发包数量。这个阶段每经过一个 RTT,发包数量就会翻倍,直到 cwnd 增大到一个阈值 ssthreshold(该阶段的 cwnd 以指数形式增长)
    • 拥塞避免
    当 cwnd 增大到 ssthreshold 后 TCP 就进入了拥塞避免阶段。
    在这个阶段 cwnd 不再成倍增加,而是一个 RTT 增加 1,即缓慢地增加 cwnd,以防止网络出现拥塞(该阶段的 cwnd 线性增长)
    • 快速重传 / 超时重传
    随着传输速率的不断上升,丢包是难以避免的,针对不同的丢包场景,TCP 引入了两种重传策略:
    • 超时重传
      发送端超过 RTO 后仍然收不到 ack 响应,会据此判断此时网络已经出现了拥塞:

      1. ssthreshold = cwnd / 2
      2. cwnd = 1
      3. 进入慢启动阶段
    • 快速重传
      发送端连续收到 3 个重复的 ack 序号后,会据此判断数据包出现了丢失,但此时网络未出现拥塞情况(拥塞窗口不必恢复到初始值):

      1. cwnd = cwnd / 2
      2. ssthreshold = cwnd
      3. 进入快速恢复阶段
    • 快速恢复
    快速重传阶段检测到报文丢失后,会进入快速恢复阶段,立即重传丢失的数据段而无需等待 RTO 超时:
    • cwnd = cwnd + 3 * MSS
    • 重传数据
    • 再每收到一个重复ACK,cwnd = cwnd + 1
    • 直到收到不重复ACK,设置cwnd = ssthreshold,进入拥塞避免阶段

    接收窗口

    除了网络状况外,发送方还需要知道接收方的处理能力。如果接收方的处理能力差,那么发送方就必须要减缓它的发包速度。接收方的处理能力是通过接收窗口 rwnd 来表示的:

    • 接收方在收到数据包后,会给发送方回一个 ack,并后把自己的 rwnd 大小写入到 TCP 头部的 win 这个字段,这样发送方就能根据这个字段来知道接收方的 rwnd
    • 发送方在发送下一个 TCP 数据报的时候,会先对比发送方的 cwnd 和接收方的 rwnd,得出这二者之间的较小值,然后控制发送的数据量不能超过这个较小值

    rwnd 的最大值由最大接收缓冲区空间确定,并且用户可以动态指定每个 TCP 连接的接收缓冲大小。然而 TCP 头部中的 win 这个字段只有 16bit,能够表示的大小最大只有 65535(64K)。为了支持更大的接收窗口rwnd以满足高性能网络,TCP 提供了窗口缩放选项 WSopt 来突破这一限制。

    此外,针对一些 RTT 较大的高带宽网络,提高 rwnd 有助于提升传输效率,为此 TCP 引入了能够根据网络状况自动调整接收缓存的算法,这种自动调整技术也称为 DRS (Dynamic Right-Sizing)




    参考资料:

  • 相关阅读:
    (十一)QPainter绘图, QPixmap,QImage,QPicture,QBitmap
    (十)事件,定时器
    (九)自定义控件
    (八)控件介绍,QLable
    (六)QDialog,QMessageBox,QFileDialog,QColorDialog颜色,QFontDialog字体
    (七)布局
    (五)qt资源文件
    (四)窗口mainwindow
    (三)信号与槽
    JMeter学习-011-JMeter 后置处理器实例之
  • 原文地址:https://www.cnblogs.com/buttercup/p/13472935.html
Copyright © 2020-2023  润新知