curl 与 libcurl 的 CURLOPT_UNIX_SOCKET_PATH:用 Unix 域套接字替代 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_UNIX_SOCKET_PATH 是 libcurl 提供的连接端点切换选项:设置后,libcurl 不再走 TCP/IP 网络栈,而是直接通过 Unix 域套接字(Unix Domain Socket,UDS)与本地服务通信,同时跳过 DNS 解析。本文基于 curl 官方文档 docs/libcurl/opts/CURLOPT_UNIX_SOCKET_PATH.md 展开,结合仓库源码与测试用例,讲清该选项的用法、限制、底层实现原理,以及命令行工具--unix-socket的对应实操,帮助你打通「本地进程间通信 + HTTP 语义」的开发场景。
一、选项速览:名称、原型与适用协议
| 属性 | 值 |
|---|---|
| 选项名 | CURLOPT_UNIX_SOCKET_PATH |
| 头文件 | <curl/curl.h> |
| 加入版本 | 7.40.0(仓库文档Added-in: 7.40.0) |
| 适用协议 | 全部(文档Protocol: All,源码层面受USE_UNIX_SOCKETS编译开关控制) |
| 默认值 | NULL,即默认不使用 Unix 域套接字 |
| 相关选项 | CURLOPT_ABSTRACT_UNIX_SOCKET、CURLOPT_OPENSOCKETFUNCTION、unix(7) |
函数原型:
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_UNIX_SOCKET_PATH, char *path);调用成功返回CURLE_OK (0),非零表示出错,具体错误码见libcurl-errors(3)。
二、核心语义:把「TCP 到主机」替换为「UDS 到路径」
启用该选项后,curl 的行为发生三个关键变化:
- 连接端点替换:curl 连接的是 Unix 域套接字(
path指向的套接字文件),而不是对 URL 中的主机发起 TCP 连接; - 跳过 DNS 解析:由于不再创建网络连接,URL 中的主机名不会被解析为 IP 地址。URL 的主机部分仅作为语义占位符(典型用法是
http://localhost/),实际数据从path指定的套接字收发; - 路径即一切:URL 中的路径部分仍然正常用于 HTTP 请求行与路由,服务端收到的请求与该套接字绑定的本地服务完全一致。
从仓库源码可以印证这一实现路径。在 lib/url.c 中,libcurl 建立连接目标(peer)时优先处理 UDS:
#ifdef USE_UNIX_SOCKETS /************************************************************* * Set UDS first. It overrides "via_peer" and proxy settings. *************************************************************/ if(network_scheme && CURL_EASY_STR(data, STRING_UNIX_SOCKET_PATH)) { result = Curl_peer_uds_create( needle->origin->scheme, CURL_EASY_STR(data, STRING_UNIX_SOCKET_PATH), (bool)data->set.abstract_unix_socket, &needle->via_peer); ...随后在 lib/url.c 中,一旦检测到连接目标是 UDS,就把传输方式标记为TRNSPRT_UNIX:
#ifdef USE_UNIX_SOCKETS if(Curl_conn_get_first_peer(needle, FIRSTSOCKET)->unix_socket) needle->transport_wanted = TRNSPRT_UNIX; #endifUDS 传输在套接字层面对应AF_UNIX。在 lib/cf-socket.c 中,地址族为AF_UNIX时直接输出sun_path(端口固定为 0),与AF_INET/AF_INET6走完全不同的分支:
#ifdef USE_UNIX_SOCKETS case AF_UNIX: if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { su = (struct sockaddr_un *)sa; curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); } else addr[0] = 0; /* socket with no name */ *port = 0; return CURLE_OK; #endifDNS 层的印证同样明确:lib/vdns/cf-dns.c 与 lib/vdns/hostip.c 中都有对peer->unix_socket的判断——当连接目标是 UDS 时,直接使用Curl_unix2addr把路径转换为地址,不再做主机名解析。
三、参数行为细节
3.1 传 NULL 即禁用
设置path为 NULL,则关闭 Unix 域套接字连接,恢复默认的 TCP 行为。
3.2 重复设置以后者为准
多次调用该选项时,最后一次设置的字符串覆盖之前的设置;再次设为 NULL 可彻底停用。
3.3 字符串生命周期
libcurl 内部会复制该字符串,应用程序不需要在调用curl_easy_setopt之后继续保留该字符串。对应的存储字段位于 lib/urldata.h(BIT(abstract_unix_socket)旁的有效字符串槽位),底层由Curl_setstropt统一管理:
#ifdef USE_UNIX_SOCKETS case CURLOPT_UNIX_SOCKET_PATH: >#ifndef CURL_DISABLE_PROXY /* Going via a unix socket ignores any proxy settings */ if(network_scheme && (!needle->via_peer || !needle->via_peer->unix_socket)) { result = Curl_proxy_init_conn(data, needle); ...此外从 lib/url.c 的注释「Set UDS first. It overrides 'via_peer' and proxy settings」可见,UDS 的优先级高于 "connect to"(CURLOPT_CONNECT_TO)与 alt-svc 带来的间接端点。
五、完整可运行示例
5.1 基础用法
#include <curl/curl.h> int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, "/tmp/httpd.sock"); curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/"); result = curl_easy_perform(curl); curl_easy_cleanup(curl); } return 0; }这段代码的含义:libcurl 建立到/tmp/httpd.sock的 UDS 连接,并在该连接上发送对http://localhost/的 HTTP 请求;localhost不会被解析。
5.2 突破 107 字节路径上限(Linux /proc 技巧)
如果你在 Linux 上确有超过 107 字节的套接字路径需求,可以利用/proc文件系统绕开限制:
int dirfd = open(long_directory_path_to_socket, O_DIRECTORY | O_RDONLY); char path[108]; snprintf(path, sizeof(path), "/proc/self/fd/%d/httpd.sock", dirfd); curl_easy_setopt(curl_handle, CURLOPT_UNIX_SOCKET_PATH, path); /* 务必在丢弃 handle 之前保持 dirfd 有效 */原理:/proc/self/fd/<fd>是短小的符号链接,指向真实的(很长的)目录路径,而sun_path中实际存储的只是这个短引用,从而绕开 108 字节限制。需要注意,dirfd必须在整个 handle 使用期间保持打开,否则符号链接失效。
六、命令行工具对应:--unix-socket
libcurl 选项在命令行工具中的映射为--unix-socket <path>(注册于 src/tool_getparam.c,帮助文本见 src/tool_listhelp.c):
curl --unix-socket /tmp/httpd.sock http://localhost/仓库的 HTTP 测试套件 tests/http/test_11_unix.py 覆盖了多种场景,可作为真实行为的验证依据:
- 通过 UDS 下载
http:资源(--unix-socket指向测试内创建的AF_UNIX监听套接字,见该文件第 59-60 行的socket.socket(socket.AF_UNIX, ...)与bind); - 通过 UDS 下载
https:资源; - 通过 UDS 下载 HTTP/3(HTTP/3 over UDS,
h3场景); - 通过 UDS 下载时忽略代理参数——测试在第 136-142 行同时传入
--proxy参数,验证代理被忽略,与文档「proxy 不生效」的说明完全一致。
配套的测试数据文件还包括 tests/data/test1268、tests/data/test1435、tests/data/test1436,服务端对 UDS 的支持见 tests/server/sws.c。
七、结合源码的深入理解
7.1 连接目标模型:peer 结构
现代 libcurl 用struct Curl_peer(lib/peer.h)抽象「连接实际谈话的对象」。其中:
BIT(unix_socket); /* hostname is a UDS path without the prefix */ BIT(abstract_uds); /* only TRUE when `unix_socket` also TRUE */(见 lib/peer.h)。UDS 路径被当作 peer 的hostname字段存储,这在连接复用(connection reuse)判断中起作用——lib/peer.c 的 peer 相等性比较包含unix_socket标志,意味着指向同一 UDS 路径的请求才能复用同一连接。
7.2 UDS peer 的创建
lib/peer.c 的Curl_peer_uds_create把路径填入 peer 的 hostname,并标记unix_socket = TRUE;空路径会返回CURLE_FAILED_INIT。而 lib/url.c 还做了一个优化:当 origin 与 via_peer 相等时解引用,避免冗余。
7.3 与抽象套接字的关系
本选项设置的是文件系统路径型UDS;若要使用 Linux 抽象命名空间(sun_path以\0开头、无文件系统实体),请使用CURLOPT_ABSTRACT_UNIX_SOCKET。两者共享同一个字符串存储槽(见 lib/setopt.c),区别仅在abstract_unix_socket标志位——该标志最终传入Curl_peer_uds_create(lib/url.c),并在解析时由Curl_unix2addr决定是否走抽象地址(lib/vdns/hostip.c)。
7.4 编译前提
该功能在编译时受USE_UNIX_SOCKETS宏控制(lib/setopt.c、lib/url.c 等处的条件编译)。主流 Unix 系平台(Linux、BSD、macOS)默认开启;在不支持 AF_UNIX 的平台上,该选项不会出现于curl_easy_setopt的分支中,调用将返回CURLE_UNKNOWN_OPTION。
八、典型应用场景
- 本地服务加速:通过 nginx/服务网格 sidecar 暴露的 Unix 套接字访问本机 HTTP 服务,省去 TCP 三次握手与环回接口开销;
- 权限控制:UDS 文件可以设置严格的文件权限,实现「仅特定用户可访问」的进程间通信边界;
- 简化部署:容器内 sidecar 与主进程间用 UDS 通信时,无需为每个实例分配端口,避免端口冲突与监听地址配置;
- 测试与 CI:如 tests/http/test_11_unix.py 所示,测试服务器绑定 UDS 后,客户端用
--unix-socket连接,天然隔离不同测试进程,避免 TCP 端口竞争。
结语
CURLOPT_UNIX_SOCKET_PATH 是 libcurl 中「本地、快速、可鉴权」的连接方式:它把 TCP 传输替换为 Unix 域套接字传输,跳过 DNS,忽略 TCP/代理选项,路径长度限制为 107 字节(可用 /proc 技巧绕过)。无论是通过 C API 编程,还是直接使用curl --unix-socket命令行,理解其连接语义与限制,都能让你在本地进程通信与 HTTP 客户端之间搭建起稳定高效的通路。
【免费下载链接】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),仅供参考