网络协议-TCP

TCP/IP的三次握手

流程

  1. 第一次握手:client端建立链接视乎。client端发送包(SYN=1,seq=x),这时候客户端进入SYN_SENT状态
  2. 第二次握手:server端收到SYN包,需要确认client端的seq(ack=x+1)。server端发送SYN+ACK包(SYN=1,ACK=1,seq=y,ack=x+1),这时候server端进入SYN_RECV状态
  3. 第三次握手:client端手server端SYN+ACK包,需要确认server端的seq(ack=y+1)。client端发送ACK包(ACK=1,ack=y+1),这时候client和server都是ESTABLISHED

至此完成链接,开始传输数据

目的

建立安全的双工的通信通道,保证双方都能发送、收到数据。

TCP/IP的四次挥手

四处挥手流程

  1. 第一次挥手:client端发出断开链接请求。client发送包(FIN=1,seq=x),client端状态为FIN_WAIT_1
  2. 第二次挥手:server端收到FIN包,需要确认client端的seq(ack=x+1)。server端发送ACK包(ACK=1,seq=y,ack=x+1) ,server端状态为CLOSE_WAIT,client端收到后状态FIN_WAIT_2
  3. 第三次挥手:server端不会立刻关闭链接,会继续发送完到client端的数据,server端发送FIN包(FIN=1,seq=z,ack=x+1),server端状态为LAST_ACK,client端状态为FIN_WAIT_2
  4. 第四次挥手:client端发送ACK包确认server端的FIN(ack=z+1),client端发送ACK包(ACK=1,ack=z+1,seq=h),server端状态为CLOSED,client端状态为TIME_WAIT状态。

至此4次挥手完成,客户端进入TIME_WAIT–2MSL–>CLOSED。
主要:如果client发送了FIN,服务端没有发送ACK直接发送了FIN,这时候客户端会变成CLOSEING。(比如FIN包由于网络原因丢失)

四次挥手目的

实现双工链接关闭的安全性。防止客户端的端口重用后,还收到上次服务端的请求

TIME_WAIT和CLOSE_WAIT状态

TIME_WAIT状态:对于复杂的网络状态,TCP 的实现提出了多种应对措施,TIME_WAIT 状态的提出就是为了应对其中一种异常状况。为了理解 TIME_WAIT 状态的必要性,我们先来假设没有这么一种状态会导致的问题。暂以 A、B 来代指 TCP 连接的两端,A 为主动关闭的一端。

  • 四次挥手中,A 发 FIN, B 响应 ACK,B 再发 FIN,A 响应 ACK 实现连接的关闭。而如果 A 响应的 ACK 包丢失,B 会以为 A 没有收到自己的关闭请求,然后会重试向 A 再发 FIN 包。如果没有 TIME_WAIT 状态,A 不再保存这个连接的信息,收到一个不存在的连接的包,A 会响应 RST 包,导致 B 端异常响应。此时, TIME_WAIT 是为了保证全双工的 TCP 连接正常终止。
  • 我们还知道,TCP 下的 IP 层协议是无法保证包传输的先后顺序的。如果双方挥手之后,一个网络四元组(src/dst ip/port)被回收,而此时网络中还有一个迟到的数据包没有被 B 接收,A 应用程序又立刻使用了同样的四元组再创建了一个新的连接后,这个迟到的数据包才到达 B,那么这个数据包就会让 B 以为是 A 刚发过来的。此时, TIME_WAIT 的存在是为了保证网络中迷失的数据包正常过期。

    由以上两个原因,TIME_WAIT 状态的存在是非常有意义的。

为什么客户端要等待2MSL才能关闭

协议规定主动关闭一方,进入FIN_WAIT_2->TIME_WAIT,必须等待2MSL(MSL为最大报文段生存时间,LWIP为1分钟,windows为2分钟)时间然后才进入CLOSED,删除TCP控制块。在2MSL等待时间内迟到的报文段将被抛弃。

  • 报文段有生存时间,当连接关闭时,有可能收到迟到的报文段。这时,若立马就建立新的连接(同一端口),那么新的连接就会接收迟到的报文,误以为是发给自己的。
  • 另一个原因是可靠的实现全双工连接的终止:由于网络原因可能服务端没有收到ACK包,所有会在一段时间内重新发送FIN包和ACK包。

不用等待2MSL的方法:使能SO_REUSEPORT(允许重用本地地址),可以通过调用setsockopt函数来使能。