curl 中 CURLOPT_TCP_KEEPIDLE 详解:控制 TCP 保活探测的空闲等待时间
curl 中 CURLOPT_TCP_KEEPIDLE 详解控制 TCP 保活探测的空闲等待时间【免费下载链接】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_KEEPIDLE是 libcurl 提供的 TCP keep-alive 精细控制选项用于设置连接空闲多久后才开始发送保活探测包。它通常与CURLOPT_TCP_KEEPALIVE、CURLOPT_TCP_KEEPINTVL、CURLOPT_TCP_KEEPCNT配合使用帮助开发者解决长连接被中间设备NAT、防火墙、运营商网关静默掐断的经典问题。阅读本文后你将掌握该选项的完整语义、默认值、平台差异以及它在 curl 源码中的落地实现与命令行工具中的对应用法。一、选项概述TCP keep-alive 空闲等待时间TCP 连接建立后如果长时间没有数据传输网络路径上的 NAT 设备、防火墙或代理可能将这条“僵尸连接”悄悄回收导致客户端在真正需要发送数据时才发觉连接已失效。TCP keep-alive 机制正是为此设计连接空闲一段时间后协议栈主动发送探测包确认对端仍然存活。CURLOPT_TCP_KEEPIDLE控制的就是上述机制中的“空闲时间阈值”——连接保持空闲多少秒之后才允许协议栈开始发送第一个 keep-alive 探测包。其官方定义为Pass a long. Sets thedelay, in seconds, to wait while the connection is idle before sending keepalive probes.该选项在 libcurl 7.25.0 版本中加入仅对 TCP 协议生效。二、API 原型与参数说明#include curl/curl.h CURLcode curl_easy_setopt(CURL *handle, CURLOPT_TCP_KEEPIDLE, long delay);参数delay以秒为单位含义连接空闲delay秒后TCP 协议栈开始发送保活探测包默认值60 秒不显式设置时采用此值最大值2147483648。任何超过该值的输入都会被截断cap为 2147483648平台限制并非所有操作系统都支持该选项详见第五节。2.1 最大值截断的源码证据在 lib/setopt.c 中CURLOPT_TCP_KEEPIDLE的实际处理逻辑如下case CURLOPT_TCP_KEEPIDLE: result value_range(arg, 0, 0, INT_MAX); if(!result) s-tcp_keepidle (int)arg; break;其中value_range()lib/setopt.c的语义为小于below_error的值返回CURLE_BAD_FUNCTION_ARGUMENT小于最小值则钳位到最小值大于最大值则钳位到最大值。这里上下界分别为 0 与INT_MAX即 2147483647配合 64 位平台上long的更大范围实现了文档所述的“大于 2147483648 时截断”行为并将最终结果以int类型存入句柄配置。2.2 配置存储位置该值最终保存在struct UserDefined中lib/urldata.hint 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 */三者并列存放配合 lib/urldata.h 中的开关标志BIT(tcp_keepalive); /* use TCP keepalives */一起使用。三、完整示例与其它保活选项搭配使用由于CURLOPT_TCP_KEEPIDLE只是“空闲阈值”单独设置它并不会让保活机制生效——必须先通过CURLOPT_TCP_KEEPALIVE打开保活开关。官方示例CURLOPT_TCP_KEEPIDLE.md给出了一套完整的搭配写法int main(void) { CURL *curl curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, https://example.com); /* enable TCP keep-alive for this transfer */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); /* set keep-alive idle time to 120 seconds */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L); /* interval time between keep-alive probes: 60 seconds */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 60L); /* maximum number of keep-alive probes: 3 */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPCNT, 3L); result curl_easy_perform(curl); curl_easy_cleanup(curl); } }上述代码的含义是连接建立后空闲 120 秒开始发送探测包之后每隔 60 秒探测一次连续 3 次探测无响应则判定连接失效。四个选项的配套关系如下选项作用本示例值默认值CURLOPT_TCP_KEEPALIVE保活总开关0 关闭 / 1 开启10CURLOPT_TCP_KEEPIDLE空闲多少秒后开始探测12060CURLOPT_TCP_KEEPINTVL两次探测之间的间隔秒数6060CURLOPT_TCP_KEEPCNT判定连接失效前的最多探测次数3由操作系统决定配套选项的详细文档可分别查阅 CURLOPT_TCP_KEEPALIVE.md、CURLOPT_TCP_KEEPINTVL.md 与 CURLOPT_TCP_KEEPCNT.md。3.1 一个实用建议对于通过 NAT 访问公网的长连接场景可考虑将CURLOPT_TCP_KEEPIDLE设置为略小于 NAT 映射超时时间的值常见为 25 分钟并配合同等量级的CURLOPT_TCP_KEEPINTVL以尽量小的探测开销维持连接存活。不过具体数值需结合网络环境实测不同运营商与设备的超时策略差异很大。四、返回值的正确解读curl_easy_setopt()始终返回CURLcodeCURLE_OK0设置成功非零值发生错误具体含义参见 libcurl-errors.md。需要特别指出的是返回值只反映参数是否被接受并写入句柄配置并不代表底层的setsockopt()调用成功。实际生效发生在连接建立阶段若当时的系统调用失败curl 只会通过调试跟踪日志CURL_TRC_CF输出类似Failed to set TCP_KEEPIDLE on fd ...的告警而不会让传输失败。这一点在第五节源码分析中可以看到。五、跨平台实现剖析从选项到内核套接字CURLOPT_TCP_KEEPIDLE的语义在各操作系统上是一致的但落到内核 API 时差异巨大。curl 在 lib/cf-socket.c 的tcpkeepalive()函数中做了完整的兼容处理该函数在 TCP 连接建立后立即被调用lib/cf-socket.cif(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); }5.1 总开关先行SO_KEEPALIVEtcpkeepalive()的第一步总是先调用setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, ...)打开协议栈层面的保活总开关lib/cf-socket.c。只有当这一步成功时才会继续设置 IDLE / INTVL / CNT 等细化参数源码注释明确写道 only set IDLE and INTVL if setting KEEPALIVE is successful。5.2 各平台的 idle 参数映射设置空闲时间的代码存在四级回退取决于编译期可用的常量lib/cf-socket.cTCP_KEEPIDLELinux、较新的 AIX、HP-UX 等直接使用这是最标准的路径TCP_KEEPALIVEmacOS / *BSD 风格libcurl 把tcp_keepidle值写入该选项TCP_KEEPALIVE_THRESHOLDSolaris 11.4 风格同样承载 idle 语义若均不可用则跳过该参数。5.3 单位换算KEEPALIVE_FACTOR不同平台对时间的计量单位不一致KEEPALIVE_FACTOR宏lib/cf-socket.c专门处理此差异#if defined(USE_WINSOCK) || \ (defined(__sun) !defined(TCP_KEEPIDLE)) || \ (defined(__DragonFly__) __DragonFly_version 500702) || \ (defined(_WIN32) !defined(TCP_KEEPIDLE)) /* Solaris 11.4, DragonFlyBSD 500702 and Windows 10.0.16299 * use millisecond units. */ #define KEEPALIVE_FACTOR(x) ((x) * 1000) #else #define KEEPALIVE_FACTOR(x) #endif也就是说Solaris 11.4 之前、DragonFlyBSD 500702 之前以及 Windows 10.0.16299 之前的老平台内核接口以毫秒为单位curl 自动将用户给出的秒数乘以 1000其余平台直接以秒传递。5.4 Windows 的两套实现路径Windows 分支lib/cf-socket.c根据系统版本选择不同方案Windows 10 170910.0.16299及以上使用标准setsockopt()的TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT系列选项与 Linux 语义一致更老的 Windows退化为WSAIoctl(SIO_KEEPALIVE_VALS, ...)此时结构体中的keepalivetime对应 idle 值、keepaliveinterval对应间隔值老版本单位同样是毫秒且TCP_KEEPCNT无法直接表达。5.5 Solaris 的特殊合并处理在仅有TCP_KEEPALIVE_ABORT_THRESHOLD的 Solaris 老平台上curl 将keepcnt * keepintvl的乘积作为“判定超时”整体写入lib/cf-socket.c并在注释中说明Linux 默认探测次数为 9、*BSD/macOS 为 8、Windows 为 5 或 10且 Solaris 的探测间隔并非等长而是指数退避。这也是CURLOPT_TCP_KEEPCNT的默认值由操作系统决定的原因。六、命令行工具中的对应用法--keepalive-timelibcurl 的该组选项在 curl 命令行工具中有直接映射。--keepalive-time seconds正是CURLOPT_TCP_KEEPIDLE的命令行入口文档见 keepalive-time.md其官方描述为Set the time a connection needs to remain idle before sending keepalive probes and the time between individual keepalive probes. It is currently effective on operating systems offering theTCP_KEEPIDLEandTCP_KEEPINTVLsocket options (meaning Linux, *BSD/macOS, Windows, Solaris, and recent AIX, HP-UX and more).用法示例curl --keepalive-time 20 https://example.com注意该文档同时明确了两个关键事实keep-alive 用于检测空闲连接上的网络中断broken networks是维持长连接健康度的通用手段若未指定默认值同样是 60 秒与 libcurl 侧默认值一致。6.1 与 no-keepalive 的冲突命令行工具默认开启 keep-alive对应CURLOPT_TCP_KEEPALIVE默认为 1 的场景差异需注意libcurl API 侧该开关默认为 0而命令行工具会主动开启。文档特别警告使用--no-keepalive时--keepalive-time完全不生效no-keepalive.md这与源码中“先设SO_KEEPALIVE、失败即跳过后续参数”的实现逻辑完全一致。此外命令行工具还提供--keepalive-cnt countkeepalive-cnt.md来覆盖“判定连接失效前的探测次数”它对应CURLOPT_TCP_KEEPCNT而探测间隔则复用同一个--keepalive-time参数即该参数同时承载了 libcurl 侧CURLOPT_TCP_KEEPIDLE与CURLOPT_TCP_KEEPINTVL两个选项的语义两者默认值都是 60 秒因此多数场景下合并并无影响。七、从选项注册表到句柄的完整链路为了让你对参数的处理流程有整体认识这里梳理CURLOPT_TCP_KEEPIDLE从注册到生效的完整调用链选项注册在 lib/easyoptions.c 中注册为CURLOT_LONG类型的取值选项供curl_easy_setopt()的参数合法性校验使用参数解析lib/setopt.c 中的value_range()完成范围钳位0 ~ INT_MAX存入data-set.tcp_keepidle生效时机TCP 连接建立后socket 过滤器cfilter框架在 lib/cf-socket.c 检测到tcp_keepalive开关打开随即调用tcpkeepalive()内核落地tcpkeepalive()依据平台能力将秒值必要时换算为毫秒通过setsockopt()写入内核 TCP 栈。八、使用注意事项小结必须先开启CURLOPT_TCP_KEEPALIVE否则CURLOPT_TCP_KEEPIDLE即使设置也不会生效默认值 60 秒不设置即采用系统 libcurl 默认最大值 2147483648 秒超出自动截断负值会返回CURLE_BAD_FUNCTION_ARGUMENT平台相关选项受操作系统支持程度限制macOS 走TCP_KEEPALIVE、老 Solaris 走TCP_KEEPALIVE_THRESHOLD/TCP_KEEPALIVE_ABORT_THRESHOLD、老 Windows 走SIO_KEEPALIVE_VALS且单位是毫秒setsockopt()失败不阻断传输只会输出CURL_TRC_CF调试日志实际影响需通过开启 libcurl 跟踪如CURL_DEBUG环境变量配合调试构建观察配合探测次数使用判定连接失效所需的探测次数由 OS 决定Linux 常见为 9、*BSD/macOS 为 8、Windows 为 5 或 10可通过CURLOPT_TCP_KEEPCNT覆盖。相关文档索引配套选项CURLOPT_TCP_KEEPALIVE.md | CURLOPT_TCP_KEEPINTVL.md | CURLOPT_TCP_KEEPCNT.md命令行对应keepalive-time.md | keepalive-cnt.md | no-keepalive.md错误码说明libcurl-errors.md核心实现lib/setopt.c | lib/cf-socket.c | lib/urldata.h【免费下载链接】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),仅供参考