TCP/UDP 网络协议教程 / 02-IP 协议基础
02 - IP 协议基础
2.1 IP 协议概述
IP(Internet Protocol)是网络层的核心协议,负责将数据包从源主机传输到目的主机。
关键特性:
• 无连接:不建立连接,直接发送
• 不可靠:不保证到达、不保证顺序
• 尽力而为:Best-effort delivery
• 路由:通过路由器逐跳转发
2.2 IPv4 头部结构
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 字段 |
长度 |
说明 |
| Version |
4 bit |
IP 版本号(4 或 6) |
| IHL |
4 bit |
头部长度(单位:4字节) |
| Total Length |
16 bit |
数据包总长度(最大 65535) |
| TTL |
8 bit |
生存时间(每经过路由器减1) |
| Protocol |
8 bit |
上层协议(TCP=6, UDP=17) |
| Source Address |
32 bit |
源 IP 地址 |
| Destination Address |
32 bit |
目的 IP 地址 |
# 解析 IP 头部示例
import struct
def parse_ip_header(data):
"""解析 IP 头部"""
if len(data) < 20:
return None
# 解析前 20 字节
fields = struct.unpack('!BBHHHBBHII', data[:20])
version = fields[0] >> 4
ihl = fields[0] & 0xF
ttl = fields[5]
protocol = fields[6]
src_ip = struct.pack('!I', fields[8])
dst_ip = struct.pack('!I', fields[9])
import socket
return {
'version': version,
'header_length': ihl * 4,
'ttl': ttl,
'protocol': protocol, # 6=TCP, 17=UDP
'src_ip': socket.inet_ntoa(src_ip),
'dst_ip': socket.inet_ntoa(dst_ip)
}
2.3 IPv4 地址
地址分类
| 类别 |
范围 |
默认掩码 |
用途 |
| A 类 |
1.0.0.0 - 126.255.255.255 |
/8 |
大型网络 |
| B 类 |
128.0.0.0 - 191.255.255.255 |
/16 |
中型网络 |
| C 类 |
192.0.0.0 - 223.255.255.255 |
/24 |
小型网络 |
| D 类 |
224.0.0.0 - 239.255.255.255 |
- |
多播 |
| E 类 |
240.0.0.0 - 255.255.255.255 |
- |
保留 |
私有地址
| 类别 |
地址范围 |
CIDR |
| A 类 |
10.0.0.0 - 10.255.255.255 |
10.0.0.0/8 |
| B 类 |
172.16.0.0 - 172.31.255.255 |
172.16.0.0/12 |
| C 类 |
192.168.0.0 - 192.168.255.255 |
192.168.0.0/16 |
特殊地址
| 地址 |
用途 |
| 0.0.0.0 |
所有地址 |
| 127.0.0.1 |
本地回环 |
| 255.255.255.255 |
有限广播 |
| 169.254.x.x |
链路本地地址 |
2.4 子网划分
CIDR 表示法
IP地址/前缀长度
例如:192.168.1.0/24
前缀长度 24 表示前 24 位是网络部分
子网掩码:255.255.255.0
可用主机数:2^(32-24) - 2 = 254
import ipaddress
def subnet_info(network_str):
"""获取子网信息"""
network = ipaddress.ip_network(network_str, strict=False)
return {
'network_address': str(network.network_address),
'broadcast_address': str(network.broadcast_address),
'netmask': str(network.netmask),
'prefix_len': network.prefixlen,
'total_hosts': network.num_addresses,
'usable_hosts': network.num_addresses - 2 if network.prefixlen < 31 else network.num_addresses,
'first_host': str(network.network_address + 1),
'last_host': str(network.broadcast_address - 1)
}
# 示例
print(subnet_info('192.168.1.0/24'))
# {'network_address': '192.168.1.0', 'broadcast_address': '192.168.1.255',
# 'netmask': '255.255.255.0', 'prefix_len': 24, 'total_hosts': 256,
# 'usable_hosts': 254, 'first_host': '192.168.1.1', 'last_host': '192.168.1.254'}
子网划分示例
原始网络:192.168.1.0/24(256 个地址)
划分为 4 个子网:
┌─────────────────┬─────────────────┬─────────────────┬─────────────────┐
│ 192.168.1.0/26 │ 192.168.1.64/26 │192.168.1.128/26 │192.168.1.192/26 │
│ 64 个地址 │ 64 个地址 │ 64 个地址 │ 64 个地址 │
│ .1 - .62 │ .65 - .126 │ .129 - .190 │ .193 - .254 │
└─────────────────┴─────────────────┴─────────────────┴─────────────────┘
2.5 IPv6 简介
IPv6 地址格式
128 位地址,冒号十六进制表示:
完整:2001:0db8:0000:0000:0000:0000:0000:0001
简化:2001:db8::1
规则:
1. 前导零可省略
2. 连续全零段可用 :: 替代(只能使用一次)
IPv4 vs IPv6
| 特性 |
IPv4 |
IPv6 |
| 地址长度 |
32 位 |
128 位 |
| 地址数量 |
~43 亿 |
~3.4×10^38 |
| 表示方法 |
点分十进制 |
冒号十六进制 |
| 头部大小 |
20-60 字节 |
40 字节(固定) |
| 分片 |
路由器和发送端 |
仅发送端 |
| 校验和 |
有 |
无(交给上层) |
| NAT |
广泛使用 |
不需要 |
2.6 路由基础
路由表
# 查看路由表
$ ip route show
default via 192.168.1.1 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100
10.0.0.0/8 via 192.168.1.254 dev eth0
路由决策过程:
1. 提取目的 IP 地址
2. 与路由表逐条匹配(最长前缀匹配)
3. 找到匹配项 → 从指定接口转发
4. 无匹配项 → 使用默认路由(default gateway)
5. 无默认路由 → 丢弃并发送 ICMP 错误
路由类型
| 类型 |
说明 |
示例 |
| 直连路由 |
直接连接的网络 |
192.168.1.0/24 dev eth0 |
| 静态路由 |
手动配置 |
10.0.0.0/8 via 192.168.1.254 |
| 动态路由 |
协议自动学习 |
BGP、OSPF、RIP |
| 默认路由 |
兜底路由 |
default via 192.168.1.1 |
2.7 IP 分片
分片原因
MTU (Maximum Transmission Unit):链路层最大传输单元
以太网 MTU = 1500 字节
IP 数据包最大 = 65535 字节
当 IP 包 > MTU 时,需要分片
分片字段
| 字段 |
说明 |
| Identification |
标识同一个数据包的所有分片 |
| Flags.DF |
Don’t Fragment,不允许分片 |
| Flags.MF |
More Fragments,后面还有分片 |
| Fragment Offset |
分片在原始数据包中的偏移(8字节为单位) |
def analyze_fragments():
"""分片示例分析"""
original_size = 4000 # 原始数据包大小
mtu = 1500 # 链路 MTU
ip_header = 20 # IP 头部大小
max_data = mtu - ip_header # 每片最大数据 = 1480
fragments = []
offset = 0
remaining = original_size - ip_header # 3980 字节数据
while remaining > 0:
frag_data = min(remaining, max_data)
mf = 1 if remaining > max_data else 0
fragments.append({
'offset': offset,
'data_size': frag_data,
'MF': mf,
'total_size': frag_data + ip_header
})
offset += frag_data
remaining -= frag_data
return fragments
for frag in analyze_fragments():
print(f"分片: offset={frag['offset']:5d}, size={frag['data_size']:5d}, MF={frag['MF']}")
# 分片: offset= 0, size= 1480, MF=1
# 分片: offset= 1480, size= 1480, MF=1
# 分片: offset= 2960, size= 1020, MF=0
2.8 ICMP 协议
ICMP 是 IP 的辅助协议,用于错误报告和网络诊断。
| 类型 |
代码 |
说明 |
工具 |
| 0 |
0 |
Echo Reply |
ping |
| 3 |
0 |
网络不可达 |
- |
| 3 |
1 |
主机不可达 |
- |
| 3 |
3 |
端口不可达 |
- |
| 8 |
0 |
Echo Request |
ping |
| 11 |
0 |
TTL 超时 |
traceroute |
# ping 使用 ICMP Echo
$ ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=118 time=12.3 ms
# traceroute 使用 TTL 超时
$ traceroute 8.8.8.8
1 192.168.1.1 (192.168.1.1) 1.234 ms
2 10.0.0.1 (10.0.0.1) 5.678 ms
...
2.9 ARP 协议
ARP 将 IP 地址解析为 MAC 地址。
ARP 工作流程:
1. 主机 A 要发送数据给 192.168.1.2
2. A 检查 ARP 缓存表
3. 若无缓存,广播 ARP 请求:"谁是 192.168.1.2?"
4. 192.168.1.2 单播回复:"我是,MAC 是 xx:xx:xx:xx:xx:xx"
5. A 更新 ARP 缓存,发送数据
# 查看 ARP 缓存
$ arp -a
? (192.168.1.1) at 00:11:22:33:44:55 [ether] on eth0
? (192.168.1.2) at 66:77:88:99:aa:bb [ether] on eth0
2.10 注意事项
⚠️ MTU 问题:路径 MTU 发现(PMTUD)失败会导致连接问题,某些防火墙会丢弃 ICMP “需要分片"消息
⚠️ IP 地址耗尽:IPv4 地址已耗尽,NAT 和 IPv6 是解决方案
⚠️ 安全考虑:IP 欺骗(IP Spoofing)是常见的攻击手段,需要配合上层协议验证
2.11 扩展阅读
下一章:03 - TCP 头部详解 - 深入理解 TCP 协议的每个字段