TCP 三次握手与四次挥手

三次握手

TCP 三次握手与四次挥手_第1张图片
三次握手
  • 第一次握手:起初两端都处于 CLOSED 关闭状态,Client 将标志位 SYN 置为 1,随机产生一个值 seq=x,并将该数据包发送给 Server,Client 进入 SYN-SENT 状态,等待 Server 确认;
  • 第二次握手:Server 收到数据包后由标志位 SYN=1 得知 Client 请求建立连接,Server 将标志位 SYN 和 ACK 都置为 1,ack=x+1,随机产生一个值 seq=y,并将该数据包发送给 Client 以确认连接请求,Server 进入 SYN-RCVD 状态,此时操作系统为该 TCP 连接分配 TCP 缓存和变量;
  • 第三次握手:Client 收到确认后,检查 ack 是否为 x+1,ACK 是否为 1,如果正确则将标志位 ACK 置为 1,ack=y+1,并且此时操作系统为该 TCP 连接分配 TCP 缓存和变量,并将该数据包发送给 Server,Server 检查 ack 是否为 y+1,ACK 是否为 1,如果正确则连接建立成功,Client 和 Server 进入 ESTABLISHED 状态,完成三次握手,随后 Client 和 Server 就可以开始传输数据
为什么 A 还要发送一次确认呢?可以二次握手吗?

主要为了防止已失效的连接请求报文段突然又传送到了 B,因而产生错误。如 A 发出连接请求,但因连接请求报文丢失而未收到确认,于是 A 再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,A 共发出了两个连接请求报文段,其中第一个丢失,第二个到达了 B,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达 B,此时 B 误认为A 又发出一次新的连接请求,于是就向 A 发出确认报文段,同意建立连接,不采用三次握手,只要 B 发出确认,就建立新的连接了,此时 A 不理睬 B 的确认且不发送数据,则 B 一直等待 A 发送数据,浪费资源

B 无法知道 A 是否已经接收到自己的同步信号,如果这个同步信号丢失了,A 和 B 就 B 的初始序列号将无法达成一致

Server 端易受到 SYN 攻击?

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到 SYN 洪泛攻击,SYN 攻击就是 Client 在短时间内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN 包,Server 则回复确认包,并等待 Client 确认,由于源地址不存在,因此 Server 需要不断重发直至超时,这些伪造的 SYN 包将长时间占用未连接队列,导致正常的 SYN 请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪

防范 SYN 攻击措施:降低主机的等待时间使主机尽快的释放半连接的占用,短时间受到某 IP 的重复 SYN 则丢弃后续请求

数据传输

在建立连接后,TCP 将以全双工方式传输数据,在同一时间主机 A 与主机 B 之间可以同时进行 TCP 报文段传输,并对接收到的 TCP 报文进行确认。具体过程如下:

TCP 三次握手与四次挥手_第2张图片
  • 假设主机 A 向主机 B 发送 1800 字节的数据,主机 B 向主机 A 发送 1000 字节的数据
  • 主机 A 取 seq=8001 作为第一个字节的编号,由于数据长度是 1800,字节编号就是 8001-9801。同理主机 B 编号是 18001-19000
  • 当对字节编号后,TCP 就给每个报文分配一个序号,该序号即这个报文中的第一个字节的编号,在图中主机 A 数据被拆分两个报文段(主机 A 限定发送有效值 1000 字节,所以会分两段发送一个 1000 字节,一个 800 字节),因此第一段报文序号是 seq=8001,第二段报文序号 seq=9001。同理主机 B 一段报文发送,序号是 seq=18001
  • 接收端接收到报文需要进行确认,TCP 确认号被定义下一个希望接收到的字节的编号,所以当主机 B 成功接收到主机 A 发送的第二段报文时,发现报文的字节编号 9001-9800,所以主机 B 发送给主机 A 确认序号 ack=9801。同理主机 A 接收到主机 B 发送的报文字节编号是 18001-19000,会给主机 B 发送确认报文,确认序号 ack=19001
  • 报文传输完成,这里主机 A 最后一次只发送一个 ack,代表主机 A 已经没有数据发送给主机 B 了。为了提高 TCP 传输数据效率,接收端主机不会对发送端主机发送的每一段报文都进行报文确认,而是当同时接收到多个报文后再发送确认报文 ack

