前言

这个系列的博客是针对阅读《网络是怎样连接的》([日] 户根勤, 译 [中] 周自恒, ISBN: 9787115441249)一书的记录与思路整理。
有些概念是做纯路由交换的网络工程师很少接触但又很重要的部分,因此整理到这里以做分享。

协议栈 - 套接字断开

TCP 连接断开顺序 - 四次挥手

无论是服务器或者是客户端在发送/接收完数据后都有可能主动发起连接关闭的请求(具体由谁发起,是根据更上层协议决定的;例如在HTTP 1.0 中,服务器在数据传输完成后主动发起连接关闭;而在HTTP 1.1 中,服务器在传输完数据后,客户端仍然可以继续请求,关闭连接的请求只能由客户端主动发起的)。连接关闭请求的本质是在TCP 包头中将控制位的FIN 比特设置为1。

以服务器优先发起断开连接为例

  1. 服务器一方的应用程序在完成数据传输后,调用Socket 库的close 程序。
  2. 服务器协议栈生成包含断开信息的TCP 头部,即将TCP头部的控制位的FIN 设置为1
  3. 协议栈委托IP模块向客户端发送数据。同时,服务器的套接字中会记录下断开操作的相关信息。
  4. 客户端收到服务器发来的控制位FIN为1 的TCP 数据包,这时客户端的协议栈会将自己的套接字标记为进入断开操作状态。
  5. 为了告知服务器,客户端已经收到FIN 包,客户端会向服务器返回一个ACK(控制位ACK 为1 )
  6. 这个操作完成后,协议栈就可以等待应用程序来取数据了。
  7. 应用程序会调用read 来读取数据。这时,协议栈不会向应用程序传递数据,而是告知应用程序来自服务器的数据已全部收到。
  8. 客户端应用程序收到消息后也调用close 来结束数据收发操作,这时客户端的协议栈也会和服务器一样,生成一个FIN 为1的数据包,然后委托IP 模块发送给服务器。
  9. 服务器收到客户端的FIN包后,返回一个ACK 报文,告知客户端已收到。至此,客户端和服务器的通信就全部结束了。

套接字的删除

和服务器的通信结束后,用来通信的套接字也就不会再使用了,这时系统可以删除套接字了。不过,套接字并不会立即被删除,而是会等待一段时间之后再被删除。

等待的这段时间是为了防止误操作,引发误操作的原因有很多。举一个例子为例。

假设,本次连接是客户端首先发起断开请求,那么断开顺序如下:

  1. 客户端发送FIN 包
  2. 服务器返回ACK
  3. 服务器发送FIN包
  4. 客户端返回ACK

此时,如果最后的客户端返回的ACK号丢失,那么在一定时间后客户端就必须重发一个FIN。那么这个时候套接字如果已经删除,且原套接字中的端口号(or 描述符?)已经分配给另一个套接字了。此时收到的FIN包会被分配给新的套接字,而新的套接字收到FIN包后就开始执行断开操作了。这就是为什么不马上删除套接字,就是为了防止类似的误操作。