实验:

  1. Socket socket = new Socket();
  2. InetSocketAddress serverSocketAddress = new InetSocketAddress("10.0.0.3", 8080);
  3. socket.connect(serverSocketAddress);
  4. byte[] msg = getMessageBytes();
  5. socket.getOutputStream().write(msg);
  6. socket.close();

图示:

image.png

当我们调用 write 函数向内核写入一段数据时,内核会把这段数据放入一个缓冲区 buffer,如下图所示:

image.png

关闭连接的两种方式

  • FIN:优雅关闭,发送 FIN 包表示自己这端所有的数据都已经发送出去了,后面不会再发送数据
  • RST:强制连接重置关闭,无法做出什么保证

当调用 socket.close() 的时候会发生什么呢?正常情况下:

  • 操作系统等所有的数据发送完才会关闭连接
  • 因为是主动关闭,所以连接将处于 TIME_WAIT 两个 MSL

SO_LINGER

Linux 的套接字选项SO_LINGER 用来改变socket 执行 close() 函数时的默认行为。

SO_LINGER 参数是一个 linger 结构体,代码如下:

struct linger {
    int l_onoff;    /* linger active */
    int l_linger;   /* how many seconds to linger for */
};
  • 第一个字段 l_onoff 用来表示是否启用 linger 特性,非 0 为启用,0 为禁用 ,linux 内核默认为禁用。
    • 禁用情况下, close 函数立即返回,操作系统负责把缓冲队列中的数据全部发送至对端
  • 第二个参数 l_linger 在 l_onoff 为非 0 (即启用特性)时才会生效。
    • 如果 l_linger 的值为 0,那么调用 close,close 函数会立即返回,同时丢弃缓冲区内所有数据并立即发送 RST 包重置连接
    • 如果 l_linger 的值为非 0,那么此时 close 函数在阻塞直到 l_linger 时间超时或者数据发送完毕,发送队列在超时时间段内继续尝试发送,如果发送完成则皆大欢喜,超时则直接丢弃缓冲区内容 并 RST 掉连接。

小结

image.png