网络请求与诊断实战

系统讲解 curl 的进阶参数与 HTTP 请求方法、下载上传、代理与认证、重试容错,HTTP 状态码诊断,以及 DNS 解析与网络连通性排查的完整实践。

1. curl 基础与常见参数

一句话总结: curl 是「命令行 HTTP 客户端」,-s 静默、-i 带响应头、-o 落盘、-L 跟随跳转、-v 看全过程,五个参数覆盖八成的日常请求。

# 基本请求输出到 stdout
curl https://api.example.com/health

# 静默(无进度条)+ 响应体
curl -s https://api.example.com/health

# 带响应头查看
curl -si https://api.example.com/health

# 落盘保存
curl -so health.json https://api.example.com/health

# 跟随 301/302 跳转
curl -sL http://example.com

1.1 查看请求细节

# -v 打印握手、请求头、响应头全过程
curl -sv https://api.example.com/health

# 只打印响应头
curl -sI https://api.example.com/health

# 限时防止挂死
curl -s --max-time 10 https://api.example.com/health

一句话总结: 排查问题先 -v 看全貌、-I 只看头、--max-time 设超时,诊断脚本里这三样必备。

1.2 输出与静默的组合

# 丢弃响应体,只保留退出码用于判断
curl -so /dev/null -w '%{http_code}\n' https://api.example.com/health

# 输出统计信息
curl -s -o /dev/null -w '连接:%{time_connect} 首字节:%{time_starttransfer} 总:%{time_total}\n' \
  https://api.example.com/health

2. HTTP 请求方法与会话

一句话总结: -X 指定方法、-H 加请求头、-d 发表单、-F 发 multipart、-b/-c 管理 Cookie,curl 把浏览器做的事全部搬进脚本。

# GET 带查询参数
curl -sG --data-urlencode "q=shell 脚本" https://api.example.com/search

# POST JSON
curl -s -X POST -H 'Content-Type: application/json' \
  -d '{"name":"demo","size":10}' https://api.example.com/items

# PUT 更新
curl -s -X PUT -d '{"size":20}' https://api.example.com/items/1

2.1 表单与文件上传

# 普通表单(application/x-www-form-urlencoded)
curl -s -d 'user=alice&pass=secret' https://api.example.com/login

# multipart 文件上传
curl -s -F 'file=@/tmp/report.pdf' -F 'note=月度报表' \
  https://api.example.com/upload

# 自定义请求头
curl -s -H 'Accept: application/json' -H "Authorization: Bearer $TOKEN" \
  https://api.example.com/me

一句话总结: JSON 用 -H 'Content-Type: application/json' + -d,文件用 -F,鉴权头用 -H "Authorization: Bearer ..."——三类写法规整,脚本可读性就好。

# 登录拿 Cookie,后续请求复用
curl -s -c cookies.txt -d 'user=alice&pass=secret' https://api.example.com/login
curl -s -b cookies.txt https://api.example.com/profile

# 指定来源与 UA(防部分接口反爬校验)
curl -s -A 'Mozilla/5.0' -e 'https://example.com/' https://api.example.com/items

3. 下载与上传

一句话总结: -O 按远端文件名保存、-o 自定义文件名、-C - 断点续传、--limit-rate 限速,批量下载与镜像同步都靠它们。

# 按远端文件名保存
curl -sO https://example.com/file.zip

# 自定义文件名
curl -so app.tgz https://example.com/downloads/app-1.2.3.tgz

# 断点续传
curl -sC - -o app.tgz https://example.com/downloads/app-1.2.3.tgz

# 限速 512KB/s
curl -s --limit-rate 512k -o app.tgz https://example.com/app.tgz

3.1 多文件与镜像下载

# 批量下载(配合 xargs/parallel)
cat urls.txt | xargs -P 4 -I {} curl -sO '{}'

# 目录结构保持(wget 更擅长)
wget -r -np -nH https://example.com/docs/

# 校验下载完整性
curl -so pkg.tar.gz https://example.com/pkg.tar.gz
echo "$EXPECTED_SHA256  pkg.tar.gz" | sha256sum -c -

