大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
网络协议
这一课你将能做到
- 说清楚一个 HTTP 请求从浏览器到 Nginx 再到 Spring Boot 的全链路过程
- 用 tcpdump、curl、ss 等命令抓包诊断网络问题
- 配置 HTTPS(TLS 证书)并理解握手过程,能把 HTTP 301 到 HTTPS
为什么要学
你的 Spring Boot 应用、MySQL、Redis、Nginx 跑在网络上,用户请求跨越公网、VPC、内网才能到达你的代码。网络不通?SSL 证书报错?连接超时?DNS 解析失败?不会抓包和看协议的 DevOps 等于蒙眼修车。
人话:网络协议是「两台电脑如何对话」的规则。HTTP 是浏览器和服务器之间的对话格式,TCP 是「保证对方一定收到」的快递单号机制,DNS 是把域名翻译成 IP 地址的电话簿。理解这些,排查「为什么连不上」才有方向。
先建立图像
一个完整的请求链路:
用户浏览器
│ https://www.codeforge.com/api/users
▼
DNS 解析 → 8.8.8.8 / 114.114.114.114
│ www.codeforge.com → 47.96.123.45
▼
公网 → 云服务器安全组(443 放行)→ 服务器 443 端口
│ Nginx 监听 443,做 TLS 握手
▼
Nginx
│ 反向代理 → proxy_pass http://127.0.0.1:8080
▼
Spring Boot(8080)
│ 查缓存(Redis:6379)或查数据库(MySQL:3306)
▼
JSON 响应
TCP 三次握手(建立连接)与四次挥手(断开连接):
三次握手(为了确认双方收发都正常):
客户端 → SYN → 服务端
服务端 → SYN+ACK → 客户端
客户端 → ACK → 服务端
四次挥手(双方各自收拾干净再挂电话):
客户端 → FIN → 服务端
服务端 → ACK → 客户端
服务端 → FIN → 客户端
客户端 → ACK → 服务端
跟着做
步骤 1:在服务器上确认网络拓扑
# 本机 IP 和网卡
ip addr
# 路由表:包往哪走
ip route
# DNS 配置
cat /etc/resolv.conf
# 域名解析过程追踪
dig www.codeforge.com +trace
步骤 2:确认端口监听和连接状态
# 四连问
ss -tlnp # TCP 监听端口(LISTEN)
ss -tan state established # 已建立连接
ss -s # 连接数统计
netstat -i # 网卡流量统计
连接状态的含义:
LISTEN 服务在等连接
ESTABLISHED 双方正在通信
TIME_WAIT 连接已断开,等最后确认(2MSL,约 60 秒)
CLOSE_WAIT 对方已关,我方还没关(通常是我方程序 bug)
SYN_SENT 正在发 SYN 等回应(可能连不上目标)
步骤 3:用 curl 模拟请求,调试 HTTP
# 基础 GET
curl -v https://api.codeforge.com/users?page=1
# -v 会打印完整握手和响应头,调试必备
# POST JSON
curl -X POST https://api.codeforge.com/users \
-H "Content-Type: application/json" \
-d '{"name":"alice"}' \
-w "\nHTTP_CODE: %{http_code}\nTIME: %{time_total}s\n"
# 只看响应头和状态码(不下载 body)
curl -I -s -o /dev/null -w "%{http_code}" https://api.codeforge.com/health
步骤 4:抓包分析——tcpdump 实战
# 抓 80 端口的包,保存到文件(Wireshark 能打开分析)
sudo tcpdump -i any port 80 -w /tmp/http.pcap
# 实时看 443 端口的握手过程(不存文件)
sudo tcpdump -i any port 443 -nn -X | head -30
# 只抓某个 IP 到本机 8080 的包
sudo tcpdump host 1.2.3.4 and port 8080
抓到包后,用 Wireshark 打开 .pcap 文件,能看到 TCP 握手、TLS 握手、HTTP 请求/响应的完整过程。
步骤 5:配置 HTTPS(Let's Encrypt 免费证书)
# 安装 certbot(以 Ubuntu 为例)
sudo apt install certbot python3-certbot-nginx
# 一键申请并自动配置 Nginx
sudo certbot --nginx -d www.codeforge.com -d codeforge.com
# 测试自动续期(证书 90 天有效)
sudo certbot renew --dry-run
申请完后 Nginx 配置会变成这样:
server {
listen 443 ssl;
server_name www.codeforge.com;
ssl_certificate /etc/letsencrypt/live/www.codeforge.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.codeforge.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 80;
server_name www.codeforge.com;
return 301 https://$server_name$request_uri; # HTTP 强制跳转 HTTPS
}
讲清楚要点
- TCP 是可靠传输,UDP 是快速传输:Spring Boot 的 HTTP 走 TCP(数据不会丢),DNS 查询默认走 UDP(快,丢了重发一次就行)。
- 三次握手的本质:防止历史重复连接导致的混乱,同时让双方确认「自己发得出去,也收得到」。
- HTTPS = HTTP + TLS:TLS(旧称 SSL)在 TCP 连接之上做加密握手,交换对称密钥,之后所有 HTTP 数据都是密文。证书(Certificate)解决「你是不是真服务器」的信任问题。
- 301 vs 302:301 是永久重定向(浏览器会缓存,下次直接走新地址),302 是临时重定向。HTTP 跳转 HTTPS 用 301。
- 连接池是性能关键:Spring Boot 连接 MySQL、Redis,Nginx 连接后端,都有连接池。TCP 连接建立成本不低,频繁新建/断开会拖慢性能。
ss -s看到TIME_WAIT过多,通常是短连接风暴。
你可能会懵的地方
-
为什么
TIME_WAIT这么多? TCP 协议规定主动关闭方要等 2MSL(约 60 秒)才能彻底释放端口,防止最后的 ACK 丢失导致对方重发 FIN 没人收。高并发短连接场景(如爬虫)下会大量堆积,解决方案是改用连接池、或调内核参数net.ipv4.tcp_tw_reuse。 -
127.0.0.1、0.0.0.0、外网 IP 监听有什么区别?127.0.0.1只接受本机连接(MySQL 默认这样,安全);0.0.0.0接受所有网卡上的连接(Nginx 和 Spring Boot 通常这样);外网 IP 明确绑定某张网卡。绑定错了就是「明明服务在跑,外面却连不上」的元凶。 -
端口被占用了怎么办?
ss -tlnp | grep 8080找到占用的 PID →ps aux | grep <pid>确认是谁 → 决定杀进程还是改端口。Java 进程常见:上次没正常退出,旧进程还在监听。 -
ping 能通但服务访问不了? ping 走 ICMP 协议,只证明网络层通;服务访问不了可能是:防火墙/安全组没放行端口、服务没监听、服务绑定了 127.0.0.1、Nginx 配错了 upstream。
-
为什么证书配好了浏览器还报不安全? 常见原因:证书链不完整(缺少中间证书)、域名不匹配(证书是
*.example.com但访问的是sub.sub.example.com)、证书过期。用openssl s_client -connect host:443 -servername host检查。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| MySQL 绑定 0.0.0.0 且安全组放行 3306 | 数据库暴露公网,极危险 | 绑定 127.0.0.1,或走内网/VPC |
| 用 IP 而非域名访问服务 | 换服务器要全改配置 | 用域名 + DNS,换 IP 只改解析 |
| HTTP 200 就认为服务健康 | 可能返回的是错误页 | 健康接口查业务状态,不只是 TCP 通 |
| 防火墙全放行 | 等于没防火墙 | 最小端口开放,用安全组精确控制 |
| 证书过期没续期 | 全站 HTTPS 报错 | certbot 自动续期 + 监控告警 |
卡点急救
- curl 报错
Connection refused:目标端口没进程监听 →ss -tlnp | grep 端口确认;或被防火墙拦 → 检查iptables -L和云安全组。 - curl 报错
SSL certificate problem:服务器证书链不完整、或自签证书不被信任。测试时加-k跳过验证(仅测试),生产环境必须配合法证书。 - 服务间歇性超时:
ss -tan | grep CLOSE_WAIT | wc -l,如果几百个 CLOSE_WAIT,说明服务端代码没正确关闭连接(连接泄漏),Java 里常见于没关 InputStream/ResultSet。
面试常问
-
输入 URL 到页面显示,发生了什么? 按顺序讲:DNS 解析(缓存→递归查询)→ TCP 三次握手 → TLS 握手(HTTPS)→ HTTP 请求 → Nginx 处理 → 反向代理到 Spring Boot → 业务逻辑 → 查 Redis/MySQL → 组装响应 → HTTP 响应 → 浏览器渲染。能讲到这个粒度,网络基础就过关了。
-
TCP 三次握手能不能改成两次? 不能。两次握手只能确认「客户端能发、服务端能收」,但无法确认「服务端能发、客户端能收」。另外,历史过期连接如果只有两次握手,会在服务端错误建立连接。
-
HTTPS 和 HTTP 的区别?TLS 握手过程? HTTPS 在 HTTP 之下加了 TLS 加密层。握手过程简版:①客户端发支持的加密套件和随机数;②服务端返回证书+随机数;③客户端验证书,生成预主密钥用公钥加密发给服务端;④双方用三个随机数算出会话密钥;⑤之后所有通信用对称加密。
curl -v https://...能看到部分握手细节。
自测三问
ss -tlnp里的t、l、n、p分别代表什么?输出中0.0.0.0:80和127.0.0.1:3306的安全含义有何不同?- 你 curl 一个接口返回 502 Bad Gateway,下一步排查的三条命令是什么?
- 为什么生产环境必须把 HTTP 301 到 HTTPS,而不是让用户自己输入 https://?
小结一句话
网络协议是 DevOps 的「血管系统」:理解 DNS、TCP、HTTP、TLS 的请求链路,才能快速定位「连不上、慢、不安全」的问题。
延伸
- 图解 HTTP(豆瓣链接)
- 下一课:Docker——把你的应用和环境打包成一个可移植的集装箱。