Linux高性能服务器编程第三章TCP协议详解

TCP协议时TCP/IP协议族另一个重要的协议,和IP协议相比,TCP协议更靠近应用层

本章节分为四个部分:

  • TCP头部信息。TCP头部信息出现在每个TCP报文段中,用于指定通信的源端端口号、目的端端口号,管理TCP连接,控制两个方向的数据流。
  • TCP状态转移过程。TCP连接的任意一端都是一个状态机。在TCP连接从建立到断开的整个过程中,连接两端的状态机将经历不同的状态变迁。理解TCP状态转移对于调试网络应用程序将有很大的帮助。
  • TCP数据流。通过分析TCP数据流,我们就可以从网络应用程序外部来了解应用层协议和通信双方交换的应用程序数据。这一部分将讨论两种类型的TCP数据流:交互数据流和成块数据流。TCP数据流中有一种特殊的数据,称为紧急数据,我们也将简单讨论之。
  • TCP数据流的控制。为了保证可靠传输和提高网络通信质量,内核需要对TCP数据流进行控制。这一部分讨论TCP数据流控制的两个方面:超时重传和拥塞控制。

TCP服务的特点

传输层协议主要有两个:TCP协议和UDP协议。TCP协议相对于UDP协议的特点是:面向连接、字节流和可靠传输。
使用TCP协议通信的双方必须先建立连接,然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输。TCP连接是全双工的,即双方的数据读写可以通过一个连接进行。完成数据交换之后,通信双方都必须断开连接以释放系统资源。

TCP协议的这种连接是一对一的,所以基于广播和多播(目标是多个主机地址)的应用程序不能使用TCP服务。而无连接协议UDP则非常适合于广播和多播。

image-20231102093745292

TCP的头部结构

TCP头部信息出现在每个TCP报文段中,用于指定通信的源端端口,目的端端口,管理TCP连接等,本节详细介绍TCP的头部结构,包括固定头部结构和头部选项。

固定头部结构

如下图所示,为了管理TCP连接和控制数据流提供了足够的信息

image-20231102093959613

  • 16位端口号

    告知主机该报文段来自哪里,以及传给哪个上层协议或应用程序(目的端口)的。进行TCP通信时,客户端通常使用系统自动选择的临时端口号,服务端则使用知名服务端端口号

  • 32位序号 seq

    一次TCP通信过程中某一个传输方向的字节流每个字节的编号。假设主机A和主机B进行TCP通信,A发送给B的第一个TCP报文段中,序号值被系统初始化为某个随机值ISN,那么在这个传输方向上(A-B)后续的TCP报文段序号值将会被系统设置成ISN加上该报文段所携带数据的第一个字节在整个字节流的偏移:比如某个TCP报文段的数据是字节流中的1025-2048字节,那么这个报文段的序号值就是ISN+1025,另一个传输方向的TCP报文段的序号值也具有相同含义。

  • 32位确认号 ack

    用作对另一方发送来的TCP报文段的响应,是收到的TCP报文段的序号值+1

  • 4位头部长度

    标识该TCP头部有多少个32bit字(4字节)

  • 6位标志位

    • URG标志 紧急指针
    • ACK标志 确认号是否有效,携带ACK的TCP报文段为确认报文段
    • PSH标志 提示接收端应用程序应该立即从TCP接收缓冲区读走数据
    • RST标志 要求对方重新建立连接
    • SYN标志 标识请求建立一个连接,携带SYN标志的TCP报文段为同步报文段
    • FIN标志 通知对面本端要关闭连接 携带FIN标志的TCP报文段为结束报文段
  • 16位窗口大小

    流量控制的一个手段,通知对方本端的接收缓冲区还能容纳多少字节数据

  • 16位校验和

    由发送端填充,接收端对TCP报文段执行CRC算法以检验报文段在传输过程中是否损坏

  • 16位紧急指针

    是发送端向接收端发送紧急数据的方法

头部选项

TCP头部的最后一个选项字段是可变长的可选信息,最多包含40个字节

image-20231102095850136

  • kind说明选项的类型

  • length指定该选项的总长度,包括kind和length包含的两个字节

  • info是具体信息

    image-20231102100013665

TCP的建立和关闭

三次握手和四次挥手,这里不再赘述

超时重传

观察TCP报文段被发送的时间间隔,它们分别为0.2s、0.4 s、0.8 s、1.6 s和3.2s。由此可见,TCP一共执行5次重传,每次重传超时时间都增加一倍(因此,和TCP超时重连的策略相似)。在5次重传均失败的情况下,底层的IP和ARP开始接管,直到telnet客户端放弃连接为止。

Linux有两个重要的内核参数与TCP超时重传相关:/proclsys/net/ipv4/tcp_retries1和/proc/sys/net/ipv4/tcp_retries2。前者指定在底层IP接管之前TCP最少执行的重传次数,默认值是3。后者指定连接放弃前TCP最多可以执行的重传次数,默认值是15(一般对应13 ~ 30 min)。在我们的实例中,TCP超时重传发生了5次,连接坚持的时间是 15 min (可以用date命令来测量)。

虽然超时会导致TCP报文段重传,但TCP报文段的重传可以发生在超时之前,即快速重传,这将在下一节中讨论。

拥塞控制

慢启动

image-20231102101428705

其中N是此次确认中包含的之前未被确认的字节数。这样一来,CWND将按照指数形式扩大,这就是所谓的慢启动。慢启动算法的理由是,TCP模块刚开始发送数据时并不知道网络的实际情况,需要用一种试探的方式平滑地增加CWND的大小。

拥塞避免

使得CWND按照线性方式增加,减缓其扩大速度

快速重传和快速恢复

在很多情况下,发送端都可能接收到重复的确认报文段,比如TCP报文段丢失,或者接收端收到乱序TCP报文段并重排之等。拥塞控制算法需要判断当收到重复的确认报文段时,网络是否真的发生了拥塞,或者说TCP报文段是否真的丢失了。具体做法是:发送端如果连续收到3个重复的确认报文段,就认为是拥塞发生了。然后它启用快速重传和快速恢复算法来处理拥塞:

  1. 当收到第3个重复的确认报文段时,计算 ssthresh,然后立即重传丢失的报文段,设置CWND。
  2. 每次收到1个重复的确认时,设置CWND=CWND+SMSS。此时发送端可以发送新的TCP报文段(如果新的CWND允许的话)。
  3. 当收到新数据的确认时,设置CWND=ssthresh (ssthresh 是新的慢启动门限值,由第一步计算得到)。