我知道的 TCP
或者说,大家都知道的 TCP 知识
连接管理
一个 TCP 连接由一个 4 元组构成,分别是两个 IP 地址和两个端口号。
更准确地说,一个 TCP 连接是由一对端点或套接字构成的。我们将 IP 地址和端口的组合称为一个端点,或者套接字,然后可以用一对端点来唯一地描述每个连接。
TCP 连接的生命周期分为启动、数据传输和退出。我们使用三次握手来完成 TCP 连接的建立,建立后就可以在 TCP 连接上发送和接收数据,最后通常会使用四次挥手来关闭 TCP 连接。可以看到,一个 TCP 连接在正式传输数据之前需要先完成握手,在正常关闭时也需要交换控制报文,所以一些应用程序更愿意使用 UDP 协议来避免建立 TCP 连接的开销。
三次握手
三次握手,即客户端先向服务端发送 SYN 包,表示想要建立连接的意愿,并带上客户端 seq;服务端接收到后返回 SYN+ACK 包,一来表示接收到了建立连接的请求,二来给出服务端 seq,用于后续的通信;最后客户端接收到后再向服务端发送 ACK 包,连接就建立成功了。更详细的流程如下(客户端和服务端的具体状态语义见后文 “状态“ 一节):
- 第一次握手:客户端向服务端发送 SYN 包(
seq = x),请求建立连接。此时客户端从 CLOSED 状态切换为 SYN-SENT 状态。 - 第二次握手:服务端在接收到 SYN 包之后返回一个对应的 SYN+ACK 包(
ack = x + 1,而seq = y)。此时服务端从 LISTEN 状态切换为 SYN-RCVD 状态。 - 第三次握手:客户端收到 SYN+ACK 包之后,再返回一个 ACK 包(
ack = y + 1)。此时 TCP 的逻辑连接建立成功,客户端从 SYN-SENT 状态转换为 ESTABLISHED 状态。
这里的 x 和 y 分别是客户端和服务端的 ISN(Initial Sequence Number,起始序号)。ISN 并非从固定的值开始,而是会包含随机性。
使用随机数是有原因的。
其一,是避免重试导致的不同 TCP 连接的冲突。假设客户端和服务端在创建 TCP 连接,但因为网络波动导致客户端重试。如果序列号不同,则重试前传输的包和重试后传输的包的序列号完全不同,这就可以使重试前的包被安全丢弃(通过检查是否在接收窗口内)。而如果不随机的话,很可能导致错误的包被传输过去,又恰好落在接收窗口内而没有被丢弃,从而让上层看到错误的字节流。目前主流的 TCP 实现中,ISN 既会包含随机性,也会随时间变化,从而降低新旧连接之间发生序列号混淆的概率。当然也可以在应用层增加校验来避免这个问题(例如 CRC)。
其二,是一定程度上避免恶意攻击。一个不够随机的 ISN 可能会导致 TCP 会话劫持攻击,比如 Mitnick 攻击。早期的 ISN 生成方式更容易被预测,后来的实现会引入更强的随机性。TCP 本身比较脆弱:只要攻击者能伪造合适的序列号、IP 地址和端口,就可能伪装一个 TCP 包,从而打断 TCP 的正常连接。
三次握手除了用于建立连接之外,还可以用来交换选项。
四次挥手
四次挥手的详细流程如下:
- 第一次挥手:主动关闭者发送一个 FIN 包(
seq = k),请求关闭。此时主动关闭者从 ESTABLISHED 状态转换为 FIN-WAIT-1 状态。 - 第二次挥手:被动关闭者发送一个对应的 ACK 包(
ack = k + 1),表示接收到了这个 FIN 包。此时被动关闭者从 ESTABLISHED 状态转换为 CLOSE-WAIT 状态;主动关闭者接收到这个 ACK 包后从 FIN-WAIT-1 状态转换为 FIN-WAIT-2 状态。 - 第三次挥手:被动关闭者在准备好关闭时(即已无数据需要传输、准备断开连接时),发送一个 FIN 包(
seq = l)。此时它从 CLOSE-WAIT 转换为 LAST-ACK 状态,并等待最后的 ACK 包。 - 第四次挥手:主动关闭者返回一个 ACK 包(
ack = l + 1)。主动关闭者从 FIN-WAIT-2 状态转换为 TIME-WAIT 状态,等待一段时间(通常为 2 倍 MSL,以防被动关闭者未收到该 ACK 包),再转换为 CLOSED 状态并关闭 TCP 连接。而被动关闭者接收到 ACK 包后,从 LAST-ACK 状态转换为 CLOSED 状态,也关闭 TCP 连接。至此 TCP 连接断开。
注意,在第二次和第三次挥手之间,被动关闭者依然能够发送数据。TCP 连接是双向的,所以第一次和第二次挥手只会关闭数据的一个方向,此时 TCP 处于 “半关闭” 的状态。不过,很少有应用程序会利用这个功能。
同时打开和关闭
一种少见的情况是,TCP 连接的双方可能会同时打开,或者同时关闭连接。
对于同时打开的情况,双方会一开始就互相发送 SYN 包,之后又各自发送对应的 SYN+ACK 包。对于同时关闭的情况,双方都会各自发送一个 FIN 包,并各自发送对应的 ACK 包(和普通的四次挥手相比,只是两轮 FIN 包和对应的 ACK 包从顺序的变成交叉的了)。
链接和引用
- 《TCP/IP 详解 -- 卷 1:协议》,[美] Kevin R. Fall, W. Richard Stevens 著,吴英等译,机械工业出版社.
- Wikipedia / TCP: https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE