TCP协议
缓慢持续更新ing
主要参考:wiki 和 小林coding,以及这篇博客
TCP 报文头部格式
一个 TCP 报文(TCP segment) 是 TCP 协议在网络中传输的基本单位。它有一个固定格式的头部(Header)和一个可变长度的数据部分(Payload)。

源端口号和目标端口号
Source Port
: 16位,源端口号,标识发送端口。Destination Port
: 16位,标识接收端口
序列号
Sequence Number
: 32位,序号(或者序列号),有双重作用:- 如果 SYN 标志位为1,那么这就是初始序列号。实际的第一个数据字节的序列号以及相应 ACK 中的确认号(acknowledged number,也叫ACK号)都等于此序列号加 1。
- 如果 SYN 标志位为 0,那么这个序列号就是当前会话中的这个 segment 中第一个数据的累计字节序号。这主要是因为在 TCP 连接已经建立之后,SYN 就不再使用了(为 0)。
数据偏移量
Data Offset
(DOffset): 4位,数据偏移量,用指定 TCP 头的size,以word(32-bit,即4字节)为单位。TCP 头的最小长度是 5 words(因为固定部分的 TCP 报文头正好是 5 个 word),最大是 15 words,因此最小是 20 字节,最大是 60 字节,报头最多可以容纳 40 字节的选项(options)字段。这个字段之所以叫“offset”,是因为它也表示从 TCP 段起始处 到 实际数据开始位置(Data) 的偏移量。
保留位
Reserved
(Rsrvd): 4 bits, 保留位,供未来使用,应当设置为 0 .在没有进一步的规范的情况下,发送方不应该设置这些位,接收方也应当忽略他们(就算被不慎设置了)。
状态位
Flags
: 8 bits, 8位状态位,包含以下 8 个标志位:CWR
(Congestion window reduced): 拥塞窗口已减少标志,由发送方设置,以表明它收到了设置了 ECE 标志的 TCP 段,并且已经根据拥塞控制机制作出了响应。它的作用是:发送方告诉接收方“我知道网络拥塞了,我已经减小了发送窗口”。有的图解会把CWR
归入保留位。ECE
(ECN-Echo): 1 bit, ECN 回显标志具有双重作用,其含义取决于 SYN 标志位:- 如果 SYN 标志位为1,表示该 TCP peer 支持 ECN 功能
- 如果 SYN 标志位为0,则在正常传输期间接收到了 IP
标头中设置了拥塞经历标志(
ECN=11
)的数据包。这告诉 TCP 发送方网络已经发生或即将发生拥塞。有的图解会把ECE
归入保留位。
URG
, 1 bit, 表示 “紧急指针(Urgent Pointer)”字段是有效的。当 URG 标志位置为 1 时,说明当前这个 TCP 报文中有“紧急数据”;此时,紧急指针字段告诉接收方:紧急数据在数据段中结束的位置。通常用于 telnet 等老旧协议,现在很少用。ACK
: 1 bit, 指示确认字段有效。客户端发送初始 SYN 数据包之后的所有数据包都应设置此标志。即,只有客户端请求连接的第一次握手时不用设置ACK标志位,毕竟在此之前客户端还没有收到过信息。PSH
: 1 bit, 表示“推送功能(Push Function)”,请求将缓冲数据立即推送到接收应用程序。RST
: 1 bit, 重新连接。SYN
: 1 bit, 同步序列号。只有在前两次握手的过程应设置此标志。一些其他的标志位和字段根据 SYN 改变含义,有的只有在SYN为1时有效,有的只有在SYN清空有效。FIN
: 1 bit, 表示发送方的最后一个数据包,用于 TCP 连接的正常关闭。当一个主机发送带有 FIN=1 的 TCP 报文时,它表示主机请求断开连接,然后回进入终止连接的四次挥手进程。
窗口
- 16位,接收窗口的大小,表示本报文段的发送方目前愿意接收的窗口大小单位数。(详见“流量控制”和“窗口缩放”部分)。
校验和
Checksum
: 16 bits, 16 位校验和字段用于对 TCP
报头、有效载荷(payload)和 IP
伪报头(pseudo-header)进行错误校验。伪报头由源 IP 地址、目标 IP 地址、TCP
协议号 (6) 以及 TCP
报头和有效载荷的长度(以字节为单位)组成。具体见[1]
payload data: 有效载荷数据,在计算机网络中,指传输的数据中除去协议头和校验码等控制信息之外的实际数据部分。
紧急指针
Urgent Pointer
: 16 bits, 如果设置了 URG 标志,则此 16
位字段是相对于指示最后一个紧急数据字节的序列号的偏移量。现代协议几乎不使用URG和紧急指针。
选项
选项(TCP Option
):长度可变,0–320 位(即 0–40
字节),单位是 32 位(4 字节); 其大小由 TCP 报文头的 Data Offset
字段决定,计算公式是: 1
Options 字段大小 = (Data Offset - 5) × 32 位
如果这个字段存在,数据偏移量将大于 5 words。因为 Data Offset 表示的是以 word(32 位)为单位的 TCP 头部长度,所以必须通过在末尾添加填充 0,将 TCP 头部长度填充为 4 字节的整数倍。
三次握手
在 HTTP 传输数据之前,首先需要 TCP 建立连接,TCP 连接的建立,通常称为三次握手。
先弄清楚几个概念:
- 序列号(Sequence Number):是发送方为数据流中的字节指定的“标记”,表示它正在发送哪些字节。它告诉接收方“我准备从这个序列号开始给你发数据”。
- 确认号(Acknowledgment Number):是接收方告诉发送方它已经成功接收到的字节的下一个序列号,表示接收方已经收到了所有小于该确认号的字节。它告诉发送方“接下来我准备接收从这个序列号开始的数据”。
在客户端尝试连接服务器之前,服务器必须先绑定并监听某个端口,以便开放连接:这称为被动开启(passive open)。被动打开建立后,客户端可以通过三次握手(three-way/3-step handshake)发起主动开启(active open)来建立连接。
三次握手的步骤如下:
- SYN:客户端向服务器发送 SYN 报文(SYN 标志位设置为1)主动开启连接。客户端把TCP段的序列号设置为一个随机数 A。因为客户端还没有受到过消息,所以这里不用设置确认号,也不包含任何应用层的数据。
- SYN-ACK:作为回复,服务器响应了一个SYN-ACK报文。该TCP段的确认号被设置为
A+1,而序列号被服务器设置为另一个随机数
B。到这里客户端已经完成了一发一收,所以处于
ESTABLISHED
状态。 - ACK:最后,客户端把 ACK
发送给服务器,这时的序列号设置为接收到的确认号,即A+1,而确认号被设置成接收到的序列号加一,即B+1。这次报文可以携带客户端到服务器的数据。至此服务器也完成了一发一收,也处于
ESTABLISHED
状态。
步骤1和2从客户端到服务器的方向建立和确认序列号,步骤2和步骤3从服务器到客户端的方向建立和确认序列号。这些步骤完成之后,客户端和服务器都完成了确认,全双工通信正式建立。
可见,三次握手目的是保证双方都有发送和接收的能力。
分割数据
TCP 传输的数据归根结底还是来源于应用程序产生的HTTP请求消息。但是,TCP每个段能传输的数据长度是有最大限制的:
- MTU:一个网络包(IP头+TCP头+数据)的最大长度,以太网中一般为1500字节。
- MSS:除去IP头和TCP头之后,一个网络包所能容纳的TCP数据的最大长度。
如果数据的长度超过了MSS
,则会将数据以MSS
的长度为单位进行切分,每一块都会被放进单独的网络包中,加上TCP头部后交给IP模块发送出去。
在浏览器中输入网址并回车后,浏览器会向服务器发起 HTTP
请求,这是一段纯文本数据,浏览器将这段数据交给操作系统的协议栈,操作系统通过上文讲述的
TCP
协议建立连接、发送。如果数据的长度超过了MSS
,则会将数据以MSS
的长度为单位进行切分,每一块都会被放进单独的网络包中,加上TCP头部后交给IP模块发送出去。远程服务器接收到
TCP
报文后,TCP协议层按照序列号重新组装数据,然后把完整的HTTP请求交给HTTP服务器程序,最后由服务器解析并做出响应,例如返回请求的网页内容。这就是在浏览器输入网址后发生的事情。
参考链接
- https://en.wikipedia.org/wiki/Internet_checksum ↩︎