21

我试图在Linux环境下学习C语言套接字编程中选项SO_KEEPALIVE的用法。

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

我检查了这个链接keepalive_description@tldg.org但我找不到任何显示如何使用它的示例。

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

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

此外,如果假设每 3 秒发送一次探测并且客户端在两者之间关闭,我将不会知道客户端已关闭,我可能会得到 SIGPIPE。

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

4

4 回答 4

22

要修改探测次数或探测间隔,请将值写入 /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。

于 2011-03-25T16:42:11.653 回答
11

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

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

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

于 2011-03-26T14:28:29.093 回答
4

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

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

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

于 2012-09-04T01:29:56.750 回答
4

简短的回答,添加

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

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

完整的解释可以在这里找到。

于 2018-06-22T13:14:44.563 回答