TCP/IP 网络协议学习笔记(含上机实操)

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) 替代:

  • ifconfigip addr show
  • routeip route show
  • netstatss (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 边界)

💡 * * * 代表什么?

有三种可能:

  1. 路由器配置了不响应 ICMP TTL Exceeded(最常见)
  2. 防火墙拦截了探测包
  3. 该跳确实超时了

连续的 * 通常是情况 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 显示 [::]:800.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.comwww.a.shifen.comwww.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.comwww.a.shifen.comwww.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 满)
                                          │                  │ (拒绝新连接)

防御措施:

  1. SYN Cookie --- 不分配完整资源,通过加密算法验证客户端
  2. 增大 backlog --- net.core.somaxconn 参数
  3. SYN Proxy --- 反向代理拦截并验证 SYN
  4. 限速/清洗 --- 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 编程最佳实践:

  1. 使用 with 语句自动管理 socket 生命周期
  2. 设置 SO_REUSEADDR 避免 "Address already in use" 错误
  3. TCP 使用 sendall() 而非 send(),确保完整发送
  4. 使用 setsockopt() 设置超时 (SO_RCVTIMEO)
  5. 正确处理 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 -sSrequires 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 存在的两个原因:

  1. 确保最后的 ACK 能到达对方:如果主动关闭方发送的最后一个 ACK 丢失,被动方会重发 FIN。TIME_WAIT 让主动方能重新发送 ACK。

  2. 让旧连接的数据包在网络上消失:防止旧连接的延迟包被误认为新连接的数据 (如果新连接使用了相同的四元组)。

    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

相关推荐
「維他檸檬茶」1 小时前
记录python学习(未完待续)
学习·语言
玹之又玹1 小时前
AI 入门知识点学习扫盲
人工智能·学习·ai
_橙时_1 小时前
【学习记录01】
学习·em
小陈phd1 小时前
多模态大模型学习笔记(四十五)——视觉推理(Visual Reasoning):从观察到逻辑的复杂认知链
人工智能·笔记·学习
旺仔Sec1 小时前
【AI数字营销新纪元】解锁流量新密码:CSDN“GEO工具”全网首测,让你的文章被大模型“记住”!
人工智能·学习·用户体验
呼Lu噜1 小时前
Vue3学习-Vue3概述&项目创建
学习
Upsy-Daisy1 小时前
IOTA 学习笔记(八):本地启动 IOTA Localnet
笔记·学习
古方路杰出青年1 小时前
学习笔记:语音信号读取与显示——理论分析与技术详解(含代码块)
笔记·学习·语音识别
searchforAI2 小时前
Ai好记 vs Get笔记:AI音视频笔记工具深度测评对比
人工智能·笔记·学习·ai·音视频·语音识别