HTTP 与 SOCKS5 代理到底差在哪?用 Node.js 跑一次连接链路对比

HTTP 与 SOCKS5 代理到底差在哪?用 Node.js 跑一次连接链路对比

摘要:HTTP 代理和 SOCKS5 代理的差别不只是一段地址的协议前缀。它们处理连接的层次、目标域名的解析位置、认证失败的表现和适用协议都不同。本文用 curl 与 Node.js 做一组可复现对照,并整理一套排错顺序。

同一个 HTTPS 接口,直连可以返回,切到 HTTP 代理后出现 407,换成 SOCKS5 又报 ENOTFOUND。如果只把这三种错误统称为"代理不可用",排查很容易走偏。

先给结论:

  • HTTP 代理理解 HTTP 语义;访问 HTTPS 目标时,客户端通常先发送 CONNECT host:443 建立隧道,再在隧道里完成 TLS 握手。
  • SOCKS5 更接近通用的会话转发协议,不局限于 HTTP 请求。
  • SOCKS5 使用目标主机名时,域名可以在客户端解析,也可以交给代理端解析;具体取决于客户端配置。
  • 判断一种代理是否"适合",至少要同时看目标协议、DNS 解析位置、认证方式和客户端库支持,不能只比较一次请求的总耗时。

下面不比较供应商,也不讨论抽象的"速度快不快",只看一条请求真正经过了什么。

先画清两条连接路径

以访问 https://example.com/ 为例,HTTP 代理的常见路径是:

text 复制代码
Node.js 客户端
    │
    │  TCP 连接到代理
    ▼
HTTP 代理
    │
    │  CONNECT example.com:443
    │  ← 200 Connection Established
    ▼
到目标站的 TCP 隧道
    │
    │  TLS 握手 + HTTPS 请求
    ▼
example.com:443

这里有两个容易混淆的连接:客户端先连代理,代理再连目标站。407 Proxy Authentication Required 出现在代理认证阶段,还没有进入目标站业务。

SOCKS5 的路径不同:

text 复制代码
Node.js 客户端
    │
    │  TCP 连接到 SOCKS5 代理
    │  协商认证方式
    │  请求连接 example.com:443
    ▼
SOCKS5 代理
    │
    │  建立到目标站的 TCP 连接
    ▼
example.com:443

SOCKS5 不需要理解后续是不是 HTTP。隧道建立以后,里面可以传输 TLS 和 HTTPS,也可以承载其他基于 TCP 的协议。是否支持 UDP 转发,则要同时看协议能力、客户端库和代理服务端实现,不能看到 "SOCKS5" 就默认可用。

最小复现:先用 curl 把问题分层

不要一上来改业务代码。curl 的详细日志更适合确认连接到底停在哪一步。

1. HTTPS 经 HTTP 代理

bash 复制代码
curl -v \
  --proxy "http://YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
  --proxy-user "YOUR_PROXY_USER:YOUR_PROXY_PASSWORD" \
  --connect-timeout 5 \
  --max-time 15 \
  "https://example.com/"

重点观察:

  • 是否成功连接代理地址;
  • 是否出现 CONNECT example.com:443
  • 代理返回的是 200407,还是直接断开;
  • CONNECT 成功后,TLS 握手是否继续;
  • 最终 HTTP 状态码来自代理还是目标站。

不要把密码直接写进脚本仓库。上面的命令只是字段示意,实际运行可从安全的环境变量或凭据管理工具读取。

2. SOCKS5:本地解析目标域名

bash 复制代码
curl -v \
  --socks5 "YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
  --proxy-user "YOUR_PROXY_USER:YOUR_PROXY_PASSWORD" \
  --connect-timeout 5 \
  --max-time 15 \
  "https://example.com/"

curl 官方手册明确区分了 --socks5--socks5-hostname:前者由 curl 所在机器解析目标域名,然后把地址交给代理。

如果本地 DNS 无法解析目标域名,请求甚至还没进入目标连接阶段。这类失败不能简单归因于代理出口。

3. SOCKS5:代理端解析目标域名

bash 复制代码
curl -v \
  --socks5-hostname "YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
  --proxy-user "YOUR_PROXY_USER:YOUR_PROXY_PASSWORD" \
  --connect-timeout 5 \
  --max-time 15 \
  "https://example.com/"

也可以使用等价的代理 URL:

bash 复制代码
curl -v \
  --proxy "socks5h://YOUR_PROXY_USER:YOUR_PROXY_PASSWORD@YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
  "https://example.com/"

socks5h 中的 h 可以记成 hostname:目标主机名交给代理端解析。注意这里只是改变目标域名的解析位置,代理服务器自身的域名仍然需要客户端能够找到。

用 curl 记录分阶段耗时

-v 适合看过程,--write-out 更适合做多次对照。下面的指标均是从请求开始累计到某一阶段的时间,并不是互相独立的耗时:

bash 复制代码
curl --silent --show-error --output NUL \
  --proxy "http://YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
  --connect-timeout 5 \
  --max-time 15 \
  --write-out "dns=%{time_namelookup}s connect=%{time_connect}s tls=%{time_appconnect}s ttfb=%{time_starttransfer}s total=%{time_total}s code=%{http_code}\n" \
  "https://example.com/"