一句话总结: 单文件下载用 curl,递归镜像用 wget;下载后 sha256sum -c 校验,自动化里不能跳过完整性检查。

3.2 大文件与进度

# 显示进度条(非静默)
curl -# -O https://example.com/big.iso

# 断点续传 + 重试组合
curl -sC - --retry 5 --retry-delay 3 -o big.iso https://example.com/big.iso

4. 代理与认证

一句话总结: -x 指定 HTTP/SOCKS 代理、-U 代理认证、-u 基础认证、--cacert/-k 控制 TLS 校验,内网与抓包场景都靠它们打通。

# HTTP 代理
curl -s -x http://proxy.example.com:3128 https://api.example.com/health

# SOCKS5 代理
curl -s --socks5-hostname 127.0.0.1:1080 https://api.example.com/health

# 基础认证
curl -su 'user:pass' https://api.example.com/private

4.1 代理环境变量

# 全局代理环境变量
export https_proxy=http://proxy.example.com:3128
curl -s https://api.example.com/health    # 自动走代理

# 某次请求绕过代理
curl -s --noproxy '*' https://internal.example.com/health

一句话总结: https_proxy/http_proxy 环境变量被 curl 默认读取,脚本里按需 export 或 --noproxy '*' 绕过。

4.2 TLS 校验与 CA

# 信任自签名证书(测试环境)
curl -sk https://self-signed.example.com/health

# 使用自定义 CA 文件
curl -s --cacert /etc/ssl/certs/custom-ca.pem https://internal.example.com/health

# 指定客户端证书(mTLS)
curl -s --cert client.crt --key client.key https://api.example.com/secure

5. 重试与容错

一句话总结: --retry 重试次数、--retry-delay 间隔、--retry-all-errors 连 4xx 也重试、--max-time 单次超时,组合出健壮的请求模板。

# 网络错误重试 5 次,间隔 3 秒
curl -s --retry 5 --retry-delay 3 https://api.example.com/health

# 连接超时与总超时
curl -s --connect-timeout 5 --max-time 20 https://api.example.com/health

# 所有错误都重试(默认只重试瞬时错误)
curl -s --retry 5 --retry-all-errors https://api.example.com/health

5.1 状态码判断与自动重试

#!/usr/bin/env bash
set -euo pipefail

url="https://api.example.com/health"
for i in {1..5}; do
  code=$(curl -so /dev/null -w '%{http_code}' --max-time 10 "$url" || echo 000)
  if [[ "$code" == 200 ]]; then
    echo "健康检查通过"
    exit 0
  fi
  echo "尝试 $i/5 状态 $code"
  sleep 3
done
echo "健康检查失败" >&2
exit 1

一句话总结: curl 的网络错误(连接失败)会返回非零退出码,但 5xx 是「成功请求 + 非 200 状态码」——脚本要分别处理:重试前者,重试后者要看幂等性。

5.2 幂等与安全重试

# GET 可安全重试;POST 需业务幂等才重试
# 只读接口放心 --retry,写操作自行判断
curl -s --retry 3 https://api.example.com/health

# 把 curl 退出码做精细处理
curl -s -o /dev/null https://api.example.com/health
case $? in
  0) echo OK ;;
  6) echo "无法解析主机" ;;
  7) echo "连接被拒" ;;
  28) echo "超时" ;;
  *) echo "其他错误" ;;
esac

6. HTTP 状态诊断

一句话总结: 状态码 2xx 成功、3xx 跳转、4xx 客户端错、5xx 服务端错;-w '%{http_code}' 取码,-w 还能取重定向链与大小等指标。

# 只看状态码
curl -so /dev/null -w '%{http_code}\n' https://api.example.com/health

# 查看重定向链
curl -sI -L https://example.com/old-path
# 或
curl -s -o /dev/null -w '%{url_effective}\n' -L https://example.com/old-path

6.1 常见状态码速查

