libcurl CURLINFO_LASTSOCKET:获取传输会话最后使用的 Socket,及其弃用原因与 ACTIVESOCKET 迁移指南
【免费下载链接】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
本文围绕 libcurl 的CURLINFO_LASTSOCKET接口展开:它是一个用于在CURLOPT_CONNECT_ONLY场景下从 easy handle 中"提取"已建立连接的 socket 的查询参数。读完后,你将理解它的工作机制与源码实现(Curl_getconnectinfo与连接池中的lastconnect_id)、它为何在 7.45.0 起被标记为弃用(64 位 Windows 上SOCKET为 64 位而long仅 32 位),以及如何平滑迁移到替代品CURLINFO_ACTIVESOCKET。
接口概览:CURLINFO_LASTSOCKET 是什么
CURLINFO_LASTSOCKET是curl_easy_getinfo(3)的一个查询参数,用于接收"本次 curl 会话最后使用的 socket"。它的原型为:
#include <curl/curl.h> CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_LASTSOCKET, long *socket);根据 CURLINFO_LASTSOCKET 官方文档:
- 传参方式:传入一个
long类型变量的指针; - 返回值语义:返回该会话最后使用的 socket 句柄;如果 socket 已不再有效,则返回
-1; - 适用协议:All(所有协议均可用);
- 引入版本:7.15.2;
- 典型搭配:与
CURLOPT_CONNECT_ONLY(3)联合使用——先让 libcurl 完成连接建立但不做数据传输,再由应用取出 socket 自行操作; - 所有权约定:使用完该 socket 后,你仍然必须像往常一样调用
curl_easy_cleanup(3),由 libcurl 负责关闭 socket 并清理该 handle 关联的其他资源; - 弃用状态:自 7.45.0 起被标记为 Deprecated,官方建议改用
CURLINFO_ACTIVESOCKET(3)。
返回值的判定遵循curl_easy_getinfo(3)的统一约定:CURLcode为CURLE_OK(0)表示查询成功,非 0 表示发生错误,具体错误码见libcurl-errors(3)。在 curl_easy_getinfo 文档 的参数总表中,它同样被明确标注为Deprecated:"Last socket used. See CURLINFO_LASTSOCKET(3)"。
在 curl.h 头文件 中,该枚举的定义本身就携带了弃用信息:
/* include/curl/curl.h */ CURLINFO_LASTSOCKET CURL_DEPRECATED(7.45.0, "Use CURLINFO_ACTIVESOCKET") = CURLINFO_LONG + 29,可见它属于CURLINFO_LONG这一类型族——这正是后文弃用问题的根源:结果被装进一个long。
典型用法:与 CURLOPT_CONNECT_ONLY 配合提取连接
CURLINFO_LASTSOCKET最主要的应用场景是"只建立连接、不做传输"。CURLOPT_CONNECT_ONLY的语义(见 CURLOPT_CONNECT_ONLY 文档)是:传1时,libcurl 执行所有必要的代理认证与连接建立,但不进行任何数据传输就返回;传2时,若协议是 WebSocket,libcurl 会完成握手请求并读完全部响应头后再把控制权交给应用。此外,connect-only 传输既不复用已有连接,其建立的连接也不允许被复用。
因此一个经典的"借 libcurl 建链、自己收发数据"流程是:
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L)跳过传输阶段;curl_easy_perform(curl)完成连接建立;curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd)取出 socket;- 应用直接在该 socket 上操作(如自定义协议、TLS 后透传数据等);
- 最后调用
curl_easy_cleanup(curl),libcurl 关闭 socket 并回收资源。
官方文档给出的完整示例(原文保留):
int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; long sockfd; /* does not work on win64 */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Do not do the transfer - only connect to host */ curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); result = curl_easy_perform(curl); if(result != CURLE_OK) { printf("Error: %s\n", curl_easy_strerror(result)); curl_easy_cleanup(curl); return 1; } /* Extract the socket from the curl handle */ result = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd); if(!result && sockfd != -1) { /* operate on sockfd */ } curl_easy_cleanup(curl); } }注意示例中long sockfd; /* does not work on win64 */这一注释——它直接点出了该接口在 64 位 Windows 上的失效问题。
源码实现:从 Curl_getconnectinfo 到连接池查找
从源码结构看,CURLINFO_LASTSOCKET的取值逻辑集中在 getinfo.c:
case CURLINFO_LASTSOCKET: sockfd = Curl_getconnectinfo(data, NULL); /* note: this is not a good conversion for systems with 64-bit sockets and 32-bit longs */ if(sockfd != CURL_SOCKET_BAD) *param_longp = (long)sockfd; else /* this interface is documented to return -1 in case of badness, which may not be the same as the CURL_SOCKET_BAD value */ *param_longp = -1; break;可以确认两个实现细节:
-1与CURL_SOCKET_BAD的区分:底层拿到的无效值CURL_SOCKET_BAD会被刻意转换成文档约定的-1返回,因为两者在数值上可能并不相同;- 有损转换的官方注释:
/* note: this is not a good conversion for systems with 64-bit sockets and 32-bit longs */——源码作者自己也承认这个强制(long)转换在 64 位 socket / 32 位long的系统上是不好的转换。
Curl_getconnectinfo定义在 connect.c,其核心行为是:
curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, struct connectdata **connp) { DEBUGASSERT(data); /* this works for an easy handle: * - that has been used for curl_easy_perform() * - that is associated with a multi handle, and whose connection * was detached with CURLOPT_CONNECT_ONLY */ if(data->state.lastconnect_id != -1) { struct connectdata *conn; conn = Curl_cpool_get_conn(data,>int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; curl_socket_t sockfd; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Do not do the transfer - only connect to host */ curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); result = curl_easy_perform(curl); if(result != CURLE_OK) { printf("Error: %s\n", curl_easy_strerror(result)); curl_easy_cleanup(curl); return 1; } /* Extract the socket from the curl handle */ result = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); if(!result && sockfd != CURL_SOCKET_BAD) { /* operate on sockfd */ } curl_easy_cleanup(curl); } }迁移要点总结:
- 变量类型:由
long sockfd改为curl_socket_t sockfd,确保能完整承载 64 位 socket; - 失效判定:由
sockfd != -1改为sockfd != CURL_SOCKET_BAD; - 语义微调:ACTIVESOCKET 返回的是"最近一次为传输连接而活动的 socket",且仅在传输完成后可取得,典型用法仍是配合
CURLOPT_CONNECT_ONLY(3)跳过传输阶段; - 所有权不变:无论新旧接口,socket 的最终关闭权都在
curl_easy_cleanup(3),应用不应自行close()/closesocket()该句柄。
使用注意事项与测试佐证
- cleanup 不可省略:两条文档都强调,从 handle 提取 socket 后仍必须调用
curl_easy_cleanup(3),由 libcurl 统一关闭 socket 并释放关联资源; - multi 接口约束:如前文所述,connect-only 连接依托 easy handle 在 multi handle 上的挂载状态存活,移除句柄后
curl_easy_send/recv失效; - 测试覆盖:仓库测试用例 lib2032.c 中就实际调用了
curl_easy_getinfo(ntlm_curls[idx], CURLINFO_LASTSOCKET, &longdata)(第 45 行),用于 NTLM 认证场景下对已连接 socket 的处理,说明该接口即便在弃用状态下仍被保留并测试(对应 curl.h 中的CURL_DEPRECATED只是告警而非移除); - 相关接口速查:
CURLINFO_ACTIVESOCKET (3)、CURLOPT_CONNECT_ONLY (3)、curl_easy_getinfo (3)、curl_easy_setopt (3)——即原文档 See-also 一节列出的四个配套接口,前三个已有仓库内对应文档可直接延伸阅读。
小结
CURLINFO_LASTSOCKET是 libcurl 早期(7.15.2)为"连接建立与应用自定义数据收发解耦"提供的取值接口,与CURLOPT_CONNECT_ONLY构成了完整的连接提取模式。源码层面它通过Curl_getconnectinfo()借助lastconnect_id从连接池定位连接并返回 socket,无效时按文档约定返回-1。由于long在 64 位 Windows 上仅 32 位,该接口自 7.45.0 起弃用;新代码应统一改用类型安全(curl_socket_t)的CURLINFO_ACTIVESOCKET,迁移成本仅限于变量类型与无效值判定两处改动。
【免费下载链接】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),仅供参考