socket 关闭

pAVo4O0.png

  • 客户端主动发起连接,发送 FIN 包

  • 服务端接收到 FIN 包传递给应用程序

  • 服务端应用程序接收到 FIN 包后关闭连接(TCP 向对端发送 FIN)

  • 客户端接收到 FIN 包向对端发送确认包

  • TIME_WAIT 状态存在的两个原因

    • 防止第三次挥手的数据包丢失
    • 确保每次成功建立的连接不是旧连接的化身
  • 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 包数据会发送到对端

pAVoowT.png

  • 方式1:正常 write 后直接 close
    • 默认情况下 close 会直接返回,TCP 将尝试发送已排队等待发送到对端的数据,发送完后再发送 FIN 包
    • 该方式下客户端无法知道对端 TCP 是否接收该数据(正常情况下 TCP 的可靠性能保证对端接收到剩余数据,但对端主机崩溃的情况下会造成数据丢失)

pAVohyq.png

  • 方式2:设置套接字 SO_LINGER 选项(linger time > 0)
    • 当 close 超时时,情况和方式 1 一样
    • 当 close 成功返回,说明数据已被对端 TCP 确认,但是不能确保数据被对端应用程读取(如对端程序在从缓冲区读出数据前崩溃)

pAVoImV.png

  • 方式3:write 后 shutdown 写端,调用 read 等到对端的 FIN 才返回
    • 该方式下可以获知对端应用程序已读取我们的数据