状态码含义常见原因
200成功正常响应
301/302永久/临时跳转路径变更、HTTP→HTTPS
401未认证缺 Token、认证失败
403禁止访问权限不足、被 WAF 拦截
404不存在路径拼错
429请求过多触发限流
500/502/503服务端错误应用崩溃、网关超时、过载
# 诊断 301 落到哪
curl -sIL https://example.com/old-path | grep -i '^HTTP/\|^Location:'

# 429 限流看响应头
curl -sI https://api.example.com/items | grep -i 'ratelimit'

一句话总结: 定位思路:4xx 先查请求本身(URL/头/权限),5xx 查服务端;301 用 -I -L 追链,429 看 Retry-After 头再退避重试。

6.2 响应体与错误区分

# JSON 接口错误看响应体
curl -s https://api.example.com/items | jq '.error // .message'

# 同时输出状态码与响应体
resp=$(curl -s -w '\n%{http_code}' https://api.example.com/items)
body=${resp%$'\n'*}; code=${resp##*$'\n'}
echo "code=$code body=$body"

7. DNS 与连通性排查

一句话总结: 排查顺序「DNS 解析 → TCP 连通 → TLS 握手 → HTTP 状态」四步走;dig/nslookup 查 DNS,ping 看基本连通,nc 探端口。

# DNS 解析
dig +short api.example.com
nslookup api.example.com

# 基本连通性
ping -c 3 api.example.com

# 端口连通性
nc -zv api.example.com 443

# 全链路:curl 分阶段计时
curl -sv https://api.example.com/health 2>&1 | grep -E 'Connected|TLS|HTTP/'

7.1 分层定位

现象第一步查命令
解析失败DNSdig +short、nslookup
不通路由/防火墙ping、traceroute
端口不通防火墙/服务未起nc -zv host port
连接建立但 TLS 失败证书/版本openssl s_client -connect host:443
HTTP 异常状态码/头curl -sI、-w '%{http_code}'
# TLS 层诊断
openssl s_client -connect api.example.com:443 -servername api.example.com \
  < /dev/null 2>&1 | grep -E 'subject|issuer|Verify'

# 端口探活
for port in 80 443 3306; do
  nc -zvw 3 api.example.com "$port" 2>&1
done

一句话总结: 「域名解析不出 → 网络不通 → 端口没开 → TLS 证书错 → HTTP 状态非 2xx」是按层排除的固定套路,命令从上往下逐个试。

7.2 脚本化健康检查

#!/usr/bin/env bash
set -euo pipefail

check_host() {
  local url="$1"
  local code
  code=$(curl -so /dev/null -w '%{http_code}' --max-time 10 "$url" || echo 000)
  echo "$url -> $code"
  [[ "$code" =~ ^2[0-9][0-9]$ ]]
}

for u in https://web.example.com/health https://api.example.com/health; do
  check_host "$u" || echo "!! $u 异常"
done

8. 总结

环节要点
基础参数-s 静默、-i/-I 响应头、-o 落盘、-L 跟随、--max-time 超时
方法与会话-X 方法、-H 头、-d/-F 表单、-b/-c Cookie
下载上传-O/-o 保存、-C - 续传、--limit-rate 限速、sha256 校验
代理认证-x 代理、--socks5、-u 认证、--cacert/-k TLS
重试容错--retry + --retry-delay + --retry-all-errors,退出码 6/7/28 语义
状态诊断2xx/3xx/4xx/5xx 分类,-w '%{http_code}' 取码,429 看 Retry-After
DNS/连通dig/nslookup/ping/nc/openssl 按层排除
脚本化-w '%{http_code}' 做健康检查,退出码 case 分支处理

curl 让脚本从「本地执行」跨入「与外部服务交互」:请求模板化、超时重试化、状态码判分化。诊断网络问题坚持「DNS → 连通 → TLS → HTTP」的分层套路。下一步是性能优化,把脚本从「能跑」推向「跑得快」。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「shell」更多文章

  1. 脚本性能优化实战
  2. 定时任务调度实战
  3. SSH 远程自动化实战