本文首发于公众号:腐烂的橘子
第 1 步 - 初始连接请求 SYN(Synchronize)
第 2 步 - 服务端回复 SYN—ACK(Synchronize-Acknowledge)
第 3 步 - 客户端回复 ACK(Acknowledge)
简单了解 TCP 连接之后,我们不免想知道这些标志位是什么,TCP 报文段结构就是使用 TCP 发送数据时报文段时,这个报文段具体的样子。结构如下[1]:
图片来源于:https://www.geeksforgeeks.org/services-and-segment-structure-in-tcp/
TCP 报文首部(header)大小是 20-60 字节(bytes)。40 字节是可选的,所以典型的 TCP 报文首部大小为 20 字节。
这里需要说明一个基本知识:1字节 = 8比特,即 1bytes = 8bits。所以图中上面浅绿色部分共 20 字节,160 比特;下面深绿色部分共 40 字节,320比特。
以上一共 160 比特,共 20 字节,加上可选的 40 字节,所以一个 TCP 报文的大小处于 20~60 字节。
第三次握手是客户端发送确认消息给服务端,主要原因是为了通知服务端,服务端发送的初始序列号已被客户端确认。
因为一共有三个阶段,如果每个阶段都发生丢包,有以下三种情况:
TCP 在丢包后都会触发重传,重传有超时重传、快速重传等很多类型,我们会在后面的文章中详细描述。这里触发的重传,都是使用最基本的“超时重传”的机制。所以上面的三种情况都会触发超时重传,区别只是客户端还是服务端来重传、和重传什么内容。
第一次握手是客户端将 SYN 包发送到服务端,客户端状态变为 SYN_SEND。如果此阶段丢包,服务端没有收到来自客户端的 SYN 包,一直处于 LISTEN 状态。客户端这边则会触发超时重传机制,在发送 SYN 包的 x 秒后,重新发送一次 SYN 包。
超时时间 x 的值是多少?
其中,x 是超时时间,这个时间是由操作系统内核代码 /include/net/tcp.h
中的 TCP_TIMEOUT_INIT
字段定义的[2],例如 linux v4.14.12 中的定义如下:
#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ)) /* RFC6298 2.1 initial RTO value */
这里的超时时间是 1*HZ
,取决于 TCP/IP 规范文档 RFC6298 2.1 中的初始 RTO (RTO,Retransmission Time Out,超时重传时间)时间,RFC6298 2.1 中的描述如下[3]:
“Note that the previous version of this document used an initial RTO of 3 seconds [PA00]. ”这句话表明 RTO
的初始值是 3 秒,所以超时时间是 3 秒,在发送 SYN 包的 3 秒后,会重新发送一次 SYN 包。
我们知道了超时时间是 3 秒后会再次发送 SYN 包,假如这个包再次超时,时间也是 3 秒吗?一共要重传多少次呢?
我们先来看重传次数,重传次数可以在 linus 系统中使用命令查看:
sysctl net.ipv4
输入命令后会展示很多系统配置,从中我们可以看到两个关于 SYN 的配置:
net.ipv4.tcp_syn_retries = 6
net.ipv4.tcp_synack_retries = 5
其中 net.ipv4.tcp_syn_retries = 6
就是我们想要的超时重传次数,这时超时重传 6 次后会停止重试,重试 6 次后如果还是没有收到服务端发送的 ACK,客户端就会停止请求连接。
假如这个包再次超时,时间也是 3 秒吗?这个时间是不固定的,因为这个超时时间是基于 RTO 来计算的,RTO 的计算方式在 RFC6298 文档(或其他版本文档)中有定义,可以自行研究一下。总体来说,RTO 的时间会逐渐变长。
第二次握手时,服务端会返回 ACK+SYN 报文。如果报文丢失,意味着客户端的请求没有被确认,且服务端的序列号也没有发送到客户端。因此会有如下结果:
在前面我们知道 net.ipv4.tcp_syn_retries = 6
代表 SYN 会重传 6 次,即第一次握手会重试 6 次,net.ipv4.tcp_synack_retries = 5
就代表第二次握手的 SYN+ACK
报文会重试 5 次,即第二次握手会重试 5 次。
第三次握手是客户端发送 ACK 报文给服务端,目的是让服务端知道,客户端已经确认了服务端的初始序列号。如果第三次握手失败,则服务端收不到确认,所以还是会重传 ACK + SYN 报文,根据 net.ipv4.tcp_synack_retries = 5
,仍然会重试 5 次。
知其然还要知其所以然,无论第几次握手失败,本质上还是利用了超时重传的机制。如果不了解 SYN、ACK 和序列号的关系,那就说明不了解 TCP 报文段的格式。其实重传机制、RTO、RTT 等等这些概念都需要我们一步步了解之后才能更深入理解三次握手,以及网络中的其他流程机制。