TCP 用三个分节建立一个连接,终止一个连接则需要四个分节。
1. 某个应用进程首先调用 close,我们称这一端为执行主动关闭的一端,这一端TCP 发送一个FIN分节 FIN K,标是数据发送完毕。
2. 接收 到FIN的另一端执行被动关闭,这个FIN 由 tcp 确认 ack k + 1,他的接收也作为文件结束符传输给接收方应用进程,因为FIN的接收意味着应用进程在相应连接上再也接收不到额外数据。
3. 一段时间后,接收到文件结束符的应用进程将调用close关闭他的套接口,这导致它的tcp 也发送一个FIN, FIN J.
4. 接收到这个FIN的原发送方 TCP对他进程确认 ack j+1.
因为每个方向都要有一个FIN 和一个ACK, 所以一般需要四个分节,我们用限定词一般是因为有时步骤1的FIN 随数据一起发送,另外,执行被动关闭的那一端tcp在步骤2和3发出的ACK与FIN也可以合并成一个分节。
FIN占据一个字节的序列号空间,这与SYN相同,所以每个FIN的ACK是这个FIN的序列号加1.
在步骤2和3之间可以有从执行被动关闭端到执行主动关闭端的数据流,这称为半关闭(half-close),套接口关闭时每一端都要发送一个FIN,这种情况在应用进程调用close时会发生,但在进程终止时所有打开的描述符句柄将自动关闭,此时仍然打开的tcp链接也会发出一个FIN.
在连接关闭前,两端进程的tcp都处于ESTABLISHED状态,如果这时进程主动调用close(主动发起端),则发出FIN 分节,自身转换到FIN_WAIT_!状态,接收到FIN分节的对端则从ESTABLISH状态转换到 CLOSE_WAIT状态并发出ACK 给发出FIN端确认,然后从CLOSE_WAIT状态转换到 LAST_ACK,执行主动关闭的一端在收到对端的ACK时从FIN_WAIT_1 转换到FIN_WAIT_2等待对端发FIN分节,收到FIN分节后则从FIN_WAIT_2状态转换到TIME_WAIT状态并发送FIN 的ACK给对端,执行被动关闭的一端在收到ACK后从LAST_ACK状态转换到CLOSED状态,我们发现,主动发起关闭的一端进入TIME_WAIT状态,此状态是关于TCP网络编程中最难理解的部分,下一篇继续分析此部分。
不论是建立tcp 链接还是终止连接,都是在各个状态间的转换,一个tcp链接有11种状态,并且规定了如何基于当前状态及在该状态下所接收的分节从一个状态转换到另一个状态,比如当进程在 closed状态下执行主动打开时(connect),tcp将发送一个SYN分节,并从closed状态转换到 SYN_SEND状态,如果该tcp 接收到一个带有ACK 的SYN,他将发送一个ACK并转换成ESTABLISHED状态,这个状态是数据传送状态。tcp协议栈在实现过程中是用一个有限状态机(FSM)来管理这些状态的流转。
一个工具,查看系统中处于各个状态的个数:
#netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c