四次挥手

TCP 三次握手与四次挥手_第3张图片
四次挥手
  • A 的应用进程先向其 TCP 发出连接释放报文段(FIN=1,序号 seq=u),并停止再发送数据,主动关闭 TCP 连接,进入 FIN-WAIT-1(终止等待 1)状态,等待 B 的确认
  • B 收到连接释放报文段后即发出确认报文段(ACK=1,确认号 ack=u+1,序号 seq=v),B 进入 CLOSE-WAIT(关闭等待)状态,此时的 TCP 处于半关闭状态,A 到 B 的连接释放
  • A 收到 B 的确认后,进入 FIN-WAIT-2(终止等待 2)状态,等待 B 发出的连接释放报文段
  • B 没有要向 A 发出的数据,B 发出连接释放报文段(FIN=1,ACK=1,序号 seq=w,确认号 ack=u+1),B 进入 LAST-ACK(最后确认)状态,等待 A 的确认。
  • A 收到 B 的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),A 进入 TIME-WAIT(时间等待)状态。此时 TCP 未释放掉,需要经过时间等待计时器设置的时间 2MSL 后,A 才进入 CLOSED 状态

总结四次挥手过程:

  • 起初 A 和 B 处于 ESTABLISHED 状态
  • A 发出连接释放报文段并处于 FIN-WAIT-1 状态
  • B 发出确认报文段且进入 CLOSE-WAIT 状态
  • A 收到确认后,进入 FIN-WAIT-2 状态,等待B的连接释放报文段
  • B 没有要向 A 发出的数据时,发出连接释放报文段且进入 LAST-ACK 状态
  • A 发出确认报文段且进入 TIME-WAIT 状态
  • B 收到确认报文段后进入 CLOSED 状态
  • A 经过等待计时器时间 2MSL 后,进入 CLOSED 状态
为什么连接的时候是三次握手,关闭的时候却是四次握手?

因为当 Server 端收到 Client 端的 SYN 连接请求报文后,可以直接发送 SYN+ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。但是关闭连接时,当Server 端收到 FIN 报文时,很可能并不会立即关闭 SOCKET,所以只能先回复一个ACK 报文,告诉 Client 端,"你发的 FIN 报文我收到了"。只有等到我 Server 端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手

为什么 TIME_WAIT 状态需要经过 2MSL(最大报文段生存时间)才能返回到 CLOSE 状态?

网络是不可靠的,有可以最后一个 ACK 丢失。所以 TIME_WAIT 状态就是用来重发可能丢失的 ACK 报文。在 Client 发送出最后的 ACK 回复,但该 ACK 可能丢失。Server 如果没有收到 ACK,将不断重复发送 FIN 片段。所以 Client 不能立即关闭,它必须确认 Server 接收到了该 ACK。Client 会在发送出 ACK 之后进入到 TIME_WAIT 状态。Client 会设置一个计时器,等待 2MSL 的时间。如果在该时间内再次收到 FIN,那么 Client 会重发 ACK 并再次等待 2MSL。所谓的 2MSL 是两倍的 MSL(Maximum Segment Lifetime)。MSL 指一个片段在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间。如果直到 2MSL,Client 都没有再次收到 FIN,那么 Client 推断 ACK 已经被成功接收,则结束 TCP 连接

为什么是 2MSL?

2MSL 就是一个发送和一个回复所需的最大时间。如果直到 2MSL,A 都没有再次收到 FIN,那么 A 推断 ACK 已经被成功接收,则结束 TCP 连接
A 在发送完 ACK 报文段后,再经过 2MSL 时间,就可以使本连接持续的时间所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求的报文段

你可能感兴趣的:(TCP 三次握手与四次挥手)