039、TCP 七次握手

Par @Martin dans le
Tags :

首先补充下 TCP 报头的标志说明: SYNACKFINRSTPSH

七次握手指的是: 三次连接 + 四次断开

建立连接协议 (三次握手)

第一次握手: 建立连接时, 客户端发送 SYN 包 (syn = j) 到服务器, 并进入 SYN_SEND 状态, 等待服务器确认.

第二次握手: 服务器收到 SYN 包, 必须确认客户的 SYN, 即 ACK 包 (ack = j + 1), 同时自己也发送一个 SYN 包 (syn = k ), 即 SYN + ACK 包, 此时服务器 进入 SYN_RECV 状态.

第三次握手: 客户端收到服务器的 SYN + ACK包, 向服务器发送确认包 ACK (ack = k + 1), 此包发送完毕, 客户端和服务器进入 ESTABLISHED 状态, 完成三次握手

连接终止协议 (四次握手)

对于关闭流程, 一共有三种情况:

  • 客户端主动关闭
  • 服务器端主动关闭
  • 客户端和服务器端同时主动关闭

这里仅仅以客户端主动关闭为例列出下图:

由于 TCP 连接是全双工的, 因此每个方向都必须单独进行关闭, 这原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接, 收到一个 FIN 只意味着这一方向上没有数据流动, 一个 TCP 连接在收到一个 FIN 后仍能发送数据.
首先进行关闭的一方将执行主动关闭, 而另一方执行被动关闭.

第一次握手: TCP 客户端发送一个 FIN (fin = m), 用来关闭客户到服务器的数据传送.

第二次握手: 服务器收到这个 FIN, 它发回一个 ACK (ack = m + 1).

第三次握手: 服务器关闭客户端的连接, 发送一个 FIN (fin = n) 给客户端.

第四次握手: 客户段发回 ACK 报文确认 (ack = n + 1).

对于关闭流程, 服务器端和客户端是对等的地位, 其它两种场景处理过程类似, 需要注意的是, 由于对端是是可以主动关闭的, 因此在代码中需要加上被动关闭的响应流程.

半关闭

TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力, 这就是 TCP 的半关闭.

客户端发送 FIN, 另一端发送对这个 FINACK 报文, 当收到半关闭的一端在完成它的数据传送后, 才发送 FIN 关闭这个方向的连接, 客户端再对这个 FIN 确认, 这个连接才彻底关闭.

一般来说, 半连接只是一个暂停的过程, 但是, 在一些异常的情况的时候 (如远端主机故障) 的时候常常会造成半关闭连接, 由于 TCP 连接处于半打开或半关闭状态的时候, 仍然会占用相应的端口资源, 尤其对于 http 之类的海量服务来说, 会造成大量端口被占用, 会造成资源的浪费.

另外, 有的程序也针对 TCP 协议的这一特征来恶意进行网络攻击.

例如, 对于一个服务器, 大量的恶意客户端建立连接后, 既不发请求, 也不 close 套接字, 这种情况下服务器如何保护自己呢?

因为如果听之任之的话, 大量的恶意连接会耗尽服务器的可用描述符, 导致服务器不能服务, 就算服务器主动 close, 如果客户端不 close 的话, 那么这个连接还是不能完全释放, 对于服务器来说, 需要增加相应的机制进行半连接的处理.

Reference: 深入浅出 TCP 之半关闭与 CLOSE_WAIT