Windows PowerShell 中可使用 NUL;Linux 和 macOS 改为 /dev/null

字段含义:

字段 表示什么 解读时的限制
time_namelookup curl 完成名称解析的累计时间 可能主要反映代理地址解析,不一定等于目标域名解析
time_connect 完成到远端 TCP 连接的累计时间 使用代理时,远端通常先指代理
time_appconnect 完成 TLS 等应用层握手的累计时间 包含此前 DNS、TCP、代理协商等时间
time_starttransfer 收到首字节的累计时间 还包含服务端处理时间
time_total 本次操作总时间 不能单独说明慢在哪一层

因此,tls - connect 可以作为观察差值,但不能在所有代理实现和连接复用场景中机械地等同于"纯 TLS 耗时"。需要严格压测时,还要固定 curl 版本、连接复用方式、目标接口和采样次数。

Node.js 中做同一组请求

下面使用 Node.js 20+,以及两个常用 Agent:

bash 复制代码
npm install https-proxy-agent socks-proxy-agent

保存为 proxy-compare.mjs

js 复制代码
import https from 'node:https';
import { performance } from 'node:perf_hooks';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { SocksProxyAgent } from 'socks-proxy-agent';

const target = process.env.TARGET_URL ?? 'https://example.com/';
const httpProxy = process.env.HTTP_PROXY_URL;
const socksProxy = process.env.SOCKS_PROXY_URL;

function requestOnce(label, agent) {
  return new Promise((resolve) => {
    const startedAt = performance.now();

    const req = https.get(target, { agent, timeout: 10_000 }, (res) => {
      let bytes = 0;
      res.on('data', (chunk) => { bytes += chunk.length; });
      res.on('end', () => resolve({
        label,
        ok: true,
        status: res.statusCode,
        bytes,
        totalMs: Math.round((performance.now() - startedAt) * 10) / 10,
      }));
    });

    req.on('timeout', () => req.destroy(new Error('request timeout')));
    req.on('error', (error) => resolve({
      label,
      ok: false,
      code: error.code ?? 'UNKNOWN',
      message: error.message,
      totalMs: Math.round((performance.now() - startedAt) * 10) / 10,
    }));
  });
}

const jobs = [];

if (httpProxy) {
  jobs.push(requestOnce(
    'http-connect',
    new HttpsProxyAgent(httpProxy),
  ));
}

if (socksProxy) {
  jobs.push(requestOnce(
    'socks5',
    new SocksProxyAgent(socksProxy),
  ));
}

if (jobs.length === 0) {
  console.error('Set HTTP_PROXY_URL and/or SOCKS_PROXY_URL first.');
  process.exitCode = 2;
} else {
  console.table(await Promise.all(jobs));
}

PowerShell 运行示例:

powershell 复制代码
$env:TARGET_URL = 'https://example.com/'
$env:HTTP_PROXY_URL = 'http://YOUR_PROXY_USER:YOUR_PROXY_PASSWORD@YOUR_PROXY_HOST:YOUR_PROXY_PORT'
$env:SOCKS_PROXY_URL = 'socks5h://YOUR_PROXY_USER:YOUR_PROXY_PASSWORD@YOUR_PROXY_HOST:YOUR_PROXY_PORT'
node .\proxy-compare.mjs

Linux/macOS 可写成:

bash 复制代码
TARGET_URL='https://example.com/' \
HTTP_PROXY_URL='http://YOUR_PROXY_USER:YOUR_PROXY_PASSWORD@YOUR_PROXY_HOST:YOUR_PROXY_PORT' \
SOCKS_PROXY_URL='socks5h://YOUR_PROXY_USER:YOUR_PROXY_PASSWORD@YOUR_PROXY_HOST:YOUR_PROXY_PORT' \
node proxy-compare.mjs

下面是格式演示,不是真实线路测试结果:

text 复制代码
(示例输出)
┌─────────┬────────────────┬──────┬────────┬───────┬─────────┐
│ (index) │ label          │ ok   │ status │ bytes │ totalMs │
├─────────┼────────────────┼──────┼────────┼───────┼─────────┤
│ 0       │ http-connect   │ true │ 200    │ 1256  │ 418.7   │
│ 1       │ socks5         │ true │ 200    │ 1256  │ 463.2   │
└─────────┴────────────────┴──────┴────────┴───────┴─────────┘

这段 Node.js 代码只报告端到端耗时。它适合验证"相同目标和相同运行环境下,请求能否完成、返回什么状态、总共用了多久",不把内部阶段包装成精确测量。需要定位 DNS、TCP、TLS 和 TTFB 时,回到前面的 curl 输出更清楚。

三类常见错误,分别查什么

407 Proxy Authentication Required

这通常来自 HTTP 代理,表示代理已经收到请求,但认证信息缺失或不被接受。优先检查:

  1. 用户名与密码是否进行了正确的 URL 编码;
  2. 代理是否接受当前认证方式;
  3. 凭据是否绑定来源地址或使用范围;
  4. 代码是否真的把认证信息传给 Agent。

