54-套接字选项(SO_LINGER)

SO_LINGER 是相当复杂的选项之一,它主要影响 close 和 shutdown 函数的行为(参考 man 手册),为了方便讨论,后面我以 close 为例。

在不同的平台上,SO_LINGER 选项表现行为也不一样,不同的类 unix 系统不一样,windows 和 linux 也不一样,这实在是太麻烦了。本文将在 centos7 上对此选项的行为进行分析,然后不加证明的引用一篇针对 SO_LINGER 在不同平台上进行交叉实验的结果。

1. 程序路径

代码托管在 gitos 上,请使用下面的命令获取:

git clone https://git.oschina.net/ivan_allen/unp.git

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/options/opt

2. SO_LINGER 的作用

该选项只对面向连接的(connection-oriented)的协议有用,比如 TCP 协议。它的目的是改变 close 函数的行为

SO_LINGER 的数据类型是结构体:

struct linger {   l_onoff;   l_linger; }

默认情况下,SO_LINGER 选项是被关闭的,此时成员 l_onoff = 0,这种情况下 l_linger 是没有用的。通过前面的 showopts 程序我们可以查看到该默认值,如图 1.


这里写图片描述
图1 查看 SO_LINGER 选项的默认值

3. SO_LINGER 如何影响 close

这里分三种情况讨论(只针对 linux,我当前的测试内核是 3.10),前提条件是对端 TCP 程序正常,套接字描述符是阻塞的。

  • 1) l_onoff == 0, {off, ~}

这是系统的默认情况。此时 l_linger 被忽略,close 立即返回,如果发送缓冲还有残留数据,系统会在后台将这些数据发送给对端,最后发送 FIN 给对端。

  • 2) l_onff != 0 && l_linger == 0 , {on, 0}

close 函数立即返回,将放弃发送缓冲区中残留数据,立即发送 RST 给对端。

  • 3) l_onff != 0 && l_linger > 0, {on, >0}

close 会阻塞,直到下面两种情况中的任何一种发生就返回,返回值是 0:

a) 所有数据都已发送完(包含 FIN),且得到对端的 ack,这种情况为提前返回;

b) 延滞时间到(l_linger 指定的时间,单位为秒)。

在 linux 系统中,无论有没有提前返回,系统会在后台将发送缓冲区中所有残留数据(包含 FIN)发送给对端。该行为与具体的系统实现有关。具体请参考图 2. unp 一书对此描述并不是很清晰,只是说对于非阻塞的套接字来说,会 Abort(它表示放弃发送缓冲区数据并丢一个 RST 给对端)。


这里写图片描述
图2 引用自 Cross-Platform Testing of SO_LINGER

我们关注的是第 3 列,即 linger 超时后,是否要 Abort. 对于 Linux 来说,并没有 Abort,这和我前面叙述的结果一样。待会我会用实验验证。

4. 实验

  • 实验环境:

准备一份 295740 字节大小的文件,只要比较大,能够把对端接收缓冲区占满就行了。我这里采用的是 input2 这个文件。

  • 关于 opt 的 slowread 选项说明

这个选项可以让 opt 程序进入慢读取模式,每隔 2 秒读取 size 字节,size 由你指定。这种情况下,opt 程序只读不发。

4.1 {off, ~} 情况


这里写图片描述
图3 close 立即返回,并在后台继续发送数据

图3中, 在接收方 flower 慢读取模式下,接收方的接收缓冲区很快被占满,导致接收窗口变成了 0.


这里写图片描述
图4 发送方 sun 在后台持续发送,直到最后一个 FIN 被确认

图4, 接收方 flower 将缓冲区中数据读取完后,缓冲区有了空闲位置,发送非 0 接收窗口给对方 sun,通知对方 sun 可以继续发送数据。


这里写图片描述
图5 接收方 flower 接收到了所有数据,并进行了关闭,发送 FIN,可是客户端进程已经不存在

图5, 接收方 flower 读取完数据发送 FIN 给发送方,但是发送方 sun 进程已经关闭。

4.2 {on, 0}


这里写图片描述
图6 发送方sun close 立即返回并 Abort

我们可以利用这种方法实现发送方主动发送 RST,比如有人就用这种方法来避免出现 TIME_WAIT 状态。


这里写图片描述
图7 接收方 flower 并没有接收到所有数据,只收到了 117040 字节

4.3 {on, 5}


这里写图片描述
图8 发送方 sun 阻塞在 close 上,5 秒等待


这里写图片描述
图9 发送方 close 返回,后台仍然在发送数据,并没有 Abort

图 9, 这里很关键,尽管 sun 的数据还没有发送完(发送缓冲区还有数据),连接也没有 Abort,系统在后台继续发送数据。


这里写图片描述
图10 发送方 sun 在后台发送完了所有数据

图 10,即便开启了 linger,close 也返回了,后台仍然将数据发送完毕,并发磅了 FIN 段给 flower.


这里写图片描述
图11 接收方接收到了所有 295740 字节的数据

5. 分析

对于 linger 的前两种情况,一般和书上,别的博客上介绍的一致。只是最后一种情况,即 {on, >0} 的情况,结论就不太一样,SO_LINGER 严重依赖于具体实现。

有的系统实现可能会在 close 超时后进行 abort,比如说 windows,而对于 linux 来说,close 超时后并不会 abort. 具体情况请参考第 4.3 节的实验,以及图 2,以及这篇博文Cross-Platform Testing of SO_LINGER.

6. 总结

  • 掌握 linger 的对 close 的影响以及 close 的行为
  • 知道平台相关性

说明:本文转自blog.csdn.net,用于学习交流分享,仅代表原文作者观点。如有疑问,请联系我们删除~