【问题标题】:How to use SO_KEEPALIVE option properly to detect that the client at the other end is down?如何正确使用 SO_KEEPALIVE 选项来检测另一端的客户端已关闭?
【发布时间】:2011-07-23 01:10:08
【问题描述】:

我正在尝试学习Linux环境下C语言socket编程中选项SO_KEEPALIVE的用法。

我创建了一个服务器套接字并使用我的浏览器连接到它。它成功了,我能够读取 GET 请求,但我卡在 SO_KEEPALIVE 的使用上。

我检查了此链接keepalive_description@tldg.org ,但找不到任何说明如何使用它的示例。

一旦我在accept() 函数上检测到客户端的请求,我就会在客户端套接字上设置SO_KEEPALIVE 选项值1。现在我不知道,如何检查客户端是否宕机,如何更改发送的探测之间的时间间隔等。

我的意思是,我将如何获得客户端已关闭的信号? (没有在客户端读取或写入 - 我认为当探针没有从客户端回复时我会收到一些信号),在设置选项 SO_KEEPALIVE 后我应该如何编程)。

另外,如果假设每 3 秒发送一次探测,并且客户端在这之间发生故障,我将不会知道客户端已故障,我可能会收到 SIGPIPE。

无论如何,重要的是我想知道如何在代码中使用 SO_KEEPALIVE。

【问题讨论】:

    标签: c linux sockets keep-alive


    【解决方案1】:

    要修改探测次数或探测间隔,您可以将值写入 /proc 文件系统,如

     echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time
     echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl
     echo 20 > /proc/sys/net/ipv4/tcp_keepalive_probes
    

    请注意,这些值对于系统上所有启用 keepalive 的套接字都是全局的,您还可以在设置 setsockopt 时基于每个套接字覆盖这些设置,请参阅您链接的文档的第 4.2 节。

    您不能使用 keepalive 从用户空间“检查”套接字的状态。相反,内核只是更积极地强制远程端确认数据包,并确定套接字是否坏了。当您尝试写入套接字时,如果 keepalive 确定远程端已关闭,您将获得 SIGPIPE。

    【讨论】:

    • 从套接字读取时,当状态发生变化时,您会收到通知。如果由于keepalives而确定对等方已死亡,则select/poll将通知套接字可读,并且read()/recv()将返回错误。无论如何,您应该始终读取或监视套接字以进行读取。
    • 是的,我认为就写作而言,这只是意味着您可以更早地获得 SIGPIPE,因为内核会更快地检测到故障。如果您正在从套接字读取,它还允许 read() 返回错误,而不是根据上面的@nos 注释仅返回“没有可用数据”。获取读取事件 w。从选择循环读取错误可能是来自 SO_KEEPALIVE 的最佳“早期通知”
    • 请注意,至少在配置的 keepalive_time + (keepalive_intrvl*keepalive_probes) 之前,keepalive 不会检测到故障。我认为默认情况下,如果您不更改设置,这可能会默认超过一个小时!
    • @nirudh Tomer 默认的 keepalive 探测每 2 小时发送一次,如果间隔 75 秒的 9 个探测都失败,则对等方被确定为死亡。你是如何调整默认值的?请注意,tcp keepalive 并不是专门为检测对等方的快速死亡而设计的。如果对等点仍然可以访问,则发送将失败,没关系,您检测到它死了!如果网络出现问题,快速检测到死节点并不容易,通常也不可取。
    • ...如果您真的需要一种可靠的方法来快速检测到这种情况,您需要在应用程序级别发送心跳,并在您的读/写操作中使用一些合理的超时(注意,没有免费的在那里吃午饭,这总是以很多代码来覆盖很多极端情况)
    【解决方案2】:

    如果您启用 SO_KEEPALIVE,您将获得相同的结果,就像您不启用 SO_KEEPALIVE - 通常您会发现套接字已准备好并在读取时收到错误。

    您可以在 Linux 下基于每个套接字设置 keepalive 超时(这可能是 Linux 特定的功能)。我建议这样做,而不是更改系统范围的设置。有关详细信息,请参阅 tcp 的手册页。

    最后,如果您的客户端是 Web 浏览器,它很可能无论如何都会很快关闭套接字 - 它们中的大多数只会在相对较短的时间内(30 秒、1 分钟等)保持 keepalive (HTTP 1.1) 连接打开)。当然如果客户端机器已经消失或网络宕机(这是 SO_KEEPALIVE 真正有用的检测),那么它将无法主动关闭套接字。

    【讨论】:

    • 在 Windows 中,您可以使用 WSAIoctl 配置每个套接字 TCP 保持活动设置。
    【解决方案3】:

    如前所述,SO_KEEPALIVE 使内核更加积极地持续验证连接,即使您什么都不做,但 不会 改变或增强向您传递信息的方式。当您尝试实际做某事(例如“写”)时,您会发现,并且您会立即发现,因为内核现在只是报告先前设置的标志的状态,而不必等待一些秒(或在某些情况下更长)网络活动失败。仍将使用与处理“对方意外离开”条件完全相同的代码逻辑;改变的是时间(而不是方法)。

    几乎每个“实用”套接字程序都以某种方式在数据阶段提供了对套接字的非阻塞访问(可能使用 select()/poll(),或者可能使用 fcntl()/ O_NONBLOCK/EINPROGRESS&EWOULDBLOCK,或者如果您的内核支持它,可能使用 MSG_DONTWAIT)。假设由于其他原因已经完成了这项工作,另外立即发现连接断开是微不足道的(有时根本不需要代码)。但是,如果数据阶段确实已经以某种方式提供了对套接字的非阻塞访问,那么直到下次尝试执行某些操作时,您才会发现连接断开。

    (在数据阶段没有某种非阻塞行为的 TCP 套接字连接是出了名的脆弱,就好像错误的数据包遇到网络问题一样,程序很容易无限期地“挂起”,并且没有你可以做很多事情。)

    【讨论】:

    • 有没有办法在不尝试实际写入的情况下访问这个“先前设置的标志”?
    【解决方案4】:

    简答,补充

    int flags =1;
    if (setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags))) { perror("ERROR: setsocketopt(), SO_KEEPALIVE"); exit(0); };
    

    在服务器端,当客户端关闭时,read() 将被解锁。

    完整的解释可以在here找到。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多