tcp头部

Catalogue
  1. 1. 传输层 tcp 协议 实现
    1. 1.1. 首部协议格式
      1. 1.1.1. 源端口和目的端口
      2. 1.1.2. 序号
      3. 1.1.3. 确认号
      4. 1.1.4. 数据偏移
      5. 1.1.5. 保留
      6. 1.1.6. 控制报文标志
        1. 1.1.6.1. 紧急URG(URGent)
        2. 1.1.6.2. 确认ACK(ACKnowledgment)
        3. 1.1.6.3. 推送 PSH(PuSH)
        4. 1.1.6.4. 同步SYN(SYNchronization)
        5. 1.1.6.5. 终止FIN(FINis,意思是“完”“终”)
      7. 1.1.7. 窗口
      8. 1.1.8. 检验和
      9. 1.1.9. 紧急指针
      10. 1.1.10. 选项
        1. 1.1.10.1. 选项的一般结构体
    2. 1.2. 多种状态的标志
      1. 1.2.1. 发起连接时的报文状态
      2. 1.2.2. 终端连接的报文状态

[toc]

传输层 tcp 协议 实现

image

首部协议格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

源端口和目的端口

各占 2 个字节,分别 tcp 连接的源端口和目的端口。关于端口的概念之前已经介绍过了。

序号

占 4 字节,序号范围是[0,2^32 - 1],共 2^32(即 4294967296)个序号。序号增加到 2^32-1 后,下一个序号就又回到 0。TCP 是面向字节流的,在一个 TCP 连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号(ISN)必须在连接建立时设置。首部中的序号字段值则是指的是本报文段所发送的数据的第一个字节的序号。例如,一报文段的序号是 301,而接待的数据共有 100 字节。这就表明:本报文段的数据的第一个字节的序号是 301,最后一个字节的序号是 400。显然,下一个报文段(如果还有的话)的数据序号应当从 401 开始,即下一个报文段的序号字段值应为 401。

确认号

占 4 字节,是期望收到对方下一个报文段的第一个数据字节的序号。例如,B 正确收到了 A 发送过来的一个报文段,其序号字段值是 501,而数据长度是 200 字节(序号 501~700),这表明 B 正确收到了 A 发送的到序号 700 为止的数据。因此,B 期望收到 A 的下一个数据序号是 701,于是 B 在发送给 A 的确认报文段中把确认号置为 701。注意,现在确认号不是 501,也不是 700,而是 701。
总之:若确认号为 N,则表明:到序号 N-1 为止的所有数据都已正确收到。TCP 除了第一个 SYN 报文之外,所有 TCP 报文都需要携带 ACK 状态位。

数据偏移

占 4 位,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。这个字段实际上是指出 TCP 报文段的首部长度。由于首部中还有长度不确定的选项字段,因此数据偏移字段是必要的,但应注意,“数据偏移”的单位是 4 个字节,由于 4 位二进制数能表示的最大十进制数字是 15,因此数据偏移的最大值是 60 字节。

保留

占 6 位,保留为今后使用,但目前应置为 0。

控制报文标志

紧急URG(URGent)

当 URG=1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快发送(相当于高优先级的数据),而不要按原来的排队顺序来传送。例如,已经发送了很长的一个程序要在远地的主机上运行。但后来发现了一些问题,需要取消该程序的运行,因此用户从键盘发出中断命令。如果不使用紧急数据,那么这两个字符将存储在接收 TCP 的缓存末尾。只有在所有的数据被处理完毕后这两个字符才被交付接收方的应用进程。这样做就浪费了很多时间。
当 URG 置为 1 时,发送应用进程就告诉发送方的 TCP 有紧急数据要传送。于是发送方 TCP 就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍然是普通数据。这时要与首部中紧急指针(Urgent Pointer)字段配合使用。

确认ACK(ACKnowledgment)

仅当 ACK=1 时确认号字段才有效,当 ACK=0 时确认号无效。TCP 规定,在连接建立后所有的传送的报文段都必须把 ACK 置为 1。

推送 PSH(PuSH)

当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP 就可以使用推送(push)操作。这时,发送方 TCP 把 PSH 置为 1,并立即创建一个报文段发送出去。接收方 TCP 收到 PSH=1 的报文段,就尽快地交付接收应用进程。
复位RST(ReSeT)
当 RST=1 时,表名 TCP 连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。RST 置为 1 用来拒绝一个非法的报文段或拒绝打开一个连接。

同步SYN(SYNchronization)

在连接建立时用来同步序号。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK=1,因此 SYN 置为 1 就表示这是一个连接请求或连接接受报文。

终止FIN(FINis,意思是“完”“终”)

用来释放一个连接。当 FIN=1 时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接。

窗口

占 2 字节,窗口值是[0,2^16-1]之间的整数。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据,作为流量控制的依据,后面会详细介绍。
总之:窗口字段明确指出了现在允许对方发送的数据量。窗口值经常在动态变化。

