文章目录
- [1 源端口号(Source Port)](#1 源端口号(Source Port))
- [2 目标端口号(Destination Port)](#2 目标端口号(Destination Port))
- [3 序列号(Sequence Number)](#3 序列号(Sequence Number))
- [4 确认应答号(Acknowledgment Number)](#4 确认应答号(Acknowledgment Number))
- [5 数据偏移(Data Offset)](#5 数据偏移(Data Offset))
- [6 保留(Rsrvd)](#6 保留(Rsrvd))
- [7 控制域(Control Domain)](#7 控制域(Control Domain))
- [8 窗口大小(Window Size)](#8 窗口大小(Window Size))
- [9 校验和(Checksum)](#9 校验和(Checksum))
- [10 校验和存在的目的](#10 校验和存在的目的)
- [11 紧急指针(Urgent Pointer)](#11 紧急指针(Urgent Pointer))
- [12 可选项(Options)](#12 可选项(Options))
- [13 窗口大小与吞吐量](#13 窗口大小与吞吐量)
下图展示了 TCP 首部(Header) 的格式,相较于 UDP,TCP 首部结构复杂得多,因为它需要支持面向连接、可靠传输、流量控制、拥塞控制等多种机制。

与 UDP 不同,TCP 首部中没有表示包长度和数据长度的字段。
我们可以通过 IP 层获知 TCP 的包长度,进而由 TCP 的包长度可知数据的长度。
1 源端口号(Source Port)
源端口号表示发送端端口号,用于标识发送端的应用程序或进程,字段长 16 位。
2 目标端口号(Destination Port)
目标端口号表示接收端端口号,用于标识接收端的应用程序或进程,字段长 16 位。
3 序列号(Sequence Number)
序列号字段长 32 位,序列号(有时也叫序号)是指发送数据的位置,每发送一次数据,就累加该数据字节数。
序列号不会从 0 或 1 开始,而是在建立连接时由计算机随机生成一个数作为初始值,并通过 SYN 数据包传给接收端主机。
此后,再将每次转发过去的字节数累加到初始值上表示数据的位置。
此外,在建立连接和断开连接时,发送的 SYN 和 FIN 数据包虽然并不携带数据,但是序列号也会增加 1。
4 确认应答号(Acknowledgment Number)
确认应答号字段度为 32 位,表示下一次应该收到的数据的序列号。
实际上,它是指已收到确认应答号减 1 为止的数据,发送端收到该确认号以后,可以认为此序列号之前的数据都已经被正常接收。
5 数据偏移(Data Offset)
该字段表示 TCP 所传输的数据应该从 TCP 数据包的哪位开始计算,也可以把它看作 TCP 首部的长度。
该字段长 4 位,单位为 4 字节(即 32 位)。在不包括可选项字段的情况下,TCP 首部长度为 20 字节,此时数据偏移字段应设置为 5。
反之,如果该字段的值为 5,则说明从 TCP 数据包的最开始到 20 字节都是 TCP 首部,剩下的部分为 TCP 数据。
6 保留(Rsrvd)
该字段长度为 4 位,主要是为了以后扩展时使用。
一般设置为 0,但即使收到该字段不为 0 的 TCP 数据包,一般也不会被丢弃。
7 控制域(Control Domain)
该字段长 8 位,每个控制标志占 1 位,从左至右依次为 CWR、ECE、URG、ACK、PSH、RST、SYN、FIN。
这些控制标志也叫作控制域,具体结构如下图所示。

CWR(Congestion Window Reduced):
与 ECE 标志、IP 首部的 ECN 字段一起结合使用,用于通知发送方已收到对方的拥塞控制信号,表示发送方已将拥塞窗口缩小。
ECE(ECN-Echo):
当该位为 1 时,接收端会通知通信对方,表示从对方到这边的网络有拥塞。
当接受端收到一个 IP 首部中 ECN 标志为 1 的数据包时,它会将 TCP 首部中的 ECE 设置为 1。
URG(Urgent Flag):
该位为 1 时,表示数据包中有需要紧急处理的数据,对于需要紧急处理的数据,需要与紧急指针相配合。
ACK(Acknowledgment Flag):
该位为 1 时,表示确认应答号字段变为有效,除首次建立连接的 SYN 数据包外,TCP 规定该标志必须始终置 1。
PSH(Push Flag):
该位为 1 时,表示需要将收到的数据立即传给上层应用程序;为 0 时,则不需要立即传输而是先进行缓存。
RST(Reset Flag):
该位为 1 时,表示 TCP 连接中出现异常,必须强制断开连接。
例如,一个没有使用的端口即使发来连接请求,也无法进行通信,此时可以返回一个 RST 设置为 1 的数据包。
此外,在程序宕掉或切断电源等原因导致主机重启的情况下,由于所有的连接信息全部被初始化,因此原有的 TCP 通信将不能继续进行。
在这种情况下,如果通信对方发送一个设置为 1 的 RST 数据包,就会使通信强制断开连接。
SYN(Synchronize Flag):
SYN 用于建立连接。SYN 为 1 时,表示希望建立连接,并在序列号字段进行序列号初始值的设置。
Synchronize 本身有同步的意思,意味着建立连接的双方,序列号和确认应答号要保持同步。
FIN(FIN Flag):
该位为 1 时,表示今后不会再有数据发送,希望断开连接。
当通信结束希望断开连接时,通信双方的主机之间可以相互交换 FIN 设置为 1 的 TCP 报文段。
每台主机对对方的 FIN 数据包进行确认应答以后就可以断开连接。
不过,主机收到设置为 1 的 TCP 报文段以后不必马上发送 FIN 数据包,可以等到缓冲区中的所有数据都因已经成功发送而被自动删除之后再发。
8 窗口大小(Window Size)
该字段长 16 位,用于通知从 TCP 首部的确认应答号所指位置开始,接收端能够接受的数据大小(单位:字节)。
TCP 不允许发送超过此处所示大小的数据。
不过,如果窗口为 0,则表示可以发送窗口探测(Window Probe),以获知接收端最新的窗口大小,但这个数据必须是 1 字节。
9 校验和(Checksum)
TCP 校验和与 UDP 的校验和相似,区别在于 TCP 的校验和无法关闭。
TCP 和 UDP 一样,在计算校验和时使用 TCP 伪首部,TCP伪首部如下图所示:

伪首部中的源 IP 地址和目标 IP 地址在 IPv4 地址的情况下都是 32 位字段,为 IPv6 地址时,都是 128 位字段。
填充是为了补充位数,一般填入 0。
为了让其全长为 16 位的整数倍,需要在数据部分的最后填充"0"。
计算步骤如下:
- 首先将 TCP 校验和字段设置为"0"。
- 然后以 16 位为单位进行 1 的补码和计算。
- 最后将得到的 1 的补码和放入校验和字段。
接收端主机在收到 TCP 报文段以后,会通过 IP 首部获取 IP 地址信息,进而构造出 TCP 伪首部,然后重新进行校验和计算。
由于校验和字段保存除本字段以外其他部分的和的补码值,因此如果计算校验和字段在内的所有数据的 16 位以后,得出的结果是"16 位全部为 1",说明所收到的数据是正确的。
10 校验和存在的目的
有噪声干扰的通信,途中如果出现位错误,可以由数据链路的 FCS 检查出来,为什么 TCP 或 UDP 中需要校验和呢?
相比检查噪声影响导致的错误,TCP 与 UDP 的校验和是一种进行路由器内存故障或应用程序漏洞导致的数据是否被破坏的检查。
有 C 语言编程经验的人知道,如果指针使用不当,极有可能会破坏内存中的数据结构。
路由器的应用程序中可能会存在漏洞,或应用程序异常宕机的情况。
在互联网中,发送数据包要经由好多个路由器,一旦发送途中的某一个路由器发生故障,经过此路由器的数据包、协议首部或数据就极有可能被破坏。
即使是在这种情况下,如果 TCP 或 UDP 能够提供校验和计算,那么也可以判断协议首部和数据是否被破坏。
11 紧急指针(Urgent Pointer)
该字段长 16 位。只有在 URG 控制域为 1 时,紧急指针才有效。该字段的值表示报文段中紧急数据的指针。
正确来讲,从数据的首位到紧急指针所指的位置为紧急数据。也就是说,紧急指针指出了紧急数据的末尾在报文段中的位置。
如何处理紧急数据属于应用层序的问题,一般在暂时中断通信,或中断处理的情况下使用。
例如,在 Web 浏览器中点击停止按钮,或在 TELNET 中输入 Ctrl + C 时,都会有 URG 为 1 的数据包。此外,紧急指针也用作数据流分段的标志。
12 可选项(Options)
可选项字段用于提高 TCP 的传输性能。受数据偏移字段(首部长度字段)的限制,可选项字段的最大长度为 40 字节。
另外,TCP 需要通过填充使可选项字段的总长度为 32 位的整数倍。具有代表性的 TCP 可选项如表所示,这里仅挑选重点类型进行讲解。
| 类型 | 长度 | 意义 | RFC | 
|---|---|---|---|
| 0 | - | End of Option List | RFC 793 | 
| 1 | - | No-Operation | RFC 793 | 
| 2 | 4 | Maximum Segment Size | RFC 793 | 
| 3 | 3 | WSOPT - Window Scale | RFC 7323 | 
| 4 | 2 | SACK Permitted | RFC 2018 | 
| 5 | N | SACK | RFC 2018 | 
| 8 | 10 | TSOPT - Time Stamp Option | RFC 7323 | 
| 27 | 8 | Quick-Start Response | RFC 4782 | 
| 28 | 4 | User Timeout Option | RFC 5482 | 
| 29 | - | TCP Authentication Option (TCP-AO) | RFC 5925 | 
| 30 | N | Multipath TCP (MPTCP) | RFC 6824 | 
| 34 | variable | TCP Fast Open Cookie | RFC 7413 | 
| 253 | N | RFC 3692-style Experiment 1 | RFC 4727 | 
| 254 | N | RFC 3692-style Experiment 2 | RFC 4727 | 
类型 0 用于定义所有可选项列表结尾,而不是单个可选项列表的结尾。
仅当选项结尾不是TCP报头结尾时使用(即 TCP 有选项字段也有数据字段)。
类型 1 用于选项之间,将后续可选项对齐到字边界,但是不一定保证发送端一定按边界发送,所以接收端需要实现。
类型 2 的 MSS 可选项用于建立连接时决定最大报文段长度的情况,该可选项用于大部分操作系统。
类型 3 的窗口扩大可选项是一个用来改善 TCP 吞吐量的可选项。
由于 TCP 首部中窗口字段只有 16 位,因此在 TCP 数据包的往返时间内(RTT)内,只能发送最大 64K 字节的数据。
如果采用该可选项,窗口的最大值可以扩展到 1G 字节,从而使得即使是在一个 RTT 较长的网络环境中,TCP 也能达到较高的吞吐量。
类型 4 和类型 5 用于选择确认应答(SACK:Selective Acknowledgment)。
TCP 的确认应答一般只有 1 个数,如果报文段总以"豁牙子状态"(有一个没一个的)到达,会严重影响网络性能。
有了这两个可选项,可以允许最多 4 次的"豁牙子"状态确认应答。
因此,这两个可选项在避免无用重发的同时,还能提高重发的速率,从而提高网络的吞吐量。
类型 8 时间戳字段可选项,用于高速通信中对序列号的管理。
若要将几 G 的数据通过高速网络转发,32 位序列号的值可能会迅速使用完。
在传输不稳定的网络环境中,可能会在较晚的时间点收到网络中一个较早序列号的数据包。
如果接收端对新老序列号产生混淆,就无法实现可靠传输。
为了避免这类问题的发生,引入了时间戳这个可选项,它可以区分新老序列号。
13 窗口大小与吞吐量
TCP 通信的最大吞吐量由窗口大小和往返时间决定。
假设最大吞吐量为 Tmax,窗口大小为 W,往返时间为 RTT,最大吞吐量的计算公式如下:
T m a x = W R T T T_{max}= \frac{W}{RTT} Tmax=RTTW
假设窗口大小为 65535 字节,RTT 为 0.1 秒,那么最大吞吐量 Tmax 为:
T m a x = 65535 ( 字节 ) 0.1 s = 65535 × 8 ( 比特 ) 0.1 s = 5242800 ( b i t / s ) = 5.2 ( M b i t / s ) T_{max}=\frac{65535(字节)}{0.1s}=\frac{65535 \times 8(比特)}{0.1s}=5242800(bit/s)=5.2(Mbit/s) Tmax=0.1s65535(字节)=0.1s65535×8(比特)=5242800(bit/s)=5.2(Mbit/s)
以上公式表示 1 个 TCP 连接所能传输的最大吞吐量为 5.2Mbit/s。
如果建立两个以上的连接同时进行传输,这个公式的计算结果则表示每个连接的最大吞吐量。
也就是说,在 TCP 中,与其使用一个连接传输数据,不如使用多个连接传输数据,从而达到更到的网络吞吐量。
在 Web 浏览器中,一般会通过同时建立 4 个左右的连接来提高吞吐量。