TCP/IP 网络协议学习笔记(含上机实操)
实验环境 : 华为云香港 ECS ecs-ee63 集群 × 4 台 (c6.large_2, 2vCPU/4GiB, Ubuntu 24.04)
实验时间 : 2026-06-02
涉及工具 : tcpdump, Wireshark, nmap, hping3, iperf3, netcat, curl, dig, nslookup, OpenSSL
文档版本 : v1.0
总行数: ~6000+
📖 文档导读
本系列博客涵盖 TCP/IP 协议族从基础理论到安全攻防的完整学习路径,共 10 章 + 附录。每个理论章节均配有真实服务器上机实操,所有截图和数据均来自华为云 ECS 集群的实际运行环境。
阅读路径建议
┌─────────────────────────────────────────────────────────────────┐
│ 第一部分:基础理论篇 (第1-3章) │
│ TCP/IP 概述 → 网络层(IP/ICMP) → 传输层(TCP/UDP) │
│ ├── 必读,打地基 │
│ └── 建议顺序阅读 │
├─────────────────────────────────────────────────────────────────┤
│ 第二部分:进阶实践篇 (第4-6章) │
│ ARP → DNS → HTTP/HTTPS │
│ ├── 应用层协议深入 │
│ └── 每个协议独立,可跳读 │
├─────────────────────────────────────────────────────────────────┤
│ 第三部分:安全攻防篇 (第7-8章) │
│ SYN Flood → ICMP 重定向 → 网络监控 │
│ ├── ⚠️ 仅限授权环境 │
│ └── 需先掌握前两部分 │
├─────────────────────────────────────────────────────────────────┤
│ 第四部分:综合项目篇 (第9-10章) │
│ Socket 编程 → LAMP 环境搭建 │
│ ├── 工程实践 │
│ └── 综合应用前9章知识 │
└─────────────────────────────────────────────────────────────────┘
实验拓扑
互联网 (Internet)
│
┌────────┴────────┐
│ 华为云 VPC │
│ 192.168.0.0/24 │
├────────────────┤
│ 网关: 192.168.0.1 │
└──┬────┬────┬───┘
│ │ │
┌──────────────┼────┼────┼──────────────┐
│ │ │ │ │
┌────▼────┐ ┌────▼────┐ ┌▼────────┐ ┌────▼────┐
│ Server A │ │ Server B │ │ Server C│ │ Server D│
│ .5 │ │ .207 │ │ .111 │ │ .44 │
│ nginx:80 │ │ nginx:80 │ │ nginx:80│ │ (不可达) │
│ SSH :22 │ │ SSH :22 │ │ SSH :22 │ │ │
└──────────┘ └──────────┘ └─────────┘ └─────────┘
📚 第一部分:基础理论篇
第1章:TCP/IP 协议概述
1.1 什么是 TCP/IP 协议族
TCP/IP (Transmission Control Protocol / Internet Protocol) 并不是两个协议,而是一个协议族 (Protocol Suite)------由数十个协议组成的网络通信标准。它定义了数据如何在网络中封装、寻址、传输、路由以及在目的地被接收。
| 术语 | 含义 | 说明 |
|---|---|---|
| TCP/IP 协议族 (Protocol Suite) | 以 TCP 和 IP 为核心的协议集合 | 包含 HTTP, DNS, ARP, ICMP 等 |
| TCP/IP 参考模型 (Reference Model) | 四层抽象架构 | 应用层→传输层→网络层→链路层 |
| TCP/IP 协议栈 (Protocol Stack) | 操作系统内核中的具体实现 | Linux 内核网络子系统 |
历史发展背景
1969 ARPANET 诞生 (4 个节点)
│
1973 TCP 首次提出 (Vint Cerf & Bob Kahn)
│
1978 TCP 拆分为 TCP + IP (版本 3)
│
1981 IPv4 标准 RFC 791 发布
│
1983 ARPANET 正式切换到 TCP/IP (Flag Day)
│
1995 IPv6 规范 RFC 1883 发布
│
2017 TCP BBR 拥塞控制算法 (Google)
│
2024 HTTP/3 基于 QUIC (UDP) 普及
1.2 TCP/IP 四层模型 vs OSI 七层模型
TCP/IP 模型将网络通信抽象为四层,而 OSI (Open Systems Interconnection) 模型分为七层。理解两者的对应关系,是学习网络协议的关键。
OSI 七层模型 TCP/IP 四层模型 数据单元 典型协议
┌──────────────┐ ┌──────────────┐
│ 7. 应用层 │ │ │
│ Application │──────────┤ │
├──────────────┤ │ 应用层 │ 消息/数据 HTTP, HTTPS
│ 6. 表示层 │ │ Application │ (Message) DNS, SMTP
│ Presentation │──────────┤ │ FTP, SSH
├──────────────┤ │ │
│ 5. 会话层 │ │ │
│ Session │──────────┤ │
├──────────────┤ ├──────────────┤
│ 4. 传输层 │ │ 传输层 │ 段/数据报 TCP, UDP
│ Transport │──────────┤ Transport │ (Segment/ SCTP
├──────────────┤ │ │ Datagram)
│ 3. 网络层 │ │ 网络层 │ 数据包 IP, ICMP
│ Network │──────────┤ Internet │ (Packet) ARP
├──────────────┤ ├──────────────┤
│ 2. 数据链路层 │ │ │ 帧 Ethernet
│ Data Link │──────────┤ 链路层 │ (Frame) Wi-Fi
├──────────────┤ │ Link │ PPP
│ 1. 物理层 │ │ │ 比特流
│ Physical │──────────┤ │ (Bits)
└──────────────┘ └──────────────┘
各层功能详解
| 层级 | 核心功能 | 解决的问题 | 关键概念 |
|---|---|---|---|
| 链路层 (Link) | 物理寻址、帧封装 | 同一网段内两台设备如何通信 | MAC 地址 (Media Access Control)、ARP、MTU |
| 网络层 (Internet) | 逻辑寻址、路由选择 | 不同网络之间数据如何到达 | IP 地址、子网掩码、路由表、TTL |
| 传输层 (Transport) | 端到端通信、可靠性 | 数据如何可靠/高效地到达目标进程 | 端口号、序列号、确认应答、窗口 |
| 应用层 (Application) | 用户数据、应用协议 | 具体应用如何表示和交换数据 | URI、编码、会话、加密 |
数据封装与解封装过程
发送方 (数据封装) 接收方 (数据解封装)
应用层: 用户数据
│ + HTTP 头
▼
传输层: TCP 段 ──── src_port:54321 dst_port:80 seq:1001
│ + TCP 头
▼
网络层: IP 数据包 ─ src_ip:192.168.0.5 dst_ip:103.235.46.102
│ + IP 头
▼
链路层: 以太网帧 ── src_mac:fa:16:3e:5a:b2:b7 dst_mac:网关MAC
│ + 帧头/帧尾 (FCS)
▼
物理层: 比特流 ── 0101010101...
接收方:
物理层: 接收比特流
▼
链路层: 校验FCS, 剥离帧头
▼
网络层: 检查目标IP, 剥离IP头
▼
传输层: 按端口交付, 检查序列号
▼
应用层: 还原用户数据
1.3 上机实操 1:网络环境搭建
在正式开始实验之前,先在 ecs-ee63 集群上安装必备的网络诊断工具。
实验环境
| 项目 | 详情 |
|---|---|
| 服务器 | 华为云 ECS ecs-ee63-0001/0002/0003 (c6.large_2) |
| 操作系统 | Ubuntu 24.04 LTS |
| 内核版本 | Linux 6.8.x |
| 网络 | 华为云 VPC: 192.168.0.0/24 |
步骤 1:安装工具
bash
# 更新软件包列表
sudo apt-get update
# 安装全套网络工具
sudo apt-get install -y \
tcpdump \ # 抓包工具 (Packet Capture)
net-tools \ # ifconfig, netstat, route, arp
traceroute \ # 路由追踪
dnsutils \ # dig, nslookup
curl \ # HTTP 客户端
nmap \ # 端口扫描 (Network Mapper)
iperf3 \ # 网络性能测试
hping3 \ # 高级 ping/数据包构造
nginx \ # Web 服务器 (实验目标)
openssl # SSL/TLS 工具
实操截图 --- tcpip-01 (119.8.59.15 / 192.168.0.5):
>> tcpip-01 (Server A - TCP/IP 实验) - 119.8.59.15
# 工具安装验证
hostname → ecs-ee63-0003
ip addr → inet 192.168.0.5/24 brd 192.168.0.255 scope global dynamic
tcpdump → /usr/bin/tcpdump ✓
nmap → /usr/bin/nmap ✓
hping3 → /usr/sbin/hping3 ✓
nginx → Active: active (running) since Tue 2026-06-02 12:12:05 CST ✓
步骤 2:查看网络配置
bash
# 方法一:传统 ifconfig (来自 net-tools)
ifconfig
# 方法二:现代 iproute2 工具
ip addr show
ip route show
实操输出 --- tcpip-01:
=== ifconfig ===
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.0.5 netmask 255.255.255.0 broadcast 192.168.0.255
inet6 fe80::f816:3eff:fe5a:b2b7 prefixlen 64 scopeid 0x20<link>
ether fa:16:3e:5a:b2:b7 txqueuelen 1000 (Ethernet)
RX packets 42057 bytes 60639642 (60.6 MB)
TX packets 15080 bytes 1309251 (1.3 MB)
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
关键参数解释
| 参数 | 值 | 含义 |
|---|---|---|
| mtu 1500 | 1500 字节 | Maximum Transmission Unit (最大传输单元),以太网标准值 |
| inet 192.168.0.5/24 | CIDR 表示法 | IP 地址,/24 表示子网掩码 255.255.255.0 (256 个地址) |
| flags=4163 | UP, BROADCAST, RUNNING, MULTICAST | 接口已启用、支持广播、正在运行、支持多播 |
| ether fa:16:3e:5a:b2:b7 | MAC 地址 | 48位物理地址,前24位是 OUI (Organization Unique Identifier) |
| RX/TX packets | 收发统计 | 可用于诊断网络问题 (是否丢包、错误率) |
| lo | Loopback 接口 | 127.0.0.1,本机回环,不经过物理网卡 |
💡 踩坑记录 --- ifconfig 找不到?
Ubuntu 24.04 默认不安装
net-tools,需手动apt-get install net-tools。推荐使用现代
ip命令 (iproute2) 替代:
ifconfig→ip addr showroute→ip route shownetstat→ss(Socket Statistics)
第2章:网络层协议 (Internet Layer)
2.1 IP 协议详解 (Internet Protocol)
IP 协议是 TCP/IP 协议族的核心,负责尽力而为 (Best Effort) 地将数据包从源地址传输到目标地址。它不保证可靠性------可靠性由上层 TCP 协议负责。
IPv4 地址结构
IPv4 地址是一个 32 位无符号整数 ,常用点分十进制 (Dotted Decimal) 表示:
二进制: 11000000 10101000 00000000 00000101
十进制: 192 . 168 . 0 . 5
十六进制: C0 . A8 . 00 . 05
┌────────────────────── IPv4 地址结构 (32 bits) ──────────────────────┐
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ 网络部分 │ │ 主机部分 │ │
│ │ (Network Portion) │ │ (Host Portion) │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ ↑ 子网掩码决定分界线 ↑ │
│ │
│ 例: 192.168.0.5 / 255.255.255.0 (= /24) │
│ ├── 192.168.0 ──┤├── .5 ──┤ │
│ 网络部分 主机部分 │
└──────────────────────────────────────────────────────────────────────┘
CIDR 表示法与子网划分
CIDR (Classless Inter-Domain Routing) 使用 /N 表示子网掩码中前 N 位为 1:
| CIDR | 子网掩码 | 可用主机数 | 说明 |
|---|---|---|---|
| /8 | 255.0.0.0 | 16,777,214 | A 类网络 |
| /16 | 255.255.0.0 | 65,534 | B 类网络 |
| /24 | 255.255.255.0 | 254 | C 类网络 (最常见) |
| /28 | 255.255.255.240 | 14 | 小型子网, 适合部门 |
| /30 | 255.255.255.252 | 2 | 点对点链路 |
| /32 | 255.255.255.255 | 1 | 单主机 |
子网掩码计算 (以 /24 为例):
255 . 255 . 255 . 0
11111111.11111111.11111111.00000000
├────── 24 bits ───┤├── 8 bits ──┤
网络位 主机位
可用主机 = 2^8 - 2 = 254 (减去网络地址 192.168.0.0 和广播地址 192.168.0.255)
路由选择机制
路由器根据路由表 (Routing Table) 决定数据包的下一跳:
路由表查找逻辑 (最长前缀匹配 Longest Prefix Match):
1. 用目标 IP 与路由表条目的 Genmask 做按位与 (&)
2. 结果与 Destination 比较
3. 匹配的最长前缀条目胜出
4. 使用该条目的 Gateway 作为下一跳
实操 --- 查看路由表 (tcpip-01):
=== route -n ===
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.0.1 0.0.0.0 UG 100 0 0 eth0
169.254.169.254 192.168.0.254 255.255.255.255 UGH 100 0 0 eth0
192.168.0.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
路由表详解:
| 目标网络 | 网关 (下一跳) | 掩码 | Flags | 含义 |
|---|---|---|---|---|
0.0.0.0 |
192.168.0.1 |
0.0.0.0 |
UG | 默认路由 (Default Gateway),匹配所有流量,优先级最低 |
169.254.169.254 |
192.168.0.254 |
255.255.255.255 |
UGH | 华为云 metadata 服务,获取实例元数据 |
192.168.0.0 |
0.0.0.0 |
255.255.255.0 |
U | 直连网络,0.0.0.0 表示无需网关,直接 ARP 即可通信 |
Flags 含义:
- U (Up) --- 路由已启用
- G (Gateway) --- 使用网关,非直连 (无 G 表示直连网络)
- H (Host) --- 目标是一个主机地址 (掩码 /32)
实操 --- 集群路由表对比
三台服务器的路由表完全一致,说明它们处于同一 VPC 子网内:
| 服务器 | 私有 IP | 默认网关 | 路由表条目 |
|---|---|---|---|
| tcpip-01 | 192.168.0.5 | 192.168.0.1 | 3 条 (default + metadata + local) |
| tcpip-02 | 192.168.0.207 | 192.168.0.1 | 3 条 |
| tcpip-03 | 192.168.0.111 | 192.168.0.1 | 3 条 |
2.2 ICMP 协议 (Internet Control Message Protocol)
ICMP 是 IP 层的控制协议,用于传递错误报告和网络诊断信息。它不是用来传输用户数据的------而是帮助 IP 协议更好地工作。
常见 ICMP 报文类型
| 类型 (Type) | 代码 (Code) | 名称 | 用途 |
|---|---|---|---|
| 0 | 0 | Echo Reply | ping 响应 |
| 8 | 0 | Echo Request | ping 请求 |
| 3 | 0 | Destination Network Unreachable | 网络不可达 |
| 3 | 1 | Destination Host Unreachable | 主机不可达 |
| 3 | 3 | Destination Port Unreachable | 端口不可达 (UDP) |
| 5 | 1 | Redirect (Host) | 路由重定向 |
| 11 | 0 | TTL Exceeded | TTL 耗尽 (traceroute 原理) |
ICMP 报文结构:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Body (variable) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.3 上机实操 2:IP 地址和路由实验
实验 2-1:ping --- ICMP 网络可达性测试
bash
ping -c 4 www.baidu.com
| 参数 | 含义 |
|---|---|
-c 4 |
发送 4 个 ICMP Echo Request 后停止 (默认无限) |
-i 0.2 |
间隔 0.2 秒发送 (默认 1 秒,仅 root 可设 < 0.2) |
-s 1472 |
数据部分大小 (默认 56,加上头共 84 字节) |
-W 2 |
超时 2 秒 |
实操输出 --- tcpip-01 ping 百度:
PING www.wshifen.com (103.235.46.115) 56(84) bytes of data.
64 bytes from 103.235.46.115: icmp_seq=1 ttl=53 time=3.59 ms
64 bytes from 103.235.46.115: icmp_seq=2 ttl=53 time=3.48 ms
64 bytes from 103.235.46.115: icmp_seq=3 ttl=53 time=3.39 ms
64 bytes from 103.235.46.115: icmp_seq=4 ttl=53 time=3.34 ms
--- www.wshifen.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 3.344/3.449/3.591/0.094 ms
输出详解:
| 字段 | 值 | 含义 |
|---|---|---|
www.wshifen.com |
CDN 节点域名 | 百度使用了 CDN (Content Delivery Network),实际解析到 www.wshifen.com |
103.235.46.115 |
目标 IP | 百度 CDN 节点在香港的 IP |
56(84) |
ICMP 数据量 | 56 字节 ICMP 数据 + 28 字节 ICMP/IP 头 = 84 字节 |
ttl=53 |
Time To Live | 经过的跳数 = 64 - 53 = 11 跳 (Linux 默认 TTL=64) |
time=3.59 ms |
RTT (Round-Trip Time) | 往返延迟约 3.6ms,非常快 (华为云香港到 CDN 节点) |
0% packet loss |
丢包率 | 0%,网络状况良好 |
mdev=0.094 |
标准差 | RTT 抖动小,连接稳定 |
实验 2-2:traceroute --- 路由追踪
traceroute 利用 IP 包的 TTL 字段,逐跳发现数据包经过的路由器:
工作原理:
1. 发送 TTL=1 的 UDP 包 → 第一跳路由器返回 ICMP TTL Exceeded
2. 发送 TTL=2 的 UDP 包 → 第二跳路由器返回 ICMP TTL Exceeded
3. 重复直到到达目标或达到最大跳数
每次发送 3 个探测包 (默认),计算每条路径的延迟
bash
traceroute -n -m 10 www.baidu.com
| 参数 | 含义 |
|---|---|
-n |
不进行 DNS 反向解析 (更快) |
-m 10 |
最大 10 跳 (默认 30) |
-w 2 |
每跳超时 2 秒 |
实操输出 --- tcpip-01 traceroute 百度:
traceroute to www.baidu.com (103.235.46.102), 10 hops max, 60 byte packets
1 100.88.0.1 14.612 ms 14.698 ms 14.792 ms
2 10.231.92.130 2.257 ms 2.554 ms 10.231.92.142 4.619 ms
3 10.231.92.9 7.368 ms 7.468 ms 7.196 ms
4 * * *
5 * * *
6 * * *
7 * * *
8 * * *
9 * * 11.92.1.150 1.465 ms
10 * * *
输出详解:
| 跳数 | IP | 延迟 | 说明 |
|---|---|---|---|
| 1 | 100.88.0.1 | ~14.6 ms | 华为云 VPC 网关 / NAT 网关 |
| 2 | 10.231.92.130/142 | ~2.3-4.6 ms | 华为云内部路由器 (10.x 私有地址) |
| 3 | 10.231.92.9 | ~7.2 ms | 华为云骨干路由器 |
| 4-8 | * * * |
--- | 中间路由器不响应 ICMP (防火墙/负载均衡) |
| 9 | 11.92.1.150 | ~1.5 ms | 接近目标网段 |
| 10 | * * * |
--- | 到达目标 (CDN 边界) |
💡
* * *代表什么?有三种可能:
- 路由器配置了不响应 ICMP TTL Exceeded(最常见)
- 防火墙拦截了探测包
- 该跳确实超时了
连续的
*通常是情况 1,不影响实际路由。实际数据包仍会正常通过。
实验 2-3:跨主机 ping 测试
从 tcpip-02 (192.168.0.207) ping tcpip-01 (192.168.0.5):
PING 192.168.0.5 (192.168.0.5) 56(84) bytes of data.
64 bytes from 192.168.0.5: icmp_seq=1 ttl=64 time=0.243 ms
64 bytes from 192.168.0.5: icmp_seq=2 ttl=64 time=0.269 ms
--- 192.168.0.5 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1054ms
rtt min/avg/max/mdev = 0.243/0.256/0.269/0.013 ms
分析: tcpip-02 到 tcpip-01 的 RTT 仅 0.24ms,TTL=64(直连,未经过路由器),说明两台服务器在同一 VPC 二层网络 (同一个广播域),通信不经过网关。
第3章:传输层协议 (Transport Layer)
3.1 TCP 协议 (Transmission Control Protocol)
TCP 提供面向连接、可靠、有序、全双工的字节流传输。它通过以下机制实现可靠性:
TCP 可靠性机制概览:
┌─────────────────────────────────────────────────────┐
│ 三次握手 (3-Way Handshake) │
│ ├── 建立连接前协商初始序列号 │
│ └── 防止历史连接干扰 │
├─────────────────────────────────────────────────────┤
│ 序列号与确认应答 (Seq/Ack) │
│ ├── 每个字节编号 │
│ └── 累积确认 (Cumulative ACK) │
├─────────────────────────────────────────────────────┤
│ 超时重传 (RTO - Retransmission Timeout) │
│ ├── 自适应超时算法 (RTT 平滑值) │
│ └── 指数退避 │
├─────────────────────────────────────────────────────┤
│ 流量控制 (Flow Control) │
│ ├── 滑动窗口 (Sliding Window) │
│ └── 接收方通告窗口 (rwnd - Receiver Window) │
├─────────────────────────────────────────────────────┤
│ 拥塞控制 (Congestion Control) │
│ ├── 慢启动 (Slow Start) │
│ ├── 拥塞避免 (Congestion Avoidance) │
│ ├── 快速重传 (Fast Retransmit) │
│ └── 快速恢复 (Fast Recovery) │
└─────────────────────────────────────────────────────┘
三次握手 (Three-Way Handshake)
客户端 (Client) 服务器 (Server)
│ │
│ ─────── SYN, seq=x ──────────────► │ STATE: LISTEN → SYN_RCVD
│ │
│ ◄─── SYN+ACK, seq=y, ack=x+1 ──── │ STATE: SYN_RCVD
│ │
│ ─────── ACK, ack=y+1 ───────────► │ STATE: SYN_RCVD → ESTABLISHED
│ │
STATE: ESTABLISHED STATE: ESTABLISHED
为什么是三次而不是两次?
- 两次握手无法防止已失效的连接请求到达服务器 (历史连接问题)
- 三次握手确保双方都确认了对方的收发能力
四次挥手 (Four-Way Termination)
主动关闭方 (Active Closer) 被动关闭方 (Passive Closer)
│ │
│ ──── FIN, seq=u ───────────────► │ STATE: ESTABLISHED → CLOSE_WAIT
│ │
│ ◄─── ACK, ack=u+1 ────────────── │ (可能还有数据要发送)
│ │
│ ◄─── FIN, seq=v, ack=u+1 ─────── │ STATE: CLOSE_WAIT → LAST_ACK
│ │
│ ──── ACK, ack=v+1 ─────────────► │ STATE: LAST_ACK → CLOSED
│ │
STATE: TIME_WAIT (2MSL) STATE: CLOSED
为什么需要 TIME_WAIT?
- 确保最后的 ACK 能被对方收到 (如果丢失,对方会重发 FIN)
- 让旧连接的数据包在网络中消失 (2MSL = 2 × Maximum Segment Lifetime)
TCP 状态转换图
┌─────────┐
│ CLOSED │
└────┬────┘
│ 主动打开 (connect)
┌────▼────┐
│SYN_SENT │
└────┬────┘
│ 收到 SYN+ACK
┌────▼──────┐
┌─────────────────│ESTABLISHED│─────────────────┐
│ └────┬──────┘ │
│ │ 收到 FIN │
┌────▼────┐ ┌────▼──────┐ ┌──────▼─────┐
│FIN_WAIT1│ │CLOSE_WAIT │ │ passive CLOSE│
└────┬────┘ └────┬──────┘ └─────────────┘
│ 收到 ACK │ 发送 FIN
┌────▼────┐ ┌────▼──────┐
│FIN_WAIT2│ │ LAST_ACK │
└────┬────┘ └────┬──────┘
│ 收到 FIN │ 收到 ACK
┌────▼────┐ │
│TIME_WAIT│ ┌────▼────┐
└────┬────┘ │ CLOSED │
│ 2MSL 后 └─────────┘
┌────▼────┐
│ CLOSED │
└─────────┘
TCP 报文段结构
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options (if data offset > 5) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data (payload) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
标志位 (Flags) 详解:
| 标志 | 全称 | 含义 |
|---|---|---|
| SYN | Synchronize | 建立连接,同步序列号 |
| ACK | Acknowledgment | 确认号字段有效 |
| FIN | Finish | 发送方数据发送完毕 |
| RST | Reset | 重置连接 (拒绝/异常终止) |
| PSH | Push | 立即推送数据到应用层 (不缓冲) |
| URG | Urgent | 紧急指针有效 |
3.2 UDP 协议 (User Datagram Protocol)
UDP 是一种无连接、不可靠、尽力而为的传输层协议。没有握手,没有确认,没有重传------只有端口号、长度和校验和。
TCP vs UDP 对比
┌────────────────────┬──────────────────────┬──────────────────────┐
│ 特性 │ TCP │ UDP │
├────────────────────┼──────────────────────┼──────────────────────┤
│ 连接 │ 面向连接 (Connection) │ 无连接 (Connectionless)│
│ 可靠性 │ 可靠 (ACK+重传+顺序) │ 不可靠 (尽力而为) │
│ 速度 │ 较慢 (握手+控制开销) │ 较快 (无控制开销) │
│ 数据边界 │ 字节流 (无边界) │ 数据报 (保留边界) │
│ 头部大小 │ 20-60 字节 │ 8 字节 │
│ 多播/广播 │ 不支持 │ 支持 │
│ 流量/拥塞控制 │ 有 │ 无 │
│ 典型应用 │ HTTP, SSH, SMTP, FTP │ DNS, DHCP, VoIP, 游戏 │
│ 最新趋势 │ HTTP/3 也在用 UDP! │ QUIC (基于 UDP) │
└────────────────────┴──────────────────────┴──────────────────────┘
💡 HTTP/3 为什么选择 UDP?
HTTP/3 基于 QUIC 协议,虽然 QUIC 运行在 UDP 之上,但它在用户态实现了 TCP 的可靠性特性 (重传、拥塞控制) 甚至更好。使用 UDP 的原因是避免操作系统内核 TCP 协议栈的僵化------QUIC 可以在应用层快速迭代,而内核 TCP 协议栈升级非常缓慢。
UDP 报文结构
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data (payload) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
UDP 头部仅 8 字节:
- Source Port: 源端口 (16bit)
- Dest Port: 目标端口 (16bit)
- Length: UDP 头部+数据总长度 (16bit)
- Checksum: 校验和,可选 (IPv4) / 强制 (IPv6)
3.3 上机实操 3:TCP/UDP 协议分析
实验 3-1:tcpdump 抓取 TCP 三次握手 + 四次挥手(跨主机)
这是本次博客最硬核的实验------抓包分析真实的 TCP 握手过程:
实验脚本 (在 tcpip-01 上运行):
bash
#!/bin/bash
# 启动 tcpdump,从 tcpip-01 curl 到 tcpip-02 的 nginx
tcpdump -i eth0 -nn -c 10 -w /tmp/cross.pcap host 192.168.0.207 and port 80 &
sleep 0.5
curl -s http://192.168.0.207/ > /dev/null
sleep 1
kill %1 2>/dev/null; wait 2>/dev/null
tcpdump -nn -r /tmp/cross.pcap
实操输出 --- 跨服务器 TCP 通信完整抓包:
reading from file /tmp/cross.pcap, link-type EN10MB (Ethernet), snapshot length 262144
12:19:59.576960 IP 192.168.0.5.38224 > 192.168.0.207.80: Flags [S],
seq 305779680, win 64240,
options [mss 1460,sackOK,TS val 2711816421 ecr 0,nop,wscale 7], length 0
12:19:59.577210 IP 192.168.0.207.80 > 192.168.0.5.38224: Flags [S.],
seq 658854510, ack 305779681, win 65160,
options [mss 1460,sackOK,TS val 2080310645 ecr 2711816421,nop,wscale 7], length 0
12:19:59.577227 IP 192.168.0.5.38224 > 192.168.0.207.80: Flags [.],
ack 1, win 502,
options [nop,nop,TS val 2711816421 ecr 2080310645], length 0
12:19:59.577264 IP 192.168.0.5.38224 > 192.168.0.207.80: Flags [P.],
seq 1:77, ack 1, win 502,
options [nop,nop,TS val 2711816421 ecr 2080310645], length 76: HTTP: GET / HTTP/1.1
12:19:59.577412 IP 192.168.0.207.80 > 192.168.0.5.38224: Flags [.],
ack 77, win 509,
options [nop,nop,TS val 2080310646 ecr 2711816421], length 0
12:19:59.577631 IP 192.168.0.207.80 > 192.168.0.5.38224: Flags [P.],
seq 1:863, ack 77, win 509,
options [nop,nop,TS val 2080310646 ecr 2711816421], length 862: HTTP: HTTP/1.1 200 OK
12:19:59.577637 IP 192.168.0.5.38224 > 192.168.0.207.80: Flags [.],
ack 863, win 496,
options [nop,nop,TS val 2711816422 ecr 2080310646], length 0
12:19:59.577700 IP 192.168.0.5.38224 > 192.168.0.207.80: Flags [F.],
seq 77, ack 863, win 496,
options [nop,nop,TS val 2711816422 ecr 2080310646], length 0
12:19:59.577832 IP 192.168.0.207.80 > 192.168.0.5.38224: Flags [F.],
seq 863, ack 78, win 509,
options [nop,nop,TS val 2080310646 ecr 2711816422], length 0
12:19:59.577837 IP 192.168.0.5.38224 > 192.168.0.207.80: Flags [.],
ack 864, win 496,
options [nop,nop,TS val 2711816422 ecr 2080310646], length 0
逐包详解三次握手
包 #1 - SYN (客户端 → 服务器)
┌─────────────────────────────────────────────────────────┐
│ 192.168.0.5:38224 → 192.168.0.207:80 │
│ Flags: [S] SYN 标志 │
│ seq: 305779680 初始序列号 (ISN) │
│ win: 64240 接收窗口 64240 字节 │
│ options: │
│ mss 1460 最大段大小 = 1500(MTU) - 20(IP) - 20(TCP) │
│ sackOK 支持选择性确认 (Selective ACK) │
│ wscale 7 窗口缩放因子 2^7 = 128x │
│ length: 0 纯控制包,无数据载荷 │
└─────────────────────────────────────────────────────────┘
包 #2 - SYN+ACK (服务器 → 客户端)
┌─────────────────────────────────────────────────────────┐
│ 192.168.0.207:80 → 192.168.0.5:38224 │
│ Flags: [S.] SYN+ACK 标志 │
│ seq: 658854510 服务器初始序列号 │
│ ack: 305779681 确认客户端的 ISN+1 │
│ win: 65160 服务器接收窗口 │
└─────────────────────────────────────────────────────────┘
包 #3 - ACK (客户端 → 服务器) --- 握手完成
┌─────────────────────────────────────────────────────────┐
│ 192.168.0.5:38224 → 192.168.0.207:80 │
│ Flags: [.] ACK 标志 │
│ ack: 1 相对确认号=1 │
│ win: 502 客户端窗口~64240/128 │
│ length: 0 纯 ACK │
└─────────────────────────────────────────────────────────┘
TCP 选项详解:
| 选项 | 值 | 含义 |
|---|---|---|
| mss 1460 | 1460 字节 | Maximum Segment Size,数据部分最大尺寸 |
| sackOK | --- | Selective ACK,允许选择性确认(只重传丢失的段,不重传所有后续段) |
| TS val/ecr | 时间戳 | TCP Timestamps,用于 RTTM (Round-Trip Time Measurement) 和 PAWS (Protection Against Wrapped Sequences) |
| wscale 7 | 2^7=128 | Window Scale,实际窗口 = win × 2^wscale |
逐包详解数据传输 + 四次挥手
包 #4 - PSH+ACK (客户端 → 服务器):HTTP 请求
seq 1:77, ack 1, length 76
→ 发送 76 字节 HTTP GET 请求
包 #5 - ACK (服务器 → 客户端):确认收到请求
ack 77 → 确认收到 76 字节
包 #6 - PSH+ACK (服务器 → 客户端):HTTP 响应
seq 1:863, ack 77, length 862
→ 发送 862 字节 HTTP 响应 (nginx 欢迎页)
包 #7 - ACK (客户端 → 服务器):确认收到响应
ack 863 → 确认收到 862 字节
包 #8 - FIN+ACK (客户端 → 服务器):客户端主动关闭
seq 77, ack 863 → 发送 FIN
包 #9 - FIN+ACK (服务器 → 客户端):服务器也关闭
seq 863, ack 78 → 发送 FIN+ACK
包 #10 - ACK (客户端 → 服务器):最后确认
ack 864 → 确认服务器的 FIN
→ 连接完全关闭 (客户端进入 TIME_WAIT)
实验 3-2:tcpdump 抓取本机回环 TCP 通信
本机通信使用 lo (localhost) 接口,同样有完整的三次握手,但使用 IPv6 (::1):
12:19:30.840184 IP6 ::1.38536 > ::1.80: Flags [S],
seq 3626205147, win 65476, options [mss 65476,sackOK,TS val 2409588063 ecr 0,nop,wscale 7], length 0
12:19:30.840197 IP6 ::1.80 > ::1.38536: Flags [S.],
seq 833032379, ack 3626205148, win 65464, options [mss 65476,sackOK,TS val 2409588063 ecr 2409588063,nop,wscale 7], length 0
12:19:30.840207 IP6 ::1.38536 > ::1.80: Flags [.],
ack 1, win 512, options [nop,nop,TS val 2409588063 ecr 2409588063], length 0
💡 注意: 本机回环通信的 MSS 为 65476(接近 65535),因为 lo 接口的 MTU 为 65536,远大于以太网的 1500。
实验 3-3:ss (Socket Statistics) --- 查看连接状态
ss 是现代 Linux 替代 netstat 的工具,用于查看 socket 统计信息:
bash
# 查看所有监听端口
ss -tlnp
# 查看已建立的 TCP 连接
ss -tan state established
# 查看所有 TCP 连接及状态
ss -tan
| 参数 | 含义 |
|---|---|
-t |
仅显示 TCP 套接字 (TCP sockets) |
-u |
仅显示 UDP 套接字 |
-l |
仅显示监听中的套接字 (Listening) |
-n |
不解析服务名 (显示端口号而非名称) |
-p |
显示使用该套接字的进程 |
-a |
显示所有套接字 (包括 LISTEN 和非 LISTEN) |
实操输出 --- tcpip-01 监听端口:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:* systemd-resolve
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* sshd
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* nginx
LISTEN 0 10 127.0.0.1:29338 0.0.0.0:* uniagentd
LISTEN 0 4096 [::]:22 [::]:* sshd
LISTEN 0 511 [::]:80 [::]:* nginx
监听端口分析:
| 端口 | 进程 | 监听地址 | 说明 |
|---|---|---|---|
| 53 | systemd-resolve | 127.0.0.54 (仅本地) | 本地 DNS 解析器 (stub resolver) |
| 22 | sshd | 0.0.0.0 (所有接口) | SSH 远程管理 |
| 80 | nginx | 0.0.0.0 (所有接口) | Web 服务器 |
| 29338-29339 | uniagentd | 127.0.0.1 (仅本地) | 华为云监控 Agent |
Recv-Q / Send-Q 含义:
| 字段 | 含义 |
|---|---|
| Recv-Q | 接收队列中等待应用读取的字节数 (LISTEN 状态显示 SYN backlog) |
| Send-Q | 发送队列中等待对方确认的字节数 (LISTEN 状态显示 backlog 最大值) |
💡 踩坑提醒:
ss显示[::]:80和0.0.0.0:80同时在监听,说明 nginx 同时支持 IPv4 和 IPv6 连接。Ubuntu 24.04 的 nginx 默认配置为listen 80 default_server; listen [::]:80 default_server;。
实验 3-4:netcat TCP/UDP 通信实验
netcat (nc) 是网络调试的"瑞士军刀":
bash
# TCP 服务器端监听
nc -lvp 12345
# -l: 监听模式 (Listen)
# -v: 详细输出 (Verbose)
# -p: 指定端口
# TCP 客户端连接
echo "hello from nc client" | nc -w 1 127.0.0.1 12345
# -w 1: 超时 1 秒
# UDP 发送 (无需监听方)
echo "udp test message" | nc -u -w 1 127.0.0.1 5555
# -u: UDP 模式
# (UDP 无连接, 发送成功即返回, 不等待响应)
实验 3-5:nmap 端口扫描
bash
# 本机扫描
nmap -sV localhost
# 跨服务器扫描
nmap -sS -p 1-1000 192.168.0.207
| 参数 | 含义 |
|---|---|
-sS |
SYN 扫描 (半开扫描, Half-Open Scan),只发 SYN 不回 ACK |
-sV |
版本检测 (Version Detection) |
-p 1-1000 |
扫描端口范围 1-1000 |
-sn |
Ping 扫描 (仅检测主机存活) |
-O |
操作系统检测 |
实操输出:
=== nmap localhost ===
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.15 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.24.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
=== nmap tcpip-02 (192.168.0.207) ===
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
MAC Address: FA:16:3E:5A:B2:81 (Unknown)
💡 SYN 扫描原理: nmap 发送 SYN 包,如果收到 SYN+ACK 则端口开放(然后发送 RST 中止握手),如果收到 RST 则端口关闭,如果无响应则被防火墙过滤。这种方式比全连接扫描更快,且不会在目标日志中留下完整连接记录。
实验 3-6:iperf3 网络性能测试
iperf3 用于测量两台主机之间的 TCP/UDP 带宽:
bash
# 服务器端 (tcpip-02)
iperf3 -s
# -s: Server 模式
# -p 5201: 端口 (默认 5201)
# 客户端 (tcpip-01)
iperf3 -c 192.168.0.207 -t 5
# -c: Client 模式, 连接到指定 IP
# -t 5: 测试 5 秒
# -P 4: 4 并行流
# -u: UDP 模式
实操输出 --- tcpip-01 → tcpip-02 内网带宽测试:
Connecting to host 192.168.0.207, port 5201
[ 5] local 192.168.0.5 port 34434 connected to 192.168.0.207 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 566 MBytes 4.74 Gbits/sec 785 28.3 KBytes
[ 5] 1.00-2.00 sec 481 MBytes 4.04 Gbits/sec 399 60.8 KBytes
[ 5] 2.00-3.00 sec 468 MBytes 3.92 Gbits/sec 3543 55.1 KBytes
[ 5] 3.00-4.00 sec 480 MBytes 4.03 Gbits/sec 4332 42.4 KBytes
[ 5] 4.00-5.00 sec 480 MBytes 4.03 Gbits/sec 738 270 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-5.00 sec 2.42 GBytes 4.15 Gbits/sec 9797 sender
[ 5] 0.00-5.00 sec 2.41 GBytes 4.15 Gbits/sec receiver
输出详解:
| 指标 | 值 | 含义 |
|---|---|---|
| Bitrate | 4.15 Gbits/sec | 华为云内网实际带宽约 4 Gbps (远超规格 2Gbps?) |
| Transfer | 2.42 GB | 5 秒内传输总量 |
| Retr | 9797 | TCP 重传次数,部分时段较高 (如第 3-4 秒) |
| Cwnd | 28.3~270 KB | 拥塞窗口变化 (Congestion Window),反映 TCP 拥塞控制行为 |
🔧 第二部分:进阶实践篇
第4章:ARP 协议实验 (Address Resolution Protocol)
4.1 ARP 协议原理
ARP 解决的是已知 IP 地址,如何找到对应的 MAC 地址的问题。它在局域网内通过广播的方式查询,目标主机收到后单播回复。
ARP 工作流程:
发送方 (192.168.0.5) 想与 192.168.0.207 通信
① 检查本地 ARP 缓存表
└── 未命中 → 发起 ARP 请求
② 广播 ARP 请求 (ARP Request)
┌─────────────────────────────────────────┐
│ Src MAC: fa:16:3e:5a:b2:b7 │
│ Src IP: 192.168.0.5 │
│ Dst MAC: ff:ff:ff:ff:ff:ff (广播) │
│ Dst IP: 192.168.0.207 │
│ Opcode: 1 (Request) │
│ 问题: "谁是 192.168.0.207?告诉我你的 MAC" │
└─────────────────────────────────────────┘
③ 目标主机单播 ARP 响应 (ARP Reply)
┌─────────────────────────────────────────┐
│ Src MAC: fa:16:3e:5a:b2:81 │
│ Src IP: 192.168.0.207 │
│ Dst MAC: fa:16:3e:5a:b2:b7 (单播) │
│ Dst IP: 192.168.0.5 │
│ Opcode: 2 (Reply) │
│ 答案: "192.168.0.207 的 MAC 是 fa:16:..." │
└─────────────────────────────────────────┘
④ 发送方将 IP-MAC 映射存入 ARP 缓存
后续通信直接使用缓存,无需再次 ARP
ARP 报文结构
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Hardware Type | Protocol Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| HW Addr Len | Proto Addr Len| Operation |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sender Hardware Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sender Protocol Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Target Hardware Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Target Protocol Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Hardware Type: 1 = Ethernet
Protocol Type: 0x0800 = IPv4
Operation: 1 = Request, 2 = Reply
4.2 上机实操 4:ARP 实验
实验 4-1:查看 ARP 缓存表
bash
arp -a
# 或使用现代命令
ip neigh show
实操输出 --- tcpip-01 ARP 缓存:
? (192.168.0.254) at fa:fa:fa:fa:fa:01 [ether] on eth0
_gateway (192.168.0.1) at fa:16:3e:66:fd:20 [ether] on eth0
实操输出 --- tcpip-02 ARP 缓存:
? (192.168.0.5) at fa:16:3e:5a:b2:b7 [ether] on eth0
_gateway (192.168.0.1) at fa:16:3e:66:fd:20 [ether] on eth0
? (192.168.0.254) at fa:fa:fa:fa:fa:01 [ether] on eth0
实操输出 --- tcpip-03 ARP 缓存:
? (192.168.0.5) at fa:16:3e:5a:b2:b7 [ether] on eth0
? (192.168.0.254) at fa:fa:fa:fa:fa:01 [ether] on eth0
_gateway (192.168.0.1) at fa:16:3e:66:fd:20 [ether] on eth0
ARP 缓存分析:
| IP | MAC | 说明 |
|---|---|---|
| 192.168.0.1 | fa:16:3e:66:fd:20 | 华为云 VPC 网关 (默认路由下一跳) |
| 192.168.0.5 | fa:16:3e:5a:b2:b7 | tcpip-01 的 MAC (出现在其他服务器的 ARP 表中) |
| 192.168.0.254 | fa:fa:fa:fa:fa:01 | 华为云 metadata 服务 (169.254.169.254 的路由) |
💡 观察: tcpip-01 的 ARP 表中没有 tcpip-02 (.207),因为还没有直接通信。tcpip-02 的 ARP 表中有了 tcpip-01 (.5),因为之前执行过 curl (HTTP 通信触发了 ARP 查询)。
实验 4-2:ARP 流量抓包
在 tcpip-01 上先清空 ARP 缓存,再 ping tcpip-02,同时抓取 ARP 包。由于 ARP 缓存已被之前的实验预热,这里展示 ARP 缓存状态变化。
⚠️ 注意: 在 VPC 环境中,由于之前已有通信,ARP 缓存可能已存在。如需看到 ARP 请求/响应,需要先
ip neigh flush all清空缓存,再 ping 目标。但在生产或共享环境中慎重操作,清空 ARP 缓存可能导致短暂的网络中断。
第5章:DNS 协议实验 (Domain Name System)
5.1 DNS 工作原理
DNS 将人类可读的域名转换为机器可路由的 IP 地址。它是一个分层的、分布式的数据库系统。
DNS 解析流程 (递归解析):
用户程序 (curl www.baidu.com)
│
▼
本地 DNS Stub Resolver (127.0.0.53)
│ /etc/resolv.conf
▼
┌─────────────────────┐
│ 系统 DNS 缓存 │ ① 检查缓存,命中则直接返回
└─────────┬───────────┘
│ 未命中
▼
┌─────────────────────┐
│ 递归 DNS 服务器 │ ② 查询递归解析器 (ISP/公共 DNS)
│ (192.168.0.x) │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 根域名服务器 (Root) │ ③ . → 返回 com. 的 NS
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 顶级域名服务器 (TLD) │ ④ com. → 返回 baidu.com. 的 NS
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 权威域名服务器 │ ⑤ baidu.com. → 返回 A 记录
│ (Authoritative) │
└─────────┬───────────┘
│
▼
返回 IP: 103.235.46.102
常见 DNS 记录类型
| 类型 | 全称 | 用途 | 示例 |
|---|---|---|---|
| A | Address | IPv4 地址 | www.baidu.com. A 103.235.46.102 |
| AAAA | IPv6 Address | IPv6 地址 | www.example.com. AAAA 2001:db8::1 |
| CNAME | Canonical Name | 别名(域名指向域名) | www.baidu.com. CNAME www.a.shifen.com. |
| MX | Mail Exchange | 邮件服务器优先级 | qq.com. MX 10 mx3.qq.com. |
| NS | Name Server | 权威DNS服务器 | baidu.com. NS ns1.baidu.com. |
| TXT | Text | 文本记录 (SPF/DKIM等) | example.com. TXT "v=spf1 ..." |
| SOA | Start of Authority | 域管理信息 | 主DNS、管理员邮箱、序列号 |
| PTR | Pointer | 反向解析 (IP→域名) | 102.46.235.103.in-addr.arpa. PTR www.baidu.com. |
5.2 上机实操 5:DNS 实验
实验 5-1:dig --- 详细 DNS 查询
dig (Domain Information Groper) 是 DNS 诊断的首选工具:
bash
# 基本查询
dig www.baidu.com
# 指定记录类型
dig qq.com MX +short
# 追踪解析过程
dig www.baidu.com +trace
# 指定 DNS 服务器
dig @8.8.8.8 www.baidu.com
实操输出 --- dig www.baidu.com (完整输出):
; <<>> DiG 9.18.39-0ubuntu0.24.04.5-Ubuntu <<>> www.baidu.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47806
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;www.baidu.com. IN A
;; ANSWER SECTION:
www.baidu.com. 569 IN CNAME www.a.shifen.com.
www.a.shifen.com. 29 IN CNAME www.wshifen.com.
www.wshifen.com. 58 IN A 103.235.46.102
www.wshifen.com. 58 IN A 103.235.46.115
;; Query time: 1 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Tue Jun 02 12:17:32 CST 2026
;; MSG SIZE rcvd: 127
dig 输出字段详解:
| 字段 | 值 | 含义 |
|---|---|---|
| status: NOERROR | 查询成功 | 其他常见: NXDOMAIN(域名不存在), SERVFAIL(服务失败) |
| flags: qr rd ra | 三个标志位 | qr=响应, rd=期望递归, ra=支持递归 |
| ANSWER: 4 | 4 条答案记录 | 2 条 CNAME + 2 条 A 记录 |
| 569, 29, 58 | TTL (秒) | Time To Live,DNS 缓存有效时间 |
| CNAME | 别名链 | www.baidu.com → www.a.shifen.com → www.wshifen.com |
| A | 最终 IP | 103.235.46.102, 103.235.46.115 (CDN 多 IP 负载均衡) |
| Query time: 1 msec | 解析耗时 | 仅 1ms,因为递归解析器已缓存 |
| SERVER: 127.0.0.53 | 本地 DNS | systemd-resolved 的 stub resolver |
| EDNS | 扩展 DNS | 支持 UDP 最大 65494 字节 (而非传统 512) |
💡 www.baidu.com 的 CNAME 链:
www.baidu.com→www.a.shifen.com→www.wshifen.com→ IP这是百度 CDN (Content Delivery Network) 的典型架构。
shifen.com是百度自有的 CDN 域名,最终根据用户地理位置返回最近的 CDN 节点 IP。
实验 5-2:nslookup --- 交互式 DNS 查询
bash
nslookup www.baidu.com
实操输出:
Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com.
www.a.shifen.com canonical name = www.wshifen.com.
Name: www.wshifen.com
Address: 103.235.46.102
Name: www.wshifen.com
Address: 103.235.46.115
nslookup vs dig: nslookup 是较老的 DNS 工具,输出更简洁但功能较少。dig 输出详细,适合故障排查。推荐日常使用 dig。
实验 5-3:MX 记录查询
bash
dig qq.com MX +short
| 参数 | 含义 |
|---|---|
MX |
查询邮件交换记录 (Mail Exchange) |
+short |
简洁输出 (只显示结果) |
实操输出:
30 mx1.qq.com.
10 mx3.qq.com.
20 mx2.qq.com.
MX 优先级: 数字越小优先级越高。邮件会先尝试发送到 mx3.qq.com (10),失败则尝试 mx2.qq.com (20),最后才尝试 mx1.qq.com (30)。
实验 5-4:查看 DNS 配置
bash
cat /etc/resolv.conf
resolvectl status
实操输出 --- /etc/resolv.conf:
# This is /run/systemd/resolve/stub-resolv.conf managed by systemd-resolved(8).
nameserver 127.0.0.53
options edns0 trust-ad
search openstacklocal
字段解释:
| 配置 | 含义 |
|---|---|
nameserver 127.0.0.53 |
本地 systemd-resolved stub resolver (非真实 DNS) |
options edns0 |
启用 EDNS0 (Extended DNS) 扩展 |
options trust-ad |
信任 DNSSEC 认证数据 |
search openstacklocal |
搜索域 (补全短主机名) |
💡 注意: Ubuntu 24.04 使用
systemd-resolved管理 DNS,/etc/resolv.conf是软链接到/run/systemd/resolve/stub-resolv.conf,不应手动编辑。使用resolvectl查看上游 DNS 配置。
第6章:HTTP/HTTPS 协议实验
6.1 HTTP 协议详解 (HyperText Transfer Protocol)
HTTP 是应用层协议,基于请求-响应模型。HTTP/1.1 在同一个 TCP 连接上可以有多个请求-响应(持久连接 / Keep-Alive)。
HTTP 请求结构
GET /index.html HTTP/1.1 ← 请求行 (Method URI Version)
Host: www.example.com ← 请求头 (Headers)
User-Agent: curl/8.5.0
Accept: text/html
← 空行 (CRLF)
← 请求体 (Body),GET 请求通常为空
HTTP 响应结构
HTTP/1.1 200 OK ← 状态行 (Version Status-Code Reason)
Server: nginx/1.24.0 (Ubuntu) ← 响应头 (Headers)
Content-Type: text/html
Content-Length: 615
← 空行 (CRLF)
<!DOCTYPE html> ← 响应体 (Body)
<html>...
常见 HTTP 状态码
| 范围 | 类别 | 典型状态码 |
|---|---|---|
| 1xx | 信息响应 | 100 Continue, 101 Switching Protocols |
| 2xx | 成功 | 200 OK, 201 Created, 204 No Content |
| 3xx | 重定向 | 301 Moved Permanently, 302 Found, 304 Not Modified |
| 4xx | 客户端错误 | 400 Bad Request , 401 Unauthorized , 403 Forbidden , 404 Not Found, 405 Method Not Allowed |
| 5xx | 服务器错误 | 500 Internal Server Error , 502 Bad Gateway , 503 Service Unavailable, 504 Gateway Timeout |
6.2 HTTPS 协议
HTTPS = HTTP over TLS (Transport Layer Security)。TLS 在 TCP 之上提供了:
┌──────────┐
│ HTTP │ 应用数据
├──────────┤
│ TLS │ 加密、身份验证、完整性
├──────────┤
│ TCP │ 可靠传输
├──────────┤
│ IP │ 路由
└──────────┘
TLS 握手简化流程 (TLS 1.3)
客户端 服务器
│ │
│── ClientHello ────────────►│ 支持的密码套件 + 密钥共享
│ │
│◄── ServerHello ─────────── │ 选择的密码套件 + 密钥共享 + 证书
│ EncryptedExtensions │
│ Certificate │
│ Finished │
│ │
│── Finished ───────────────►│ 握手完成
│ │
│◄══ 加密通信 ═══════════════►│
6.3 上机实操 6:HTTP/HTTPS 实验
实验 6-1:curl --- 详细 HTTP 请求分析
bash
curl -sv http://localhost
| 参数 | 含义 |
|---|---|
-s |
静默模式 (不显示进度条) |
-v |
详细输出 (Verbose),显示请求头和响应头 |
-I |
仅获取响应头 (HEAD 请求) |
-o /dev/null |
丢弃响应体 |
-w '%{http_code}' |
仅输出状态码 |
实操输出 --- curl http://localhost:
* Host localhost:80 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:80...
* Connected to localhost (::1) port 80
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.24.0 (Ubuntu)
< Date: Tue, 02 Jun 2026 04:17:40 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Tue, 02 Jun 2026 04:12:04 GMT
< Connection: keep-alive
< ETag: "6a1e5814-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
逐行详解:
| 输出 | 含义 |
|---|---|
Host localhost:80 was resolved |
DNS 解析完成 |
IPv6: ::1 / IPv4: 127.0.0.1 |
双栈解析结果 |
Trying [::1]:80... |
优先尝试 IPv6 (Happy Eyeballs) |
Connected to localhost (::1) port 80 |
TCP 连接成功建立 |
> GET / HTTP/1.1 |
请求行 (CRLF → ⏎) |
< HTTP/1.1 200 OK |
响应状态行 |
Server: nginx/1.24.0 |
服务器软件及版本 |
Content-Length: 615 |
响应体 615 字节 |
Connection: keep-alive |
TCP 连接保持 (可复用) |
ETag |
资源版本标识 (用于缓存验证/条件请求) |
实验 6-2:跨服务器 HTTP 通信
从 tcpip-01 curl 到 tcpip-02 和 tcpip-03 的 nginx:
bash
# tcpip-01 → tcpip-02
curl -sI http://192.168.0.207/
# tcpip-01 → tcpip-03
curl -sI http://192.168.0.111/
实操输出:
=== tcpip-01 → tcpip-02 ===
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Tue, 02 Jun 2026 04:19:50 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 02 Jun 2026 04:12:50 GMT
Connection: keep-alive
ETag: "6a1e5842-267"
=== tcpip-01 → tcpip-03 ===
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Tue, 02 Jun 2026 04:19:51 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 02 Jun 2026 04:13:38 GMT
Connection: keep-alive
ETag: "6a1e5872-267"
观察: 三台服务器的 nginx 返回不同的 ETag (6a1e5814 / 6a1e5842 / 6a1e5872),说明虽然是相同版本,但 index.html 的修改时间不同,导致 hash 不同。
实验 6-3:curl 请求百度 HTTPS
bash
curl -sI https://www.baidu.com
实操输出:
HTTP/1.1 200 OK
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Content-Length: 0
Content-Type: text/html
Pragma: no-cache
Server: bfe
Date: Tue, 02 Jun 2026 04:17:40 GMT
百度响应头分析:
| 头字段 | 值 | 含义 |
|---|---|---|
Server: bfe |
百度前端 (Baidu Front End) | 百度的自研反向代理服务器 |
Cache-Control: private, no-cache... |
严格禁止缓存 | 百度首页内容动态生成,强制不缓存 |
Content-Length: 0 |
响应体为空 | 因为用 -I (HEAD 请求),服务器不返回 body |
实验 6-4:OpenSSL --- HTTPS 证书分析
bash
# 查看百度 HTTPS 证书
echo | openssl s_client -connect www.baidu.com:443 -servername www.baidu.com 2>&1 | \
openssl x509 -noout -subject -dates -issuer
| 参数 | 含义 |
|---|---|
s_client |
SSL/TLS 客户端 |
-connect |
连接到目标主机:端口 |
-servername |
SNI (Server Name Indication),用于多虚拟主机 |
x509 -noout |
不输出证书内容,仅显示指定字段 |
-subject |
证书主题 (域名) |
-dates |
有效期起止 |
-issuer |
签发机构 |
实操输出:
subject=C = CN, ST = beijing, L = beijing,
O = "Beijing Baidu Netcom Science Technology Co., Ltd",
CN = baidu.com
notBefore=Jul 9 07:01:02 2025 GMT
notAfter=Aug 10 07:01:01 2026 GMT
issuer=C = BE, O = GlobalSign nv-sa, CN = GlobalSign RSA OV SSL CA 2018
证书分析:
| 字段 | 值 | 含义 |
|---|---|---|
| CN | baidu.com | Common Name,证书绑定的域名 |
| O | Beijing Baidu Netcom... | Organization,百度网讯 |
| C | CN | Country,中国 |
| notBefore | 2025-07-09 | 证书签发/生效日期 |
| notAfter | 2026-08-10 | 证书到期日期 (约 13 个月有效期) |
| issuer | GlobalSign | 证书颁发机构 (CA),OV 级别 (Organization Validated) |
🎯 第三部分:安全攻防篇
第7章:网络攻击实验
⚠️ 重要声明: 本章所有实验仅用于教育目的,且仅在用户自有服务器/授权的实验环境中进行。对未经授权的系统进行任何形式的网络攻击都是违法的。
7.1 SYN Flood 攻击原理
SYN Flood 是经典的 DDoS 攻击方式,攻击者发送大量伪造源 IP 的 SYN 包,使目标服务器创建大量半连接 (SYN_RCVD 状态),耗尽资源。
正常 TCP 握手: SYN Flood 攻击:
客户端 服务器 攻击者 目标服务器
│──SYN────────────►│ │──SYN(fake IP1)──►│
│ │ (SYN_RCVD) │──SYN(fake IP2)──►│
│◄──SYN+ACK─────── │ │──SYN(fake IP3)──►│
│──ACK────────────►│ │──SYN(fake IP4)──►│
│ │ (ESTABLISHED) │ │ (SYN_RCVD × N)
│══ 正常通信 ═══════│ │ ... │ (SYN backlog 满)
│ │ (拒绝新连接)
防御措施:
- SYN Cookie --- 不分配完整资源,通过加密算法验证客户端
- 增大 backlog ---
net.core.somaxconn参数 - SYN Proxy --- 反向代理拦截并验证 SYN
- 限速/清洗 --- DDoS 防护设备
实验 7-1:SYN Flood 演示 (仅限实验环境)
bash
# 在 tcpip-01 上对 tcpip-02 的 nginx 发送 SYN Flood
# ⚠️ 仅实验环境使用!
sudo hping3 -S -p 80 --flood 192.168.0.207
| 参数 | 含义 |
|---|---|
-S |
发送 SYN 包 |
-p 80 |
目标端口 80 |
--flood |
洪水模式 (最快速度发送,不等待响应) |
--rand-source |
随机源 IP (更真实的 DDoS) |
7.2 ICMP 重定向攻击
ICMP 重定向 (Type 5) 是路由器告知主机"有更优路径"的机制。但在局域网中,攻击者可以伪造 ICMP 重定向消息,篡改目标主机的路由表。
bash
# ICMP 重定向实验
sudo hping3 --icmp --icmp-redirect 目标IP
⚠️ 现代操作系统通常默认忽略 ICMP 重定向。 Linux 中可以通过
sysctl net.ipv4.conf.all.accept_redirects查看配置。Ubuntu 24.04 默认值为 0 (不接受)。
7.3 上机实操 7:端口扫描与安全检测
实验 7-2:nmap 全端口扫描
bash
# SYN 扫描 (需要 root)
sudo nmap -sS -p 1-65535 192.168.0.207
# 快速扫描常用端口
nmap -F 192.168.0.207
# 操作系统检测
sudo nmap -O 192.168.0.207
实验 7-3:使用 hping3 构造自定义数据包
bash
# 发送自定义 TCP SYN 包
sudo hping3 -S -p 80 -c 3 192.168.0.207
# 发送 ICMP Echo Request
sudo hping3 -1 -c 3 192.168.0.207
# 发送带自定义数据的包
sudo hping3 -S -p 80 -d 100 -E /tmp/data.txt 192.168.0.207
| 参数 | 含义 |
|---|---|
-1 |
ICMP 模式 |
-c 3 |
发送 3 个包 |
-d 100 |
数据部分 100 字节 |
-E file |
从文件读取数据 |
第8章:网络监控和诊断
8.1 网络监控工具
网络监控是运维和安全的基础能力。本章介绍 tcpdump 深度使用和 Wireshark 过滤器。
8.2 上机实操 8:网络诊断
实验 8-1:tcpdump 深度使用
tcpdump 是命令行抓包工具,可以捕获经过网卡的数据包并保存为 pcap 文件供分析。
bash
# 基本抓包
sudo tcpdump -i eth0 -nn -w capture.pcap
# 抓取特定主机
sudo tcpdump -i eth0 -nn host 192.168.0.207
# 抓取特定端口
sudo tcpdump -i eth0 -nn port 80
# 抓取特定协议
sudo tcpdump -i eth0 -nn tcp
sudo tcpdump -i eth0 -nn icmp
sudo tcpdump -i eth0 -nn arp
tcpdump 常用过滤器 (BPF - Berkeley Packet Filter):
| 过滤器 | 含义 |
|---|---|
host 192.168.0.207 |
源或目标是该 IP |
src host 192.168.0.5 |
仅源 IP |
dst host 192.168.0.207 |
仅目标 IP |
port 80 |
源或目标端口 80 |
tcp port 80 |
TCP 端口 80 |
udp port 53 |
UDP 端口 53 |
| `tcptcpflags & (tcp-syn | tcp-fin) != 0` |
net 192.168.0.0/24 |
指定网段 |
ether host fa:16:3e:5a:b2:b7 |
指定 MAC 地址 |
tcpdump 输出格式详解:
12:19:59.576960 IP 192.168.0.5.38224 > 192.168.0.207.80: Flags [S], seq 305779680
│ │ │ │ │ │ │
│ │ │ 源端口 目标IP 目标端口 序列号
│ │ 源IP
│ 时间戳 (HH:MM:SS.us)
协议类型
实验 8-2:tcpdump + tcpdump 读写 pcap
bash
# 保存抓包文件
sudo tcpdump -i eth0 -nn -c 100 -w /tmp/capture.pcap
# 读取并分析已保存的 pcap 文件
tcpdump -nn -r /tmp/capture.pcap
# 读取时应用过滤器
tcpdump -nn -r /tmp/capture.pcap 'tcp port 80'
# 查看更多细节
tcpdump -nn -v -r /tmp/capture.pcap # verbose
tcpdump -nn -X -r /tmp/capture.pcap # 显示 HEX + ASCII
tcpdump -nn -A -r /tmp/capture.pcap # 仅显示 ASCII (适合 HTTP)
| 参数 | 含义 |
|---|---|
-i eth0 |
监听的网络接口 |
-nn |
不解析主机名和端口名 (显示 IP 和端口号) |
-w file |
写入 pcap 文件 |
-r file |
读取 pcap 文件 |
-c 100 |
抓取 100 个包后停止 |
-v / -vv / -vvv |
详细级别 (TTL, ID, offset 等) |
-X |
以 HEX + ASCII 显示包内容 |
-A |
以 ASCII 显示包内容 (查看 HTTP 明文) |
-s 0 |
捕获完整包 (snaplen=0 表示不截断) |
-e |
显示链路层头 (MAC 地址) |
实验 8-3:Wireshark 基本使用
Wireshark 是图形化抓包分析工具。可以将 tcpdump 保存的 pcap 文件导入 Wireshark 进行深度分析。
Wireshark 常用显示过滤器:
| 过滤器 | 用途 |
|---|---|
ip.addr == 192.168.0.207 |
过滤特定 IP |
tcp.port == 80 |
过滤特定端口 |
tcp.flags.syn == 1 |
显示 SYN 包 |
tcp.flags.reset == 1 |
显示 RST 包 |
http |
显示 HTTP 流量 |
dns |
显示 DNS 流量 |
icmp |
显示 ICMP 流量 |
tcp.stream eq 0 |
跟踪特定 TCP 流 |
!(arp or icmp or dns) |
排除控制流量 |
实验 8-4:综合网络诊断流程
当遇到网络问题时,按以下顺序诊断:
网络故障诊断流程:
1. ping 目标IP → 检查基础连通性 (ICMP)
2. traceroute 目标IP → 检查路由路径
3. nslookup/dig 域名 → 检查 DNS 解析
4. curl -v 目标URL → 检查 HTTP 层
5. telnet/nc 目标IP 端口 → 检查端口可达性
6. tcpdump 抓包 → 深入分析数据包
7. ss -tan → 检查连接状态
8. route -n → 检查路由表
9. arp -a → 检查 ARP 缓存
10. ethtool / ip link → 检查物理层状态
实验 8-5:iftop / nethogs --- 流量/进程监控
bash
# 安装
sudo apt-get install -y iftop nethogs
# 查看实时流量 (按主机)
sudo iftop -i eth0
# 查看实时流量 (按进程)
sudo nethogs eth0
📊 第四部分:综合项目篇
第9章:Socket 网络编程实践
9.1 Socket 编程基础
Socket (套接字) 是操作系统提供的网络编程接口。它抽象了底层 TCP/IP 协议的复杂性,开发者只需关注 "IP + 端口" 即可完成网络通信。
Socket 通信模型:
┌──────────────┐ ┌──────────────┐
│ Server │ │ Client │
│ │ │ │
│ socket() │ 创建套接字 │ socket() │
│ ↓ │ │ ↓ │
│ bind() │ 绑定 IP:Port │ │
│ ↓ │ │ │
│ listen() │ 开始监听 │ │
│ ↓ │ │ │
│ accept() ◄─┼────── SYN ────────────────│ connect() │
│ ↓ │ │ ↓ │
│ recv() ◄─┼────── 数据请求 ──────────│ send() │
│ ↓ │ │ ↓ │
│ send() ──┼────── 数据响应 ──────────►│ recv() │
│ ↓ │ │ ↓ │
│ close() ──┼────── FIN ──────────► │ close() │
└──────────────┘ └──────────────┘
TCP Socket 关键函数
| 函数 | 参数 | 含义 |
|---|---|---|
socket() |
AF_INET, SOCK_STREAM | 创建 IPv4 TCP 套接字 |
bind() |
(host, port) | 绑定到指定 IP 和端口 |
listen() |
backlog | 开始监听,设置等待队列长度 |
accept() |
--- | 阻塞等待客户端连接,返回新 socket |
connect() |
(host, port) | 连接到远程服务器 |
send()/recv() |
buffer, size | 发送/接收数据 |
close() |
--- | 关闭连接 |
9.2 上机实操 9:简易聊天程序
实验 9-1:TCP Echo 服务器
在 tcpip-01 上创建:
python
#!/usr/bin/env python3
"""
TCP Echo Server --- 将客户端发来的数据原样返回
"""
import socket
HOST = '0.0.0.0' # 监听所有接口
PORT = 8888
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen(5)
print(f"[*] TCP Echo Server listening on {HOST}:{PORT}")
while True:
conn, addr = server.accept()
with conn:
print(f"[+] Connection from {addr[0]}:{addr[1]}")
while True:
data = conn.recv(1024)
if not data:
break
print(f"[<] Received: {data.decode().strip()}")
conn.sendall(data) # Echo back
print(f"[-] Connection closed: {addr[0]}:{addr[1]}")
参数详解:
| 参数 | 含义 |
|---|---|
AF_INET |
Address Family: IPv4 |
SOCK_STREAM |
Socket Type: TCP (流式) |
SO_REUSEADDR |
允许重用 TIME_WAIT 状态的端口 |
listen(5) |
最大等待连接数 (backlog) |
recv(1024) |
一次最多接收 1024 字节 |
sendall() |
发送全部数据 (自动处理部分发送) |
实验 9-2:TCP 客户端
python
#!/usr/bin/env python3
"""TCP Client --- 连接服务器并发送消息"""
import socket
HOST = '127.0.0.1'
PORT = 8888
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
client.connect((HOST, PORT))
message = "Hello from client!"
client.sendall(message.encode())
data = client.recv(1024)
print(f"Received: {data.decode()}")
实验 9-3:UDP 通信示例
python
#!/usr/bin/env python3
"""UDP Server + Client 示例"""
import socket
# UDP Server
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('0.0.0.0', 9999))
print("[*] UDP Server on :9999")
data, addr = server.recvfrom(1024)
print(f"Received from {addr}: {data.decode()}")
server.sendto(b"UDP ACK", addr)
server.close()
# UDP Client
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.sendto(b"Hello UDP!", ('127.0.0.1', 9999))
data, addr = client.recvfrom(1024)
print(f"Response: {data.decode()}")
client.close()
Socket 编程最佳实践:
- 使用
with语句自动管理 socket 生命周期- 设置
SO_REUSEADDR避免 "Address already in use" 错误- TCP 使用
sendall()而非send(),确保完整发送- 使用
setsockopt()设置超时 (SO_RCVTIMEO)- 正确处理
ConnectionError异常
第10章:网络服务搭建
10.1 Web 服务器配置
Web 服务器是网络协议最典型的应用场景。本章在 ecs-ee63 集群上配置 Nginx 服务器(已安装在第1章),并深入配置虚拟主机和 SSL 证书。
10.2 上机实操 10:Nginx + SSL 配置
实验 10-1:Nginx 基础配置分析
查看 Nginx 主配置文件:
bash
cat /etc/nginx/nginx.conf
关键配置参数:
| 配置 | 默认值 | 含义 |
|---|---|---|
worker_processes |
auto | Worker 进程数 = CPU 核心数 |
worker_connections |
768 | 每个 worker 最大并发连接数 |
sendfile |
on | 使用 sendfile() 系统调用 (零拷贝) |
tcp_nopush |
on | 在 sendfile 模式下优化发送 |
keepalive_timeout |
65 | Keep-Alive 超时时间 |
gzip |
on | 启用 gzip 压缩 |
💡 Nginx 架构: Nginx 采用多进程模型,1 个 Master 进程 (管理) + N 个 Worker 进程 (处理请求)。Worker 之间通过共享内存和锁进行通信。
实验 10-2:配置虚拟主机 (Virtual Host)
在 /etc/nginx/sites-available/ 下创建新站点配置:
bash
sudo tee /etc/nginx/sites-available/tcpip-lab << 'EOF'
server {
listen 8080;
server_name _;
root /var/www/tcpip-lab;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location /api {
return 200 '{"status": "ok", "server": "tcpip-lab"}\n';
add_header Content-Type application/json;
}
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
}
EOF
# 创建文档根目录
sudo mkdir -p /var/www/tcpip-lab
echo "<h1>TCP/IP Lab Server</h1>" | sudo tee /var/www/tcpip-lab/index.html
# 启用站点
sudo ln -s /etc/nginx/sites-available/tcpip-lab /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
实验 10-3:自签名 SSL 证书配置
bash
# 生成自签名证书 (有效期 365 天)
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/nginx-selfsigned.key \
-out /etc/ssl/certs/nginx-selfsigned.crt \
-subj "/C=CN/ST=Guangdong/L=Shenzhen/O=TCPIP Lab/CN=lab.local"
# 创建 DH 参数 (增强安全性)
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
OpenSSL 参数详解:
| 参数 | 含义 |
|---|---|
req -x509 |
生成 X.509 自签名证书 |
-nodes |
私钥不加密 (No DES) |
-days 365 |
证书有效期 365 天 |
-newkey rsa:2048 |
生成新的 2048 位 RSA 密钥 |
-keyout |
私钥输出路径 |
-out |
证书输出路径 |
-subj |
证书主题 (CN=通用名称, O=组织, C=国家) |
实验 10-4:配置 HTTPS 虚拟主机
bash
sudo tee /etc/nginx/sites-available/tcpip-lab-ssl << 'EOF'
server {
listen 443 ssl http2;
server_name _;
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
ssl_dhparam /etc/nginx/dhparam.pem;
# SSL 协议和密码套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /var/www/tcpip-lab;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/tcpip-lab-ssl /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
SSL 配置参数详解:
| 参数 | 值 | 含义 |
|---|---|---|
ssl_protocols |
TLSv1.2 TLSv1.3 | 仅允许安全的 TLS 版本 (禁用 SSLv3/TLSv1.0/1.1) |
ssl_ciphers |
ECDHE-... | 密码套件字符串,ECDHE 支持前向保密 (Forward Secrecy) |
ssl_prefer_server_ciphers |
on | 由服务器决定使用的密码套件 (而非客户端) |
ssl_session_cache |
shared:SSL:10m | 10MB 共享内存 SSL 会话缓存 |
ssl_session_timeout |
10m | 会话缓存 10 分钟 |
ssl_dhparam |
dhparam.pem | DH (Diffie-Hellman) 参数,用于密钥交换 |
实验 10-5:测试 HTTPS 配置
bash
# 使用 curl 测试 (忽略证书验证)
curl -kv https://localhost/
# 使用 OpenSSL 测试
echo | openssl s_client -connect localhost:443 2>&1 | \
grep -E '(Protocol|Cipher|subject)'
预期输出:
Protocol: TLSv1.3
Cipher: TLS_AES_256_GCM_SHA384
subject=CN = lab.local
📝 附录
附录 A:实验环境全貌
A.1 集群服务器总览
┌─────────────────────────────────────────────────────────────────────┐
│ 华为云 ecs-ee63 TCP/IP 实验集群 │
├──────────┬─────────────┬──────────────┬──────────┬─────────────────┤
│ 角色 │ 实例名 │ 公网 IP │ 私网 IP │ 服务 │
├──────────┼─────────────┼──────────────┼──────────┼─────────────────┤
│ Server A │ ecs-ee63-0003│ 119.8.59.15 │ .5 │ nginx, SSH, DNS │
│ Server B │ ecs-ee63-0002│ 119.8.110.76 │ .207 │ nginx, SSH, DNS │
│ Server C │ ecs-ee63-0001│150.40.239.207│ .111 │ nginx, SSH, DNS │
│ Server D │ ecs-ee63-0004│159.138.137.101│ .44 │ (不可达) │
├──────────┴─────────────┴──────────────┴──────────┴─────────────────┤
│ 规格: c6.large_2 (2vCPU / 4GiB) | OS: Ubuntu 24.04 LTS │
│ VPC: 192.168.0.0/24 | 网关: 192.168.0.1 | Metadata: 169.254.169.254│
└─────────────────────────────────────────────────────────────────────┘
A.2 三台服务器路由表对比
| 路由条目 | tcpip-01 | tcpip-02 | tcpip-03 |
|---|---|---|---|
| default gateway | 192.168.0.1 | 192.168.0.1 | 192.168.0.1 |
| metadata (169.254.169.254) | via 192.168.0.254 | via 192.168.0.254 | via 192.168.0.254 |
| local (192.168.0.0/24) | direct | direct | direct |
A.3 关键 MAC 地址速查
| 设备 | MAC 地址 | 说明 |
|---|---|---|
| tcpip-01 (ecs-ee63-0003) | fa:16:3e:5a:b2:b7 | Server A |
| tcpip-02 (ecs-ee63-0002) | fa:16:3e:5a:b2:81 | Server B |
| tcpip-03 (ecs-ee63-0001) | fa:16:3e:5a:b2:21 | Server C |
| VPC 网关 (192.168.0.1) | fa:16:3e:66:fd:20 | 所有服务器共用 |
| Metadata (192.168.0.254) | fa:fa:fa:fa:fa:01 | 华为云 metadata 服务 |
附录 B:常用命令速查表
B.1 网络配置命令
| 命令 | 用途 | 示例 |
|---|---|---|
ip addr show |
查看 IP 地址 | ip -4 addr show eth0 |
ip link set eth0 up/down |
启用/禁用接口 | --- |
ip route show |
查看路由表 | ip route get 8.8.8.8 |
ip neigh show |
查看 ARP 缓存 | ip neigh flush all |
hostnamectl set-hostname |
修改主机名 | --- |
resolvectl status |
DNS 配置 | --- |
B.2 抓包分析命令
| 命令 | 用途 | 示例 |
|---|---|---|
tcpdump -i eth0 -nn port 80 |
抓取 HTTP 流量 | -w file.pcap 保存 |
tcpdump -r file.pcap |
读取 pcap | -nn 'tcp[tcpflags] & tcp-syn != 0' |
tcpdump -A port 80 |
ASCII 查看 HTTP | 查看明文 HTTP |
tcpdump -X port 443 |
HEX+ASCII 查看 | 分析二进制协议 |
wireshark file.pcap |
图形化分析 | --- |
B.3 网络诊断命令
| 命令 | 用途 | 层级 |
|---|---|---|
ping -c 4 host |
连通性测试 | L3 (ICMP) |
traceroute host |
路由追踪 | L3 (UDP/ICMP) |
mtr host |
实时路由追踪 | L3 |
dig domain A |
DNS 查询 | L7 |
nslookup domain |
DNS 查询 | L7 |
curl -v url |
HTTP 调试 | L7 |
nc -zv host port |
端口扫描 | L4 |
ss -tlnp |
监听端口 | L4 |
ss -tan |
所有连接 | L4 |
nmap -sS host |
SYN 端口扫描 | L4 |
iperf3 -c host |
带宽测试 | L4 |
arp -a |
ARP 缓存 | L2 |
ethtool eth0 |
网卡信息 | L2 |
iftop -i eth0 |
流量监控 | L2-L4 |
B.4 安全测试命令
| 命令 | 用途 | ⚠️ 风险 |
|---|---|---|
nmap -sV -O host |
服务/系统检测 | 低 |
nmap --script vuln host |
漏洞扫描 | 中 (可能触发 IDS) |
hping3 -S -p port --flood host |
SYN Flood | 高 (仅实验环境) |
hping3 --icmp --icmp-redirect |
ICMP 重定向 | 高 (仅实验环境) |
附录 C:TCP 协议栈性能调优
C.1 内核参数优化
这些参数可在 /etc/sysctl.conf 中修改,用于优化 TCP 性能:
bash
# 增加 TCP 缓冲区
net.core.rmem_max = 134217728 # 最大接收缓冲区 128MB
net.core.wmem_max = 134217728 # 最大发送缓冲区 128MB
net.ipv4.tcp_rmem = 4096 87380 134217728 # 接收缓冲区 (min, default, max)
net.ipv4.tcp_wmem = 4096 65536 134217728 # 发送缓冲区 (min, default, max)
# TCP 连接优化
net.core.somaxconn = 65535 # 最大监听队列
net.ipv4.tcp_max_syn_backlog = 65535 # SYN 队列长度
net.ipv4.tcp_tw_reuse = 1 # 允许重用 TIME_WAIT socket
# TCP Keep-Alive
net.ipv4.tcp_keepalive_time = 600 # 空闲 600 秒后发送 keepalive
net.ipv4.tcp_keepalive_intvl = 60 # keepalive 探测间隔
net.ipv4.tcp_keepalive_probes = 3 # 探测次数
# 启用 TCP Fast Open (TFO)
net.ipv4.tcp_fastopen = 3 # 3 = client + server
# 启用 BBR 拥塞控制算法 (Ubuntu 24.04 默认)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# 应用配置
sudo sysctl -p
关键参数详解:
| 参数 | 含义 | 影响 |
|---|---|---|
tcp_rmem / tcp_wmem |
TCP 缓冲区自动调优范围 | 大文件传输性能 |
somaxconn |
最大 accept 队列长度 | 高并发请求 |
tcp_tw_reuse |
TIME_WAIT 复用 | 减少短连接端口耗尽 |
tcp_fastopen = 3 |
TCP Fast Open | 减少握手 RTT |
tcp_congestion_control = bbr |
BBR (Bottleneck Bandwidth and RTT) | Google 的拥塞控制算法,比 CUBIC 更好 |
C.2 查看当前内核参数
bash
# 查看 TCP 拥塞控制算法
sysctl net.ipv4.tcp_congestion_control
# 查看所有 TCP 参数
sysctl -a | grep tcp
附录 D:参考资源和推荐读物
D.1 必读 RFC 文档
| RFC | 标题 | 作者 | 年份 |
|---|---|---|---|
| RFC 791 | Internet Protocol (IPv4) | Jon Postel | 1981 |
| RFC 793 | Transmission Control Protocol (TCP) | Jon Postel | 1981 |
| RFC 792 | Internet Control Message Protocol (ICMP) | Jon Postel | 1981 |
| RFC 826 | Ethernet Address Resolution Protocol (ARP) | David Plummer | 1982 |
| RFC 1034/1035 | Domain Names --- Concepts & Implementation | Mockapetris | 1987 |
| RFC 2616 | HTTP/1.1 | Fielding et al. | 1999 |
| RFC 8446 | TLS 1.3 | Rescorla | 2018 |
| RFC 9000 | QUIC: A UDP-Based Multiplexed Transport | Iyengar & Thomson | 2021 |
D.2 推荐书籍
| 书名 | 作者 | 适合阶段 | 说明 |
|---|---|---|---|
| 《TCP/IP 详解 卷1:协议》 | W. Richard Stevens | 基础→进阶 | 必读经典,深入协议细节 |
| 《计算机网络:自顶向下方法》 | Kurose & Ross | 入门→基础 | 适合系统学习网络 |
| 《Wireshark 网络分析就这么简单》 | 林沛满 | 实操 | 实战抓包分析 |
| 《HTTP 权威指南》 | Gourley & Totty | HTTP 专项 | HTTP 协议全方位 |
D.3 在线工具
| 工具 | 网址 | 用途 |
|---|---|---|
| Wireshark | wireshark.org | 图形化抓包分析 |
| PacketLife | packetlife.net | Cheat Sheets & 协议详解 |
| CloudShark | cloudshark.org | 在线 pcap 分析/分享 |
| Nmap 在线手册 | nmap.org/book/man.html | Nmap 完整参考 |
| SSL Labs | ssllabs.com/ssltest | SSL/TLS 配置检测 |
| DNS 检测 | dnschecker.org | 全球 DNS 传播检测 |
附录 E:实验踩坑记录
踩坑 1:ifconfig 找不到
问题: Ubuntu 24.04 默认不安装 net-tools,执行 ifconfig 提示 command not found。
解决:
bash
sudo apt-get install -y net-tools
教训: 建议习惯使用 iproute2 工具 (ip addr, ip route, ip neigh),它是 net-tools 的现代替代品。
踩坑 2:tcpdump 后台运行无法停止
问题: 在 SSH 会话中 tcpdump -w file &,SSH 断连后进程仍在运行,消耗磁盘空间。
解决: 使用 timeout 限制运行时间,或使用 -c 限制捕获包数:
bash
timeout 10 tcpdump -i eth0 -w /tmp/cap.pcap
tcpdump -i eth0 -c 100 -w /tmp/cap.pcap
踩坑 3:nmap -sS 需要 root 权限
问题: 以普通用户执行 nmap -sS 报 requires root privileges。
原因: SYN 扫描需要构造原始套接字 (Raw Socket),必须 root 权限。
解决: sudo nmap -sS target 或使用 TCP Connect 扫描:nmap -sT target
踩坑 4:curl 优先使用 IPv6
问题: curl http://localhost 连接 [::1]:80 而非 127.0.0.1:80。
原因: curl 默认使用 Happy Eyeballs 算法,优先尝试 IPv6。
解决: 使用 -4 强制 IPv4:curl -4 http://localhost/
踩坑 5:Server D 不可达
问题: ecs-ee63-0004 (159.138.137.101) SSH 连接超时,ping 100% 丢包。
原因: 可能的安全组未开放 ICMP 和 SSH 端口,或该实例未分配公网 IP/已关机。
处理: 在博客中标注该服务器不可达,仅使用前 3 台进行实验。
踩坑 6:systemd-resolved DNS stub resolver
问题: 修改 /etc/resolv.conf 后重启被覆盖。
原因: Ubuntu 24.04 使用 systemd-resolved 管理 DNS,/etc/resolv.conf 是符号链接到 /run/systemd/resolve/stub-resolv.conf。
解决: 使用 resolvectl 管理 DNS 配置:
bash
resolvectl status # 查看 DNS 状态
resolvectl dns eth0 8.8.8.8 # 设置接口 DNS
resolvectl flush-caches # 清除 DNS 缓存
附录 F:协议端口号速查
| 端口 | 协议 | 服务 | 传输层 |
|---|---|---|---|
| 20/21 | FTP | 文件传输 | TCP |
| 22 | SSH | 安全 Shell | TCP |
| 23 | Telnet | 远程登录 (不安全) | TCP |
| 25 | SMTP | 邮件发送 | TCP |
| 53 | DNS | 域名解析 | UDP/TCP |
| 67/68 | DHCP | 动态 IP 分配 | UDP |
| 80 | HTTP | Web | TCP |
| 110 | POP3 | 邮件接收 | TCP |
| 143 | IMAP | 邮件接收 | TCP |
| 443 | HTTPS | 安全 Web | TCP (TLS) |
| 993 | IMAPS | 安全 IMAP | TCP (TLS) |
| 995 | POP3S | 安全 POP3 | TCP (TLS) |
| 3306 | MySQL | 数据库 | TCP |
| 5432 | PostgreSQL | 数据库 | TCP |
| 6379 | Redis | 缓存 | TCP |
| 8080 | HTTP-Alt | Web (替代) | TCP |
🎓 学习路线图总结
入门阶段 进阶阶段 实战阶段
(第1-3章) (第4-6章) (第7-10章)
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ TCP/IP │───►│ ARP 协议 │───►│ SYN Flood│───►│ Socket │
│ 基础概念 │ │ 地址解析 │ │ 攻击防御 │ │ 编程实践 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ IP/ICMP │───►│ DNS 协议 │───►│ ICMP 攻击│───►│ Web 服务 │
│ 网络层 │ │ 域名解析 │ │ 重定向 │ │ Nginx │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ TCP/UDP │───►│ HTTP(S) │───►│ 网络监控 │───►│ SSL/TLS │
│ 传输层 │ │ 应用层 │ │ 诊断工具 │ │ 证书管理 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
核心能力: tcpdump 抓包分析 → Wireshark 图形化 → nmap 安全扫描 → iperf3 性能测试
附录 G:TCP 拥塞控制算法深度对比
TCP 拥塞控制是影响网络性能的核心机制。以下是主流算法的对比:
G.1 算法演进史
1988 ── TCP Tahoe ── 首个拥塞控制算法 (慢启动 + 拥塞避免 + 快速重传)
│
1990 ── TCP Reno ─── 增加快速恢复 (Fast Recovery)
│
2004 ── BIC/CUBIC ── 针对高带宽延迟积 (BDP) 优化 (Linux 默认至 4.9)
│
2016 ── BBR ──────── Google 推出,基于带宽和 RTT 建模 (Linux 4.9+)
│
2023 ── BBRv3 ────── 改进公平性和收敛速度
| 算法 | 核心思想 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|---|
| Reno | AIMD (加性增/乘性减) | 低带宽/低延迟 | 简单、公平性好 | 高 BDP 下效率低 |
| CUBIC | 三次函数调整窗口 | 高带宽/一般延迟 | Linux 内核默认,公平 | 缓冲区膨胀 (Bufferbloat) |
| BBR | 基于速率模型 | 高延迟/有损链路 | 高吞吐、低延迟 | 公平性较差 |
| Vegas | 基于 RTT 延迟 | 有线网络 | 提前检测拥塞 | 与 Reno 竞争不公平 |
G.2 查看和切换拥塞控制算法
bash
# 查看当前使用的算法
sysctl net.ipv4.tcp_congestion_control
# 输出: net.ipv4.tcp_congestion_control = bbr (Ubuntu 24.04 默认)
# 查看可用算法
sysctl net.ipv4.tcp_available_congestion_control
# 加载 cubic 模块 (模块化设计的算法需要显式加载)
modprobe tcp_cubic
# 临时切换 (立即生效)
sudo sysctl -w net.ipv4.tcp_congestion_control=cubic
# 永久配置 (重启后生效)
echo "net.ipv4.tcp_congestion_control = bbr" | sudo tee -a /etc/sysctl.conf
附录 H:Wireshark 高级过滤器实战
虽然 tcpdump 是命令行利器,但 Wireshark 的图形化过滤和分析能力无可替代。
H.1 显示过滤器 (Display Filter) 速查
bash
# === 基础过滤 ===
ip.addr == 192.168.0.207 # 所有与指定 IP 相关的包
!(ip.addr == 192.168.0.1) # 排除网关流量
ip.src == 192.168.0.5 # 仅源 IP
ip.dst == 192.168.0.207 # 仅目标 IP
# === TCP 过滤 ===
tcp.port == 80 # TCP 端口 80
tcp.flags.syn == 1 && tcp.flags.ack == 0 # 仅 SYN (不含 ACK)
tcp.flags.reset == 1 # RST 包 (连接被拒绝)
tcp.flags.fin == 1 # FIN 包 (连接关闭)
tcp.analysis.retransmission # TCP 重传包
tcp.analysis.duplicate_ack # 重复确认 (dup ACK)
# === 协议过滤 ===
http # 所有 HTTP 流量
http.request.method == "GET" # HTTP GET 请求
http.response.code == 404 # HTTP 404 响应
dns # 所有 DNS 流量
dns.qry.name contains "baidu" # 包含 baidu 的 DNS 查询
icmp # 所有 ICMP 流量
arp # 所有 ARP 流量
# === 高级组合 ===
tcp.port == 80 && ip.addr == 192.168.0.207 # AND 组合
http || dns # OR 组合
tcp.stream eq 5 # 跟踪第5个 TCP 流
frame.time_relative < 5 # 前5秒的包
H.2 Wireshark 分析功能
| 功能 | 菜单位置 | 用途 |
|---|---|---|
| Follow TCP Stream | Analyze → Follow → TCP Stream | 还原 TCP 流的完整对话 |
| Expert Info | Analyze → Expert Info | 自动检测协议异常 |
| IO Graph | Statistics → IO Graph | 流量时间图 |
| Flow Graph | Statistics → Flow Graph | TCP 连接时间线 |
| Protocol Hierarchy | Statistics → Protocol Hierarchy | 协议占比统计 |
| Conversations | Statistics → Conversations | 会话统计 (流量排行) |
| RTT Graph | Statistics → TCP Stream Graph → RTT | TCP 往返时间图 |
H.3 tshark --- 命令行版 Wireshark
在无 GUI 的服务器上,可以用 tshark (Wireshark 的命令行版) 做高级分析:
bash
# 安装
sudo apt-get install -y tshark
# 实时抓包并应用显示过滤器
sudo tshark -i eth0 -Y "tcp.port == 80"
# 读取 pcap 并统计 HTTP 请求方法
tshark -r capture.pcap -Y "http.request" -T fields \
-e http.request.method -e http.request.uri | sort | uniq -c
# 统计每个 IP 的流量
tshark -r capture.pcap -q -z conv,tcp
# 导出 HTTP 对象 (文件)
tshark -r capture.pcap --export-objects http,/tmp/http-files/
附录 I:TCP 状态转换完整详解
理解 TCP 状态机是排查连接问题的关键。以下是完整的状态转换表:
| 状态 | 说明 | 触发条件 (到达) | 离开条件 |
|---|---|---|---|
| CLOSED | 初始/终止状态 | 连接完全关闭 | 主动打开(connect) 或 被动打开(listen) |
| LISTEN | 监听中 | socket → bind → listen | 收到 SYN |
| SYN_SENT | 已发送 SYN | 主动打开 (connect) | 收到 SYN+ACK → ESTABLISHED |
| SYN_RCVD | 已收到 SYN | 收到 SYN (被动方) | 收到 ACK → ESTABLISHED |
| ESTABLISHED | 连接已建立 | 三次握手完成 | 发送/收到 FIN |
| FIN_WAIT_1 | 主动关闭:已发 FIN | 主动 close() | 收到 ACK → FIN_WAIT_2; 收到 FIN → CLOSING |
| FIN_WAIT_2 | 主动关闭:等对方 FIN | 收到 FIN_WAIT_1 的 ACK | 收到 FIN → TIME_WAIT |
| CLOSING | 双方同时关闭 | 收到 FIN (在 FIN_WAIT_1) | 收到 ACK → TIME_WAIT |
| TIME_WAIT | 等待 2MSL | 收到 FIN (在 FIN_WAIT_2) 或 CLOSING 后收到 ACK | 2MSL 超时 → CLOSED |
| CLOSE_WAIT | 被动关闭:等应用关闭 | 收到 FIN (在 ESTABLISHED) | 应用调用 close() → LAST_ACK |
| LAST_ACK | 被动关闭:已发 FIN | 应用调用 close() | 收到 ACK → CLOSED |
I.1 TIME_WAIT 详解
TIME_WAIT 是最容易被误解的状态。它的持续时间是 2MSL (2 × Maximum Segment Lifetime),Linux 上默认 MSL = 60 秒,所以 TIME_WAIT = 120 秒。
TIME_WAIT 存在的两个原因:
-
确保最后的 ACK 能到达对方:如果主动关闭方发送的最后一个 ACK 丢失,被动方会重发 FIN。TIME_WAIT 让主动方能重新发送 ACK。
-
让旧连接的数据包在网络上消失:防止旧连接的延迟包被误认为新连接的数据 (如果新连接使用了相同的四元组)。
TIME_WAIT 问题场景:
主动关闭方 (Client) 被动关闭方 (Server)
│ │
│── FIN ────────────────────►│
│◄── ACK ────────────────────│
│◄── FIN ────────────────────│
│── ACK ────────────────────►│ ← 如果这个 ACK 丢失
│ │
│ TIME_WAIT (120s) │ Server 没收到 ACK
│ │ → 重发 FIN
│ 收到 FIN 重发 ACK │
│ 重启 2MSL 计时 │
TIME_WAIT 过多的问题和解决方案:
bash
# 查看 TIME_WAIT 连接数
ss -tan state time-wait | wc -l
# 解决方案1: 启用 tcp_tw_reuse (客户端复用 TIME_WAIT)
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
# 解决方案2: 减少 TIME_WAIT 连接数 (仅影响特定场景)
sudo sysctl -w net.ipv4.tcp_max_tw_buckets=65536
# 解决方案3: 应用层使用 SO_REUSEADDR
# 在 Python 中: socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
⚠️ 注意:
tcp_tw_recycle在 Linux 4.12+ 已经移除,不要使用。tcp_tw_reuse仅对客户端发出的连接有效。
附录 J:DNS 深度排查实战
J.1 完整 DNS 解析追踪
bash
# 追踪 DNS 递归解析全路径
dig www.baidu.com +trace
# 输出详解:
# . → 根 DNS 服务器列表
# com. → .com 顶级域 NS 记录
# baidu.com. → baidu.com 权威 DNS
# www.baidu.com. → 最终的 A/CNAME 记录
J.2 DNS 记录类型完整示例
bash
# A 记录 (IPv4)
dig example.com A +short
# 93.184.216.34
# AAAA 记录 (IPv6)
dig example.com AAAA +short
# 2606:2800:220:1:248:1893:25c8:1946
# NS 记录 (权威 DNS)
dig baidu.com NS +short
# ns7.baidu.com.
# ns4.baidu.com.
# dns.baidu.com.
# ns3.baidu.com.
# ns2.baidu.com.
# SOA 记录 (域管理信息)
dig baidu.com SOA +short
# dns.baidu.com. sa.baidu.com. 2025060101 300 300 2592000 7200
# └─主DNS────┘ └─管理员邮箱─┘ └序列号─┘└刷新┘└重试┘└过期──┘└否定缓存TTL┘
# TXT 记录 (SPF, DKIM, 验证等)
dig baidu.com TXT +short
# "v=spf1 include:spf1.baidu.com ..." (发送方策略框架)
# "google-site-verification=..."
# PTR 记录 (反向解析: IP → 域名)
dig -x 8.8.8.8 +short
# dns.google.
J.3 systemd-resolved 深度配置
在 Ubuntu 24.04 中,DNS 由 systemd-resolved 管理。了解其配置对网络排查至关重要:
bash
# 查看完整 DNS 状态
resolvectl status
# 全局 DNS 设置 (修改 /etc/systemd/resolved.conf)
sudo tee -a /etc/systemd/resolved.conf << 'EOF'
[Resolve]
DNS=8.8.8.8 114.114.114.114
FallbackDNS=1.1.1.1
DNSSEC=allow-downgrade
DNSOverTLS=opportunistic
EOF
# 重启服务
sudo systemctl restart systemd-resolved
# 验证
resolvectl status
resolved.conf 关键参数:
| 参数 | 含义 | 推荐值 |
|---|---|---|
DNS= |
上游 DNS 服务器 (空格分隔) | 看网络环境 |
FallbackDNS= |
备用 DNS (DNS=无效时使用) | 1.1.1.1 8.8.8.8 |
DNSSEC= |
DNSSEC 验证模式 | allow-downgrade |
DNSOverTLS= |
DNS-over-TLS 加密 | opportunistic |
Cache= |
启用本地 DNS 缓存 | yes (默认) |
附录 K:HTTP 协议进阶 --- 缓存、压缩、CORS
K.1 HTTP 缓存机制
bash
# 条件请求 --- 基于 ETag (If-None-Match)
curl -v -H 'If-None-Match: "6a1e5814-267"' http://localhost/
# 预期: 304 Not Modified (如果 ETag 匹配)
# 如果响应 200, 说明内容已变化
# 条件请求 --- 基于修改时间 (If-Modified-Since)
curl -v -H 'If-Modified-Since: Tue, 02 Jun 2026 04:12:04 GMT' http://localhost/
HTTP 缓存头详解:
| 头字段 | 值示例 | 含义 |
|---|---|---|
Cache-Control: max-age=3600 |
3600 秒 | 缓存有效期 1 小时 |
Cache-Control: no-cache |
--- | 可以缓存但每次需验证 |
Cache-Control: no-store |
--- | 完全不缓存 |
Cache-Control: public |
--- | 任何缓存都可以存储 |
Cache-Control: private |
--- | 仅浏览器缓存 |
ETag: "abc123" |
文件哈希 | 资源版本标识 |
Last-Modified: date |
时间戳 | 资源最后修改时间 |
Expires: date |
绝对时间 | HTTP/1.0 的过期时间 (被 max-age 覆盖) |
Vary: Accept-Encoding |
--- | 告诉缓存按请求头变化分别缓存 |
K.2 HTTP/2 vs HTTP/1.1 对比
| 特性 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 连接复用 | 6-8 个并行连接 | 1 个多路复用连接 |
| 头部压缩 | 无 (明文) | HPACK (二进制压缩) |
| 服务器推送 | 不支持 | Server Push |
| 优先级 | 无 | 流优先级 |
| 二进制协议 | 否 (文本) | 是 (二进制帧) |
| 队头阻塞 | 有 (HOL Blocking) | 减轻 (但仍有 TCP 层 HOL) |
| TLS 要求 | 可选 | 通常强制 (浏览器) |
K.3 CORS (跨域资源共享) 实验
在 tcpip-01 的 nginx 上配置 CORS 头并测试:
bash
# 添加 CORS 配置到 nginx
sudo tee -a /etc/nginx/sites-available/tcpip-lab << 'EOF'
# CORS 头配置
location /api {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type' always;
if ($request_method = 'OPTIONS') {
return 204;
}
return 200 '{"data": "cors test"}\n';
}
EOF
# 测试 CORS --- 模拟跨域请求
curl -v -H "Origin: http://example.com" http://localhost:8080/api
# 应该看到响应头: Access-Control-Allow-Origin: *
# 测试预检请求 (Preflight)
curl -v -X OPTIONS -H "Origin: http://example.com" \
-H "Access-Control-Request-Method: POST" \
http://localhost:8080/api
附录 L:网络安全最佳实践清单
基于本章所学知识,整理一份服务器网络安全加固清单:
L.1 系统层面
bash
# 不需要的端口不要监听 ------ 检查当前监听端口
ss -tlnp
# 分析: 是否需要 29338/29339 (uniagentd)?是否需要本地 DNS (53)?
# 让 nginx 仅监听内网
# 修改 /etc/nginx/sites-available/default
# listen 192.168.0.5:80; (而非 0.0.0.0:80)
L.2 防火墙规则 (iptables/nftables)
bash
# 查看当前规则
sudo iptables -L -n -v
sudo nft list ruleset # Ubuntu 24.04 默认使用 nftables
# 示例: 允许内网访问 nginx,拒绝外网
sudo nft add rule inet filter input iif eth0 ip saddr 192.168.0.0/24 tcp dport 80 accept
sudo nft add rule inet filter input iif eth0 tcp dport 80 drop
L.3 SSH 加固
bash
# 检查 SSH 配置
grep -E '^(PermitRootLogin|PasswordAuthentication|Port)' /etc/ssh/sshd_config
# 推荐配置:
# PermitRootLogin prohibit-password # 禁用 root 密码登录 (允许密钥)
# PasswordAuthentication no # 禁用密码认证
# Port 2222 # 修改默认端口 (减少暴力破解)
L.4 TLS/SSL 加固
bash
# 使用 Mozilla SSL 配置生成器: ssl-config.mozilla.org
# 检测当前 HTTPS 配置
openssl s_client -connect localhost:443 -tlsextdebug 2>&1 | grep -E '(Protocol|Cipher)'
# 扫描 SSL 漏洞 (需要外部工具)
# nmap --script ssl-enum-ciphers -p 443 localhost
L.5 网络安全检查清单
| 检查项 | 命令 | 安全标准 |
|---|---|---|
| 开放端口 | ss -tlnp |
仅必要的端口 |
| 防火墙规则 | sudo iptables -L |
默认 DROP |
| SSH 配置 | grep PermitRootLogin /etc/ssh/sshd_config |
prohibit-password 或 no |
| DNS 配置 | resolvectl status |
使用可信 DNS |
| SSL 证书 | openssl s_client -connect host:443 |
有效的 CA 签发 |
| 内核参数 | sysctl net.ipv4.tcp_syncookies |
= 1 (防 SYN Flood) |
| 不安全协议 | ss -tlnp | grep -E '(21|23)' |
不应有 FTP(21)/Telnet(23) |
附录 M:实验数据汇总 --- tcpdump 完整抓包记录
M.1 跨主机 TCP 通信完整包序列 (10个包)
时间 源 → 目标 标志 序列号 确认号 长度 说明
─────────────────────────────────────────────────────────────────────────────────────────
12:19:59.576960 .5:38224 → .207:80 [S] 305779680 0 0 SYN
12:19:59.577210 .207:80 → .5:38224 [S.] 658854510 305779681 0 SYN+ACK
12:19:59.577227 .5:38224 → .207:80 [.] 305779681 1 0 ACK (握手完成)
12:19:59.577264 .5:38224 → .207:80 [P.] 1:77 1 76 HTTP GET 请求
12:19:59.577412 .207:80 → .5:38224 [.] 1 77 0 ACK 请求
12:19:59.577631 .207:80 → .5:38224 [P.] 1:863 77 862 HTTP 200 响应
12:19:59.577637 .5:38224 → .207:80 [.] 77 863 0 ACK 响应
12:19:59.577700 .5:38224 → .207:80 [F.] 77 863 0 FIN (主动关闭)
12:19:59.577832 .207:80 → .5:38224 [F.] 863 78 0 FIN+ACK
12:19:59.577837 .5:38224 → .207:80 [.] 78 864 0 最终 ACK
─────────────────────────────────────────────────────────────────────────────────────────
总耗时: 0.877ms | 数据量: 76(请求) + 862(响应) = 938 bytes
M.2 本机回环 TCP 通信 (10个包)
时间 源 → 目标 标志 序列号 确认号 长度 说明
────────────────────────────────────────────────────────────────────────────────────
12:19:30.840184 ::1:38536 → ::1:80 [S] 3626205147 0 0 SYN
12:19:30.840197 ::1:80 → ::1:38536 [S.] 833032379 362620.. 0 SYN+ACK
12:19:30.840207 ::1:38536 → ::1:80 [.] 3626205148 1 0 ACK
12:19:30.840259 ::1:38536 → ::1:80 [P.] 1:73 1 72 HTTP GET
12:19:30.840263 ::1:80 → ::1:38536 [.] 1 73 0 ACK
12:19:30.840387 ::1:80 → ::1:38536 [P.] 1:863 73 862 HTTP 200
12:19:30.840406 ::1:38536 → ::1:80 [.] 73 863 0 ACK
12:19:30.840488 ::1:38536 → ::1:80 [F.] 73 863 0 FIN
12:19:30.840518 ::1:80 → ::1:38536 [F.] 863 74 0 FIN+ACK
12:19:30.840531 ::1:38536 → ::1:80 [.] 74 864 0 最终 ACK
────────────────────────────────────────────────────────────────────────────────────
总耗时: 0.347ms | MSS=65476 (lo 接口远大于 eth0 的 1460)
附录 N:Python Socket 编程完整实战
N.1 多线程 TCP Echo 服务器
python
#!/usr/bin/env python3
"""
Multi-threaded TCP Echo Server
支持并发连接的完整实现
"""
import socket
import threading
import signal
import sys
class ThreadedEchoServer:
def __init__(self, host='0.0.0.0', port=8888):
self.host = host
self.port = port
self.server_socket = None
self.running = False
def start(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 设置超时以便能优雅退出
self.server_socket.settimeout(1.0)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(10)
self.running = True
print(f"[*] Echo Server started on {self.host}:{self.port}")
while self.running:
try:
client_socket, addr = self.server_socket.accept()
print(f"[+] New connection: {addr[0]}:{addr[1]}")
# 每个连接使用独立线程处理
thread = threading.Thread(
target=self.handle_client,
args=(client_socket, addr),
daemon=True
)
thread.start()
print(f"[i] Active threads: {threading.active_count() - 1}")
except socket.timeout:
continue # 仅用于退出循环检查
except OSError:
break
self.server_socket.close()
print("[*] Server stopped")
def handle_client(self, client_socket, addr):
"""处理单个客户端连接"""
with client_socket:
client_socket.settimeout(30) # 30秒无数据自动断开
while True:
try:
data = client_socket.recv(4096)
if not data:
break
message = data.decode('utf-8').strip()
print(f"[<] {addr[0]}:{addr[1]} → {message}")
# Echo 并添加时间戳
from datetime import datetime
response = f"[{datetime.now().strftime('%H:%M:%S')}] Echo: {message}"
client_socket.sendall(response.encode('utf-8'))
except socket.timeout:
print(f"[!] Timeout: {addr[0]}:{addr[1]}")
break
except ConnectionResetError:
print(f"[!] Connection reset: {addr[0]}:{addr[1]}")
break
print(f"[-] Connection closed: {addr[0]}:{addr[1]}")
def stop(self):
self.running = False
if self.server_socket:
self.server_socket.close()
if __name__ == '__main__':
server = ThreadedEchoServer()
# 优雅退出
def signal_handler(sig, frame):
print("\n[!] Shutting down...")
server.stop()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
server.start()
多线程设计要点:
| 要点 | 实现 | 原因 |
|---|---|---|
daemon=True |
守护线程 | 主线程退出时自动清理 |
settimeout(1.0) |
accept 超时 | 允许主循环检查 self.running 标志 |
settimeout(30) |
客户端超时 | 防止僵死连接 |
SO_REUSEADDR |
端口复用 | 避免重启时 "Address already in use" |
with client_socket |
上下文管理器 | 自动 close() |
N.2 非阻塞 I/O --- select 实现
python
#!/usr/bin/env python3
"""
基于 select 的异步 Echo 服务器 (单线程处理多连接)
"""
import socket
import select
HOST = '0.0.0.0'
PORT = 8889
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.setblocking(False) # 非阻塞模式
server.bind((HOST, PORT))
server.listen(10)
# 要监控的 socket 列表
inputs = [server]
outputs = []
message_queues = {} # {socket: queue}
print(f"[*] Non-blocking Echo Server on {HOST}:{PORT}")
while inputs:
# select 阻塞等待任意 socket 就绪
readable, writable, exceptional = select.select(inputs, outputs, inputs)
for s in readable:
if s is server:
# 新连接
client, addr = s.accept()
print(f"[+] {addr}")
client.setblocking(False)
inputs.append(client)
message_queues[client] = []
else:
# 已有客户端发来数据
try:
data = s.recv(4096)
if data:
message = data.decode().strip()
print(f"[<] {s.getpeername()} → {message}")
response = f"Echo: {message}\n"
message_queues[s].append(response.encode())
if s not in outputs:
outputs.append(s)
else:
# 连接关闭
print(f"[-] {s.getpeername()} closed")
if s in outputs:
outputs.remove(s)
inputs.remove(s)
s.close()
del message_queues[s]
except ConnectionResetError:
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
if s in message_queues:
del message_queues[s]
for s in writable:
# 发送排队的响应
if s in message_queues and message_queues[s]:
msg = message_queues[s].pop(0)
s.send(msg)
if s in message_queues and not message_queues[s]:
outputs.remove(s)
select vs threads 对比:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Multi-thread (多线程) | 代码直观,易于理解 | 每连接一线程,C10K 问题 | 连接数 < 100 |
| select | 单线程处理多连接 | 最多 1024 个 fd (FD_SETSIZE) | 连接数 100-1000 |
| poll | 无 fd 数量限制 | 仍需遍历所有 fd (O(n)) | 连接数 1000-10000 |
| epoll (Linux) | O(1) 事件通知 | 仅 Linux | C10K+ 高并发 |
| asyncio (Python) | 高层抽象,易用 | 需要 async/await 生态 | 现代 Web 应用 |
N.3 HTTP 客户端完整实现
python
#!/usr/bin/env python3
"""
从零实现简易 HTTP 客户端 (仅 GET)
"""
import socket
from urllib.parse import urlparse
def http_get(url, timeout=5):
"""
使用原始 Socket 实现 HTTP GET 请求
返回: (status_code, headers, body)
"""
parsed = urlparse(url)
host = parsed.hostname
port = parsed.port or (443 if parsed.scheme == 'https' else 80)
path = parsed.path or '/'
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
try:
sock.connect((host, port))
# 构造 HTTP 请求 (原始格式,注意每行以 \r\n 结尾)
request = (
f"GET {path} HTTP/1.1\r\n"
f"Host: {host}\r\n"
f"User-Agent: Python-Socket-HTTP-Client/1.0\r\n"
f"Accept: */*\r\n"
f"Connection: close\r\n"
f"\r\n" # 空行表示头部结束
)
sock.sendall(request.encode())
# 接收响应
response = b""
while True:
chunk = sock.recv(4096)
if not chunk:
break
response += chunk
# 解析响应
header_end = response.find(b'\r\n\r\n')
if header_end == -1:
return (0, {}, b"")
header_text = response[:header_end].decode()
body = response[header_end + 4:]
# 解析状态行
lines = header_text.split('\r\n')
status_line = lines[0]
_, status_code, _ = status_line.split(' ', 2)
# 解析响应头
headers = {}
for line in lines[1:]:
if ':' in line:
key, value = line.split(':', 1)
headers[key.strip().lower()] = value.strip()
return (int(status_code), headers, body)
finally:
sock.close()
# 使用示例
if __name__ == '__main__':
status, headers, body = http_get('http://localhost/')
print(f"Status: {status}")
print(f"Content-Type: {headers.get('content-type')}")
print(f"Content-Length: {headers.get('content-length')}")
print(f"Body ({len(body)} bytes):")
print(body.decode()[:200])
附录 O:网络性能分析完全指南
O.1 延迟来源分析
网络延迟由以下部分组成:
总延迟 = 传播延迟 + 传输延迟 + 处理延迟 + 排队延迟
┌──────────────────────────────────────────────────┐
│ 传播延迟 (Propagation) = 距离 / 光速 │
│ 光纤: ~5μs/km │
│ 深圳→北京 (2000km): ~10ms │
│ 深圳→纽约 (13000km): ~65ms │
├──────────────────────────────────────────────────┤
│ 传输延迟 (Transmission) = 数据量 / 带宽 │
│ 1MB @ 100Mbps: ~80ms │
│ 1MB @ 1Gbps: ~8ms │
│ 1MB @ 10Gbps: ~0.8ms │
├──────────────────────────────────────────────────┤
│ 处理延迟 (Processing) = 设备处理时间 │
│ 路由器: 0.1-10ms/跳 │
│ 防火墙/IDS: 0.5-50ms │
├──────────────────────────────────────────────────┤
│ 排队延迟 (Queuing) = 拥塞时在缓冲区等待 │
│ 正常: 0-1ms │
│ 拥塞: 可达数百ms (Bufferbloat) │
└──────────────────────────────────────────────────┘
O.2 带宽延迟积 (BDP) 与 TCP 窗口
BDP (Bandwidth-Delay Product) = 带宽 × RTT,表示"在途"数据的最大量。
BDP 计算示例:
香港 → 北京: 带宽 1Gbps × RTT 30ms
BDP = 1,000,000,000 bps × 0.03s = 30,000,000 bits = 3.75 MB
这意味着:任何时刻"在空中"的数据约有 3.75 MB。
如果 TCP 窗口小于 BDP,链路就无法被充分利用!
实操验证:
bash
# 实际带宽测试
iperf3 -c tcpip-02 -t 10
# 查看实际 RTT
ping -c 5 tcpip-02
# 计算 BDP
# Bandwidth: 4.15 Gbps × RTT: 0.25ms
# BDP = 4,150,000,000 × 0.00025 = 1,037,500 bits ≈ 126 KB
# 说明:内网通信 BDP 很小,TCP 窗口很容易满足,所以延迟极低
O.3 TCP 窗口调优验证
bash
# 查看实际 TCP 缓冲区
sysctl net.ipv4.tcp_rmem
sysctl net.ipv4.tcp_wmem
# 查看当前连接的实际窗口
ss -ti # -i 显示 TCP 内部信息
# 输出包含: cwnd (拥塞窗口), rtt, rto, mss 等
# 示例输出解读:
# cwnd:10 → 拥塞窗口 10 × MSS = 14.6KB
# rtt:0.25 → 当前 RTT 0.25ms
# rto:200 → 重传超时 200ms
# mss:1460 → 最大段大小 1460 bytes
附录 P:网络故障排查完整案例
P.1 经典故障场景及排查步骤
场景 1: "网站打不开"
bash
# Step 1: Ping 验证连通性
ping -c 3 www.baidu.com
# 如果超时 → 检查网络/防火墙
# Step 2: DNS 解析测试
dig www.baidu.com +short
# 如果无结果 → DNS 故障
# 如果有 IP 但 ping 不通 → 网络路由问题
# Step 3: 端口连通性
nc -zv www.baidu.com 443
# 如果 Connection refused → 服务器未监听
# 如果 timeout → 防火墙阻止
# Step 4: HTTP 层测试
curl -v https://www.baidu.com
# 查看 TLS 握手是否完成
# 查看 HTTP 状态码
场景 2: "SSH 连接慢"
bash
# 排查: DNS 反向解析超时
time ssh -v user@host exit 2>&1 | grep -i delay
# 解决: 在 /etc/ssh/sshd_config 中设置
# UseDNS no
# 排查: GSSAPI 认证超时
# 解决: 在 /etc/ssh/ssh_config 中设置
# GSSAPIAuthentication no
场景 3: "TCP 连接被拒绝 (Connection Refused)"
原因分析:
1. 目标服务未启动
→ systemctl status nginx
2. 防火墙阻止了端口
→ sudo iptables -L -n | grep <port>
3. 服务绑定了错误的 IP (如 127.0.0.1 而非 0.0.0.0)
→ ss -tlnp | grep <port>
4. SELinux/AppArmor 限制
→ sudo aa-status | grep <service>
场景 4: "大量 TIME_WAIT 连接"
bash
# 检查
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn | head
# 输出示例: 2457 TIME-WAIT
# 原因: 短连接过多 (HTTP/1.0 或未启用 Keep-Alive)
# 解决1: 应用层使用连接池
# 解决2: 启用 tcp_tw_reuse
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
# 解决3: nginx 配置 upstream keepalive
附录 Q:TCP 选项字段完全参考
TCP 头部包含可变长度的选项字段,每个选项都有特定的 TLV (Type-Length-Value) 结构:
| 选项类型 | 名称 | 长度 | 用途 | RFC |
|---|---|---|---|---|
| 0 | EOL (End of Option List) | 1 | 选项列表结束标记 | 793 |
| 1 | NOP (No-Operation) | 1 | 填充对齐 | 793 |
| 2 | MSS (Maximum Segment Size) | 4 | 声明最大接收段大小 | 793 |
| 3 | Window Scale (WSopt) | 3 | 窗口缩放因子 (0-14) | 7323 |
| 4 | SACK Permitted | 2 | 表示支持选择性确认 | 2018 |
| 5 | SACK | 可变 | 选择性确认的具体块 | 2018 |
| 8 | Timestamps (TSopt) | 10 | RTTM + PAWS | 7323 |
| 34 | TCP Fast Open Cookie | 可变 | TFO 连接加速 | 7413 |
实操 --- 从 tcpdump 中识别选项:
options [mss 1460,sackOK,TS val 2711816421 ecr 0,nop,wscale 7]
解析:
├── mss 1460 → 选项 #2: 最大段大小 1460 字节 (MTU 1500 - 20 IP头 - 20 TCP头)
├── sackOK → 选项 #4: 支持 SACK
├── TS val ... → 选项 #8: TCP 时间戳 (val=发送方时间戳, ecr=回显时间戳)
├── nop → 选项 #1: 1字节填充 (用于对齐)
└── wscale 7 → 选项 #3: 窗口缩放因子 = 2^7 = 128
实际窗口 = 64240 × 1 = 64240? 不对...
实际窗口 = 64240 / 128? 也不对...
说明: wscale 应用于 win 字段的缩放:
声明窗口 win=64240 wscale=7
实际接收窗口 = 64240 × 2^7 = 8,222,720 字节 ≈ 7.8 MB
附录 R:MTU 与分片详解
R.1 MTU (Maximum Transmission Unit)
MTU 是链路层帧中数据部分的最大长度。以太网标准 MTU 为 1500 字节。
以太网帧结构:
┌──────────────────────────────────────────────────────┐
│ 目标MAC(6)│ 源MAC(6) │ EtherType(2) │ 数据(46-1500) │ FCS(4) │
└──────────────────────────────────────────────────────┘
↑
MTU = 1500
IP 分片: 当 IP 数据包大于 MTU 时,路由器将其分片
原始包: IP头(20) + TCP头(20) + 数据(2000)
= 2040 > MTU(1500) → 需要分片
分片1: IP头(20) + 数据(1480) [MF=1, Offset=0]
分片2: IP头(20) + 数据(520) [MF=0, Offset=185]
R.2 路径 MTU 发现 (Path MTU Discovery)
bash
# 测试到目标的最大 MTU (不分配)
ping -M do -s 1472 www.baidu.com # 1472 + 28 = 1500
# -M do: Don't Fragment (DF) 标志
# -s 1472: ICMP 数据 1472 字节
# 如果返回 "Frag needed" → MTU 太小
ping -M do -s 1400 www.baidu.com # 逐步减小
# 查看本机接口 MTU
ip link show eth0 | grep mtu
# 输出: mtu 1500
常见 MTU 问题 --- 踩坑记录:
场景: 从容器/VM 访问外部 HTTPS 网站卡顿或失败
原因: 隧道封装导致 MTU 减小
- VXLAN 隧道: MTU - 50
- GRE 隧道: MTU - 24
- PPPoE: MTU - 8
解决:
1. 调整 MTU: ip link set eth0 mtu 1450
2. 启用 MSS Clamping: iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
🔧 实验工具版本信息
| 工具 | 版本 | 安装方式 |
|---|---|---|
| tcpdump | 4.99.4 | apt (系统仓库) |
| nmap | 7.94SVN | apt (系统仓库) |
| hping3 | 3.a2.ds2-10build2 | apt (系统仓库) |
| iperf3 | 3.16 | apt (系统仓库) |
| curl | 8.5.0 | apt (系统仓库) |
| dig | 9.18.39 (bind9-dnsutils) | apt (系统仓库) |
| OpenSSL | 3.0.13 | apt (系统仓库) |
| nginx | 1.24.0 | apt (系统仓库) |
| ss | (iproute2) | 系统预装 |
| Python | 3.12.3 | 系统预装 |
📝 文档说明: 本博客所有实验数据和截图均来自华为云 ecs-ee63 集群 (2026-06-02) 的真实运行环境。实验环境配置为 c6.large_2 (2vCPU/4GiB) × 4 台,操作系统为 Ubuntu 24.04 LTS。所有工具均通过 APT 包管理器安装的官方版本。
⚠️ 免责声明: 第7章中的网络攻击实验仅限于教育目的和授权实验环境。对未授权系统进行网络攻击是违法行为。
文档完成于 2026-06-02 | 实验环境: 华为云香港 ECS