检验和

占 2 字节,检验和字段检验的范围包括首部和数据这两部分。和 UDP 用户数据报一样,在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。伪首部的格式和 UDP 用户数据报的伪首部一样。但应把伪首部第 4 个字段中的 17 改为 6(TCP 的协议号是 6);把第 5 字段中的 UDP 中的长度改为 TCP 长度。接收方收到此报文段后,仍要加上这个伪首部来计算检验和。若使用 IPv6,则相应的伪首部也要改变。

紧急指针

占 2 字节,紧急指针仅在 URG=1 时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据) 。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP 就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为 0 时也可以发送紧急数据。

选项

选项长度可变,最长可达 40 字节。当没有使用“选项”时,TCP 的首部长度是 20 字节。TCP 首部总长度由 TCP 头中的“数据偏移”字段决定,前面说了,最长偏移为 60 字节。那么“tcp 选项”的长度最大为 60-20=40 字节。

选项的一般结构体

1
2
3
4
  1byte    1byte        nbytes
+--------+--------+------------------+
| Kind | Length | Info |
+--------+--------+------------------+

TCP 最初只规定了一种选项,即最大报文段长度 MSS(Maximum Segment Szie)。后来又增加了几个选项如窗口扩大选项、时间戳选项等,下面说明常用的选项。

kind=0 是选项表结束选项。

kind=1 是空操作(nop)选项
没有特殊含义,一般用于将 TCP 选项的总长度填充为 4 字节的整数倍,为啥需要 4 字节整数倍?因为前面讲了数据偏移字段的单位是 4 个字节。

kind=2 是最大报文段长度选项
TCP 连接初始化时,通信双方使用该选项来协商最大报文段长度(Max Segment Size,MSS)。TCP 模块通常将 MSS 设置为(MTU-40)字节(减掉的这 40 字节包括 20 字节的 TCP 头部和 20 字节的 IP 头部)。这样携带 TCP 报文段的 IP 数据报的长度就不会超过 MTU(假设 TCP 头部和 IP 头部都不包含选项字段,并且这也是一般情况),从而避免本机发生 IP 分片。对以太网而言,MSS 值是 1460(1500-40)字节。

kind=3 是窗口扩大因子选项
TCP 连接初始化时,通信双方使用该选项来协商接收通告窗口的扩大因子。在 TCP 的头部中,接收通告窗口大小是用 16 位表示的,故最大为 65535 字节,但实际上 TCP 模块允许的接收通告窗口大小远不止这个数(为了提高 TCP 通信的吞吐量)。窗口扩大因子解决了这个问题。假设 TCP 头部中的接收通告窗口大小是 N,窗口扩大因子(移位数)是 M,那么 TCP 报文段的实际接收通告窗口大小是 N 乘 2M,或者说 N 左移 M 位。注意,M 的取值范围是 0~14。

和 MSS 选项一样,窗口扩大因子选项只能出现在同步报文段中,否则将被忽略。但同步报文段本身不执行窗口扩大操作,即同步报文段头部的接收通告窗口大小就是该 TCP 报文段的实际接收通告窗口大小。当连接建立好之后,每个数据传输方向的窗口扩大因子就固定不变了。关于窗口扩大因子选项的细节,可参考标准文档 RFC 1323。

kind=4 是选择性确认(Selective Acknowledgment,SACK)选项
TCP 通信时,如果某个 TCP 报文段丢失,则 TCP 模块会重传最后被确认的 TCP 报文段后续的所有报文段,这样原先已经正确传输的 TCP 报文段也可能重复发送,从而降低了 TCP 性能。SACK 技术正是为改善这种情况而产生的,它使 TCP 模块只重新发送丢失的 TCP 报文段,不用发送所有未被确认的 TCP 报文段。选择性确认选项用在连接初始化时,表示是否支持 SACK 技术。

kind=5 是 SACK 实际工作的选项
该选项的参数告诉发送方本端已经收到并缓存的不连续的数据块,从而让发送端可以据此检查并重发丢失的数据块。每个块边沿(edge of block)参数包含一个 4 字节的序号。其中块左边沿表示不连续块的第一个数据的序号,而块右边沿则表示不连续块的最后一个数据的序号的下一个序号。这样一对参数(块左边沿和块右边沿)之间的数据是没有收到的。因为一个块信息占用 8 字节,所以 TCP 头部选项中实际上最多可以包含 4 个这样的不连续数据块(考虑选项类型和长度占用的 2 字节)。

kind=8 是时间戳选项
该选项提供了较为准确的计算通信双方之间的回路时间(Round Trip Time,RTT)的方法,从而为 TCP 流量控制提供重要信息。

多种状态的标志

发起连接时的报文状态

在连接建立时用来同步序号。当 SYN=1ACK=0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN=1ACK=1,因此 SYN 置为 1 就表示这是一个连接请求或连接接受报文。

终端连接的报文状态

用来释放一个连接。当 FIN=1 时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接。