socket 关闭
四次挥手
-
客户端主动发起连接,发送 FIN 包
-
服务端接收到 FIN 包传递给应用程序
-
服务端应用程序接收到 FIN 包后关闭连接(TCP 向对端发送 FIN)
-
客户端接收到 FIN 包向对端发送确认包
-
TIME_WAIT 状态存在的两个原因
- 防止第三次挥手的数据包丢失
- 确保每次成功建立的连接不是旧连接的化身
close() 和 shutdown()
-
close() 函数:
- 调用 close 只是导致相应的描述符引用计数减一,套接字真正的清理和资源释放发生需要其引用计数到达 0 时才发生(计数为 0 时才会发送 FIN 包)
-
SO_LINGER 套接字选项
- 默认情况下为关闭选项(即 l_onoff 为 0)
- 选项开启的情况(l_onoff = 1):
- l_linger = 0 时,调用 close 函数 TCP 将丢弃套接字发送缓冲区的所有数据并向对端发送 RST 包,这种情况下避免了 TCP 的 TIME_WAIT 状态
- l_linger >0 :
- 阻塞套接字调用 close:进程会睡眠直到所有数据被对端确认(收到 ack 包)或超时
- 非阻塞套接字调用 close:调用马上返回,返回值为 EWOULDBLOCK 表示数据被对端确认前超时,此时套接字发送缓冲区的残留数据都会被丢弃
-
shutdown() 函数:
- SHUT_RD:套接字接受缓冲区的数据及往后接收到的任何数据都会被丢弃,进程不能对该套接字进行读操作,对套接字发送缓冲区没任何影响(不会发送 FIN 包)
- SHUT_WR:套接字可以继续接受数据,发送缓冲区的数据和 FIN 包数据会发送到对端
如何关闭连接
- 方式1:正常 write 后直接 close
- 默认情况下 close 会直接返回,TCP 将尝试发送已排队等待发送到对端的数据,发送完后再发送 FIN 包
- 该方式下客户端无法知道对端 TCP 是否接收该数据(正常情况下 TCP 的可靠性能保证对端接收到剩余数据,但对端主机崩溃的情况下会造成数据丢失)
- 方式2:设置套接字 SO_LINGER 选项(linger time > 0)
- 当 close 超时时,情况和方式 1 一样
- 当 close 成功返回,说明数据已被对端 TCP 确认,但是不能确保数据被对端应用程读取(如对端程序在从缓冲区读出数据前崩溃)
- 方式3:write 后 shutdown 写端,调用 read 等到对端的 FIN 才返回
- 该方式下可以获知对端应用程序已读取我们的数据