curl 之 CURLOPT_TCP_KEEPCNT:精确控制 TCP Keep-Alive 探测次数

发布时间:2026/9/10 18:20:20
curl 之 CURLOPT_TCP_KEEPCNT:精确控制 TCP Keep-Alive 探测次数 curl 之 CURLOPT_TCP_KEEPCNT精确控制 TCP Keep-Alive 探测次数【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl导读CURLOPT_TCP_KEEPCNT是 libcurl 提供的 TCP keep-alive 细粒度控制选项用于设置连接在判定为“死亡”之前最多允许发送的 keep-alive 探测报文probe次数。本文以 libcurl 官方选项文档 CURLOPT_TCP_KEEPCNT.md 为主体结合本仓库中 setopt.c、cf-socket.c、url.c 等源码讲解该选项的语义、默认值、取值范围、底层调用链与跨平台差异并给出可直接编译运行的完整示例。读完本文你将掌握用 libcurl 将 keep-alive 探测次数精确调整到目标数值的完整方法理解它在 Linux、macOS、Windows、Solaris 上的不同落地方式。选项概览CURLOPT_TCP_KEEPCNT属性值选项名CURLOPT_TCP_KEEPCNT选项类型CURLOPTTYPE_LONGlong 型选项数值326见 include/curl/curl.h协议TCP引入版本8.9.0仓库文档标注 Added-in: 8.9.0默认值9该选项与CURLOPT_TCP_KEEPALIVE、CURLOPT_TCP_KEEPIDLE、CURLOPT_TCP_KEEPINTVL共同构成 libcurl 的 TCP keep-alive 参数族CURLOPT_TCP_KEEPALIVE总开关置 1 启用 keep-alive 探测CURLOPT_TCP_KEEPIDLE空闲多少秒后开始发送第一个探测CURLOPT_TCP_KEEPINTVL两次探测之间的间隔秒数CURLOPT_TCP_KEEPCNT对端无响应时最多连续发送几次探测后放弃该连接。函数原型SYNOPSIS#include curl/curl.h CURLcode curl_easy_setopt(CURL *handle, CURLOPT_TCP_KEEPCNT, long cnt);语义详解DESCRIPTION向curl_easy_setopt传入一个long值cnt它表示在放弃连接之前允许发送的 keep-alive 探测报文数量。也就是说当 TCP 连接进入 keep-alive 探测阶段对端已连续TCP_KEEPIDLE秒无数据后每TCP_KEEPINTVL秒发送一次探测若累计发送cnt次仍未收到对端 ACK 响应则判定连接已失效并将其断开。需要注意的边界条件并非所有操作系统都支持该选项。原文档明确指出 Not all operating systems support this option.某些平台的内核并不提供TCP_KEEPCNT对应的setsockopt选项此时该设置会被静默忽略详见下文“跨平台实现差异”。取值上限受系统约束。选项可接受的最大值为INT_MAX或“你的系统所允许的值”两者取更小者任何更大的传入值都会被裁剪capped到这个上限而不是报错。从源码看这一上限校验发生在 lib/setopt.c 的选项处理分支中case CURLOPT_TCP_KEEPCNT: result value_range(arg, 0, 0, INT_MAX); if(!result) s-tcp_keepcnt (int)arg; break;这里调用了value_range将参数限制在[0, INT_MAX]区间小于 0 的值会返回错误CURLE_BAD_FUNCTION_ARGUMENT大于INT_MAX的值被裁剪为INT_MAX校验通过后值被以int形式存入 easy handle 的设置结构体data-set.tcp_keepcnt该字段在 lib/urldata.h 中声明int tcp_keepidle; /* seconds in idle before sending keepalive probe */ int tcp_keepintvl; /* seconds between TCP keepalive probes */ int tcp_keepcnt; /* maximum number of keepalive probes */默认值DEFAULT默认值为9。该默认值并非文档凭空给出而是定义在 lib/url.c 的 easy handle 初始化函数中set-tcp_keepalive FALSE; set-tcp_keepintvl 60; set-tcp_keepidle 60; set-tcp_keepcnt 9; set-tcp_fastopen FALSE; set-tcp_nodelay TRUE;可以看到默认情况下tcp_keepalive为FALSEkeep-alive 总开关关闭一旦开启则默认空闲 60 秒开始探测、每 60 秒探测一次、最多探测 9 次。默认值 9 与 Linux 内核tcp_keepalive_probes的系统默认值一致也与 lib/cf-socket.c 注释中“The default value of TCP_KEEPCNT is 9 on Linux, 8 on *BSD/macOS, 5 or 10 on Windows”的描述吻合。协议适用范围PROTOCOLS该选项仅适用于TCP。文档头部 Protocol 字段标注为 TCP代码中的落地位置也印证了这一点在 lib/cf-socket.c 的 socket 建立逻辑中只有判定为 TCP 流式 socketis_tcp为真时才应用 keep-alive 相关设置if(is_tcp) { if(data-set.tcp_nodelay) tcpnodelay(cf, data, ctx-sock); if(data-set.tcp_keepalive) tcpkeepalive(cf, data, ctx-sock); tcplocalhost(cf, ctx-sock); }也就是说CURLOPT_TCP_KEEPCNT只有配合CURLOPT_TCP_KEEPALIVE启用、且连接类型为 TCP包括 HTTP、HTTPS、FTP、FTPS、SMTP、IMAP、POP3 等基于 TCP 的协议时才真正生效。完整示例EXAMPLE原文档给出了一个完整的可运行示例将 keep-alive 参数族全部设置一遍下面完整保留并补充注释int main(void) { CURL *curl curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, https://example.com); /* 启用 TCP keep-alive这是后续所有 keep-alive 参数生效的前提 */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); /* 空闲 120 秒后开始发送第一个 keep-alive 探测 */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L); /* 两次探测之间的间隔为 60 秒 */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 60L); /* 最多发送 3 次探测仍无响应则放弃连接 */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPCNT, 3L); result curl_easy_perform(curl); curl_easy_cleanup(curl); } }按此配置一条连接从空闲到被判死的时间窗口约为KEEPIDLE KEEPCNT × KEEPINTVL 120 3 × 60 300秒之后对端若仍无响应连接即被内核断开。实际窗口是否精确等于该公式取决于平台内核的实现方式见下文 Solaris 的特殊处理。底层实现从选项到 setsockopt 的完整调用链要理解CURLOPT_TCP_KEEPCNT的生效机制可以沿着如下调用链追踪应用调用curl_easy_setopt(curl, CURLOPT_TCP_KEEPCNT, cnt)进入 lib/setopt.c 的case CURLOPT_TCP_KEEPCNT分支经value_range校验后存入data-set.tcp_keepcnt传输建立连接时lib/cf-socket.c 检测到data-set.tcp_keepalive为真调用静态函数tcpkeepalive()tcpkeepalive()定义于 lib/cf-socket.c先通过setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, ...)打开 socket 级 keep-alive成功后再依次下发 IDLE、INTVL、CNT 三个 TCP 级参数。其中 CNT 的落地代码位于 lib/cf-socket.c#ifdef TCP_KEEPCNT optval curlx_sltosi(data-set.tcp_keepcnt); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, (void *)optval, sizeof(optval)) 0) { CURL_TRC_CF(data, cf, Failed to set TCP_KEEPCNT on fd % FMT_SOCKET_T : errno %d, sockfd, SOCKERRNO); } #endif注意两点实现细节使用curlx_sltosi将long安全转换为int后再调用setsockopt与字段类型int tcp_keepcnt保持一致整个调用被#ifdef TCP_KEEPCNT保护只有系统头文件定义了TCP_KEEPCNT宏Linux 等平台才会真正下发该参数若平台不支持则只输出一条CURL_TRC_CF调试日志后继续不影响连接建立——这正是文档中“并非所有操作系统支持”的源码级体现。跨平台实现差异TCP_KEEPCNT 在各类系统上的落地方式从 lib/cf-socket.c 及后续分支可以看出libcurl 对不同平台采用了差异化的 keep-alive 实现策略tcp_keepcnt也因此有不同的落地路径平台实现方式说明Linux 及支持TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT的系统直接setsockopt(IPPROTO_TCP, TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT)三个参数可独立、精确设置单位秒macOS仅有TCP_KEEPALIVE以TCP_KEEPALIVE实现 idle 语义无独立TCP_KEEPCNT下发路径Solaris 11.4仅有TCP_KEEPALIVE_THRESHOLD与TCP_KEEPALIVE_ABORT_THRESHOLD将keepcnt × keepintvl合并计算为 abort 阈值一次下发见 lib/cf-socket.cWindows 10.0.16299 及以上setsockopt(IPPROTO_TCP, TCP_KEEP*)需curlx_verify_windows_version版本校验且 Windows 上时间单位毫秒需乘 1000KEEPALIVE_FACTOR更老的 Windows / 老版本 DragonFlyBSD / 老版本 SolarisSIO_KEEPALIVE_VALSioctl 或时间单位换算时间单位同样经KEEPALIVE_FACTOR放大尤其值得关注的是 Solaris 11.4 的合并策略lib/cf-socket.c/* TCP_KEEPALIVE_ABORT_THRESHOLD should equal to * TCP_KEEPCNT * TCP_KEEPINTVL on other platforms. */ { int keepcnt curlx_sltosi(data-set.tcp_keepcnt); int keepintvl curlx_sltosi(data-set.tcp_keepintvl); if(keepcnt 0 keepintvl (INT_MAX / keepcnt)) optval INT_MAX; else optval keepcnt * keepintvl; }这段代码做了乘法溢出保护当keepcnt * keepintvl超过INT_MAX时直接取INT_MAX避免整数溢出。同时源码注释也提醒Solaris 上探测并非等间隔发送而是采用指数退避exponential backoff算法。这也意味着同一套CURLOPT_TCP_KEEPCNT参数在不同平台上最终的探测节奏可能不同跨平台部署时不要假设所有内核行为完全一致。可用性AVAILABILITY该选项自8.9.0起可用文档头部Added-in: 8.9.0。与其配套的族类选项引入时间不同CURLOPT_TCP_KEEPALIVE自 7.25.0 引入见 CURLOPT_TCP_KEEPALIVE.md。因此若你的代码需要在 8.9.0 之前的 libcurl 上编译运行应通过#ifdef CURLOPT_TCP_KEEPCNT或版本宏做条件编译保护。在 lib/easyoptions.c 的选项表中该族选项被登记为CURLOT_LONG类型支持通过curl_easy_setopt动态设置也可被curl_easy_getinfo相关机制和 curl 命令行工具生成代码--libcurl识别。返回值RETURN VALUEcurl_easy_setopt返回CURLcode以指示成功或失败CURLE_OK0设置成功非零值发生错误具体错误码参见 libcurl 错误码文档 libcurl-errors。对CURLOPT_TCP_KEEPCNT而言最常见的错误返回值出现在传入负值时——value_range(arg, 0, 0, INT_MAX)会拒绝[0, INT_MAX]区间之外的值超过INT_MAX的数值则会被静默裁剪而非报错。最佳实践与注意事项先开总开关CURLOPT_TCP_KEEPCNT只有在CURLOPT_TCP_KEEPALIVE置 1 时才会被应用到 socket 上见 lib/cf-socket.c单独设置 CNT 没有任何效果。三个参数协同设计判定连接死亡的总时间 ≈KEEPIDLE KEEPCNT × KEEPINTVL。长连接场景如 IMAP/SMTP 长时间空闲建议调大KEEPIDLE网络抖动较大的场景建议适当调大KEEPCNT避免单次丢包导致连接被误杀。注意平台差异macOS 老版本、Solaris 11.4 等平台不提供独立TCP_KEEPCNTlibcurl 会退化为其他实现或忽略该参数Windows 上时间单位为毫秒libcurl 内部已用KEEPALIVE_FACTOR做了换算应用层无需关心。不要依赖精确时间语义如 Solaris 指数退避所揭示的不同内核的探测节奏实现并不统一应把该参数视为“尽力而为”的调优手段而不是精确的定时器。参考文件导航本文主体文档docs/libcurl/opts/CURLOPT_TCP_KEEPCNT.md配套族类文档CURLOPT_TCP_KEEPALIVE.md、CURLOPT_TCP_KEEPIDLE.md、CURLOPT_TCP_KEEPINTVL.md参数校验与存储lib/setopt.c、lib/urldata.h默认值初始化lib/url.csocket 层落地实现lib/cf-socket.c选项数值定义include/curl/curl.h选项表登记lib/easyoptions.c【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考