不要把目标 API 的 401 和代理的 407 混在一起。前者通常是目标服务认证,后者是代理认证。

ENOTFOUND 或 curl 退出码 6

这类错误与域名解析有关,但先要确认解析的是谁:

  • 代理服务器域名解析失败:客户端连代理都做不到;
  • SOCKS5 本地解析模式下目标域名失败:可尝试对照 socks5h
  • 代理端解析目标域名失败:要检查代理侧 DNS,而不是继续修改本机 hosts。

ETIMEDOUT、curl 退出码 28

"超时"只是结果,不是原因。把它拆成:

  • 连接代理超时;
  • 代理连接目标超时;
  • TLS 握手迟迟没有完成;
  • 已建立连接,但首字节或响应体读取超时。

curl 的累计阶段时间和 -v 日志结合起来,通常比只看 Node.js 最终错误更有效。

HTTP 还是 SOCKS5:用任务条件判断

条件 HTTP 代理 SOCKS5
主要访问 HTTP/HTTPS API 客户端生态成熟,通常容易接入 可以使用,但要确认库支持
需要代理理解或处理 HTTP 更合适 不负责 HTTP 语义
需要承载非 HTTP 的 TCP 协议 能力取决于 CONNECT 目标与代理策略 通常更通用
需要明确控制目标 DNS 解析位置 取决于 HTTP 客户端和代理实现 socks5/socks5h 通常更直观
排查代理认证 看 HTTP 状态和 Proxy-Authenticate 看 SOCKS 协商或客户端错误
客户端只支持 HTTP Agent 接入成本低 需要额外 Agent 或 transport

实际选择时,顺序应该是:

  1. 客户端要访问什么协议;
  2. 当前库原生支持什么;
  3. 目标域名应该在哪里解析;
  4. 认证与凭据如何安全注入;
  5. 失败时能否拿到足够日志;
  6. 最后才比较同一测试条件下的性能。

几个容易踩的坑

第一,不要看到 HTTPS 目标就把代理 URL 也机械写成 https://。目标使用 HTTPS,不代表代理入口本身一定使用 HTTPS;代理提供方给出的协议和端口才是依据。

第二,不要把 socks5://socks5h:// 当成完全相同。至少在 curl 的语义里,前者本地解析目标域名,后者交给代理解析。

第三,不要用一次请求决定哪种协议更快。DNS 缓存、TLS 会话、连接复用、目标服务负载和代理节点状态都会影响结果。至少进行多轮采样,并记录失败率与分位数。

第四,不要在日志里完整打印代理 URL。URL 中可能包含用户名和密码,报错上报前需要脱敏。

复盘清单

遇到"代理请求失败"时,可以依次确认:

  • 客户端能否解析并连接代理服务器;
  • 代理协议与端口是否匹配;
  • 认证失败发生在代理还是目标服务;
  • HTTPS 经 HTTP 代理时,CONNECT 是否成功;
  • SOCKS5 的目标域名是在本地还是代理端解析;
  • 超时发生在连接、TLS、首字节还是响应读取阶段;
  • 运行日志是否已去除凭据;
  • 对比测试是否固定了目标、超时、并发和采样次数。

HTTP 与 SOCKS5 没有脱离场景的绝对优劣。真正值得保留的不是"哪一个更快"的单次结果,而是一套能把 DNS、连接、认证、握手和响应分开的复现方法。

参考资料

  • curl 官方手册:https://curl.se/docs/manpage.html
  • Node.js Performance Hooks:https://nodejs.org/api/perf_hooks.html
  • https-proxy-agenthttps://github.com/TooTallNate/proxy-agents/tree/main/packages/https-proxy-agent
  • socks-proxy-agenthttps://github.com/TooTallNate/proxy-agents/tree/main/packages/socks-proxy-agent
相关推荐
小小龙学IT2 小时前
gRPC 开源高性能 RPC 框架深度解析:从 HTTP/2 到跨语言微服务实战
http·rpc·开源
Misnearch1 天前
MCP以及底层协议、传输模式
http·mcp·json-rpc
Aision_2 天前
实习学习笔记:HTTP请求走私漏洞全方位解析
笔记·学习·安全·web安全·http·网络安全·网络攻击模型
zhao3266857512 天前
除了网页浏览,HTTP和HTTPS代理还能干啥?适用场景有哪些
网络协议·http·https
沐苏瑶2 天前
计算机网络核心笔记:打通 TCP/UDP 与 HTTP/HTTPS 底层逻辑(重点下)
笔记·计算机网络·http
NeilYuen3 天前
实现TCP发送HTTP请求
网络协议·tcp/ip·http
wuhuhuan3 天前
Day17:HTTP 接口测试与链式调用 — sendRequest 从入门到实战
网络·网络协议·http
执笔画流年呀3 天前
网络原理(http)(https)
网络·http·https
llllll1523 天前
HTTP协议全解:从报文结构到HTTPS加密原理
网络协议·http·https