TCP Keepalive HOWTO | ||
---|---|---|
上一页 |
并非所有人都是软件开发者,如果只是缺少一个功能,也并非所有人都会从头重写软件。也许你想为一个现有的应用程序添加 keepalive 支持,因为虽然作者可能认为这不重要,但你认为它会很有用。
首先,记住之前关于你需要 keepalive 的情况的描述。现在你需要处理面向连接的 TCP 套接字。
由于 Linux 没有提供通过内核本身启用 keepalive 支持的功能(像类 BSD 操作系统通常做的那样),唯一的办法是在创建套接字后执行 setsockopt (2) 调用。 有两种解决方案
修改原始程序的源代码
使用库预加载技术注入 setsockopt (2)
记住 keepalive 与程序无关,而是与套接字相关的,所以如果你有多个套接字,你可以分别处理每个套接字的 keepalive。第一阶段是理解程序的功能,然后在代码中搜索每个套接字。这可以使用 grep(1) 完成,如下所示
# grep 'socket *(' *.c |
这将或多或少地显示代码中的所有套接字。下一步是只选择正确的套接字:你需要 TCP 套接字,所以查找PF_INET(或AF_INET), SOCK_STREAM和IPPROTO_TCP(或更常见的,0)在你的套接字列表的参数中,并移除不匹配的项。
另一种创建套接字的方式是通过 accept(2)。 在这种情况下,跟踪已识别的 TCP 套接字,并检查其中是否有任何是监听套接字:如果是,请记住 accept(2) 返回一个套接字描述符,该描述符必须插入到你的套接字列表中。
一旦你识别出套接字,就可以继续进行更改。最快速和直接的补丁可以通过在套接字创建块之后简单地添加 setsockopt(2 ) 函数来完成。 可选地,如果你不喜欢系统默认值,你可以包含额外的调用来设置 keepalive 参数。在实现函数的错误检查和处理程序时请小心,可以从周围的原始代码中复制样式。记住将optval设置为非零值,并在调用函数之前初始化optlen。
如果你有时间或者你认为这真的很酷,尝试为你的程序添加完整的 keepalive 支持,包括命令行开关或配置参数,让用户选择是否使用 keepalive。
通常在很多情况下,你无法修改应用程序的源代码,或者当你必须为所有程序启用 keepalive 时,修补和重新编译所有内容是不推荐的。
libkeepalive 项目的诞生是为了帮助为应用程序添加 keepalive 支持,因为 Linux 内核没有提供像 BSD 那样本地执行相同操作的能力。libkeepalive 项目主页是 http://libkeepalive.sourceforge.net/
它由一个共享库组成,该库覆盖了大多数二进制文件中的 socket 系统调用,而无需重新编译或修改它们。 该技术基于 Linux 中包含的 ld.so(8) 加载器的 预加载 功能,它允许你强制加载优先级高于正常的共享库。 程序通常使用位于glibc共享库中的 socket(2) 函数调用; 使用 libkeepalive,你可以包装它并在套接字创建后立即注入 setsockopt (2),从而返回一个已经为主程序设置了 keepalive 的套接字。 由于用于注入系统调用的机制,当 socket 函数被静态编译到二进制文件中时,这不起作用,例如在使用 gcc(1 ) 标志链接的程序中-static.
下载并安装 libkeepalive 后,你将能够为你的程序添加 keepalive 支持,而无需成为root,只需在执行程序之前设置LD_PRELOAD环境变量。 顺便说一句,超级用户也可以使用全局配置强制预加载,然后用户可以通过将KEEPALIVE环境变量设置为off.
来决定关闭它。 环境也用于设置 keepalive 参数的特定值,因此你可以有能力以不同的方式处理每个程序,在启动应用程序之前设置KEEPCNT, KEEPIDLE和KEEPINTVL。
这是一个 libkeepalive 用法的示例
$ test SO_KEEPALIVE is OFF $ LD_PRELOAD=libkeepalive.so \ > KEEPCNT=20 \ > KEEPIDLE=180 \ > KEEPINTVL=60 \ > test SO_KEEPALIVE is ON TCP_KEEPCNT = 20 TCP_KEEPIDLE = 180 TCP_KEEPINTVL = 60 |
你可以使用 strace (1) 来理解发生了什么
$ strace test execve("test", ["test"], [/* 26 vars */]) = 0 [..] open("/lib/libc.so.6", O_RDONLY) = 3 [..] socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 getsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [0], [4]) = 0 close(3) = 0 [..] _exit(0) = ? $ LD_PRELOAD=libkeepalive.so \ > strace test execve("test", ["test"], [/* 27 vars */]) = 0 [..] open("/usr/local/lib/libkeepalive.so", O_RDONLY) = 3 [..] open("/lib/libc.so.6", O_RDONLY) = 3 [..] open("/lib/libdl.so.2", O_RDONLY) = 3 [..] socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPCNT, [20], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [180], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0 [..] getsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPCNT, [20], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPIDLE, [180], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], [4]) = 0 [..] close(3) = 0 [..] _exit(0) = ? |
有关更多信息,请访问 libkeepalive 项目主页: http://libkeepalive.sourceforge.net/