如果互联网是一座城市,协议就是交通规则。最近我把《趣谈网络协议》系列从P5一路看到P9,内容正好覆盖网络层和传输层最核心的几块:IP、ARP、ICMP、TCP/UDP,以及数据包如何一步步跨出本地网络。这篇笔记不是课程的复读机,而是我把这几节内容吸收之后,结合自己抓包实验重写的一份记录。适合正在啃网络协议、总是分不清“IP和MAC到底谁在干活”的朋友,也适合想快速把零散协议知识串成体系的人。
1. IP协议:地址规划决定了你能走多远
1.1 IPv4地址结构
P5课程顺着OSI模型往下摸到网络层,IP是这一层最核心的协议。IPv4地址是32位二进制,为了人类好记忆才写成点分十进制。我刚学的时候总以为192.168.1.1这种数字就是“门牌号”,但真的理解网络号/主机号之后,才看懂协议设计者的意图。
IPv4地址由两部分拼成:网络号标识你所在的子网,主机号标识子网里的这台设备。早期分类编址把地址分成A/B/C类,A类第一字节1-126,B类128-191,C类192-223。这种分类方式很直观,但浪费严重:一个B类地址能带六万五千多台主机,实际没人会这么用。所以现在实际环境里用的是CIDR无类域间路由,写成192.168.1.0/24这样,斜杠后面的24表示前24位是网络号,后面8位是主机号。这个斜杠记法第一次看容易懵,但记一句话就够了:斜杠后面的数字越大,能分配给主机的地址就越少。
比如192.168.1.0/24这个网段,共有256个地址,但可用主机只有254个。因为主机位全0表示网络地址本身,全1表示广播地址,这两个都不能分配给网卡。同理192.168.1.0/25可用地址只有126个,/26只有62个。很多人背不住这个表,其实规律很简单:地址总数每减半一次,可用主机数也基本减半,脑海中画一条2的幂曲线就清楚了。
还有一类地址必须专门记住:私有地址。10.0.0.0/8、172.16.0.0/12、192.168.0.0/16,这三个范围只能在局域网内部使用,公网路由器默认不会转发。我们平时做实验用的192.168.x.x全是私有地址,回环地址127.0.0.0/8则用来表示本机。这些看起来是死知识,但后面抓包和排障时非常关键,比如你ping 127.0.0.1通,完全不能说明网卡和网线没问题,因为流量根本没出本机。
1.2 子网划分与CIDR
子网划分本质上是从主机位里借位当网络位。比如192.168.1.0/24想要切成4个子网给四个部门,需要在主机位里多借2位,掩码从/24变成/26,每个子网就有64个地址,可用主机62个。这里很多教程喜欢让背公式,我更推荐直接想:掩码每加1,可用地址数量减半。所以/25是126,/26是62,/27是30。工作中做点对点链路甚至可以直接用/30或/31,而不是给一条只有两个IP的链路分配一整个C类段。
实操上,Linux里有个特别好用的小工具ipcalc。安装后输入ipcalc 192.168.1.0/26,它会直接列出网络地址、广播地址、可用主机范围和子网掩码,相当于把计算器给你端到面前。我自己做网络规划时,会先用这类工具把各个子网的边界列出来,再对着物理拓扑分配,避免出现两个网段互相重叠还看不出来的尴尬。
不过工具算出来的是理论值,实际规划还要考虑一些业务边界。比如核心交换机之间的互联地址、管理地址、终端地址,最好规划时就用不同子网区分开,别为了省地址把所有设备塞进一个大网段。否则一旦出故障,广播域太大,抓包排查会非常痛苦。P5这节课给我的启发就是:IP规划不只是技术活,更是留容错空间的设计活。
2. ARP:IP和MAC之间的“门牌翻译官”
2.1 ARP工作流程
P6讲ARP时,我最大的感受是:网络层的人说话都用IP,但真正的链路层只认MAC地址。IP地址是跨城市的详细地址,MAC地址是小区内的门牌号。要把数据封装成帧发出去,必须知道下一跳设备的MAC地址,这就是ARP存在的意义。
ARP流程很短但非常关键。场景:电脑A想访问同网段的电脑B,比如192.168.1.10。A先查自己的ARP缓存,如果没有对应表项,就发一个广播帧:“谁是192.168.1.10?请把你的MAC地址告诉我。”这个广播帧的源MAC是A自己的,目标MAC是FF-FF-FF-FF-FF-FF。B收到帧后,发现目的IP是自己,就单播回复一条ARP应答:“我是192.168.1.10,我的MAC是xx:xx:xx。”A收到后把映射关系写进缓存,默认几分钟后过期。
这里容易混淆的点是:询问用的是广播,应答用的是单播。广播喊一句“全楼都听见”,但只有目标主机会回话。而且ARP报文的IP/MAC映射关系是临时缓存的,不是一成不变。你可以用arp -a在Windows下查看缓存,会发现网关、同网段主机都在列表里。如果缓存过期或设备更换了网卡,下次通信又会触发一次ARP学习。
跨网访问时还有个细节:如果目标IP不在同一子网,主机不会直接去请求目标IP的MAC,而是把数据帧的目的MAC填成默认网关的MAC,IP头部里的目的IP仍然是目标主机的IP。我学习时一度想不通,后来用快递类比才理解:我要把包裹寄到你所在的小区,快递单上写的是你家的详细门牌,但配送员在小区门口会先交给代收点,再由代收点完成最后一步。MAC地址只在本地链路有效,每跨一个路由器就会替换一次。
2.2 ARP抓包实验与常见坑
看ARP报文最直接的方式是打开Wireshark抓包,然后在命令行清掉缓存。Windows下用arp -d,Linux下用ip neigh flush all,接着随便ping一下同网段地址。过滤栏输入arp,就能看到两条报文:Request和Reply。注意观察Reply的目的MAC是发起方单播,而不是广播,这就验证了前面说的应答模式。
实际工作里常见的坑是IP地址冲突。我遇到过某台设备手动指定了一个静态IP,和另一台设备通过DHCP拿到的地址相同,于是整个局域网里这个IP对应的MAC一直在变。ARP缓存频繁更新,网络时通时断。抓包时会看到两条ARP Reply轮番出现,内容却是不同MAC。解决方式是全网统一用DHCP静态绑定,不要在同一网段手工分配可能冲突的地址。
另外一个绕不开的话题是ARP欺骗。这个原理并不复杂,就是ARP协议信任所有回应,不校验是否由真正的目标主机发出。有人伪造ARP应答说“网关的IP是我的MAC”,同网段设备就会把去往网关的流量发给这个伪造者。我提这个不是为了教人,而是想说学协议一定要懂攻击面,才知道防御点在哪里。企业网络里可以配置交换机动态ARP检测,或者在关键网关上做静态ARP绑定,能有效降低这类风险。抓包时如果发现大量高频的ARP Reply且MAC来源异常,就要警惕了。
3. ICMP:让网络自己说“哪里病了”
3.1 Ping与Tracert背后发生了什么
P7讲ICMP,这是平时排障最常用的协议。它本身不承载业务数据,而是传递网络控制信息,被直接封装在IP数据报里。我以前觉得ICMP就是“ping用的协议”,其实ping只是ICMP的一种应用场景。
ping的原理是发一个ICMP Echo Request(类型8),目标主机收到后回一个Echo Reply(类型0)。我在Linux里测试时常用ping -c 5 223.5.5.5限制发包数量,看到64 bytes from 223.5.5.5: icmp_seq=1 ttl=56 time=...这些信息。TTL字段很关键,它表示IP包每经过一个路由器就减1,如果目标地址是公网,中间经过的跳数越多,剩余TTL就越小。time这一项是往返时延,如果突然变得很大,说明链路或者中间设备有拥塞。
tracert/traceroute更巧妙。它利用了IP协议的TTL机制:每发一个探测包就把TTL设为1、2、3……第一个路由器收到TTL为0的包后会丢弃,并回一个ICMP Time Exceeded(类型11),这样本机就知道了第一跳是谁。逐跳增加TTL,最终到达目标主机后回Echo Reply,整条路径就暴露出来了。Windows下命令是tracert -d,Linux下是traceroute -I。如果某跳显示* * *,不一定代表链路断了,很可能只是那台路由器不回应ICMP超时报文,要结合后续跳数和最终到达情况综合判断。
3.2 ICMP报文类型与防火墙
ICMP不只是echo,最常见的还有目标不可达(类型3)。类型3又分很多代码,比如0表示网络不可达,1表示主机不可达,4表示需要分片但IP头设置了DF标志。第4种情况特别值得关注,它支撑着Path MTU Discovery机制。
机制是这样的:网络中间某条链路MTU小于1500,比如只有1400,路由器想把大数据包分片转发,但报文头部设置了DF标志要求不能分片,于是路由器回一个ICMP Fragmentation Needed报文,源主机收到后自动缩小发送包大小。如果这个ICMP反馈被防火墙拦截,就会出现诡异现象:小包通、大包不通。我实际排查过一个案例,某个站点网页加载到一半就卡住,抓包发现TCP三次握手成功,但到HTTP数据传输阶段一直重传。后来定位到运营商链路MTU限制,而服务器发出的1500字节包还带DF位,中间设备回ICMP被安全策略拦截。最终调整了接口MTU才恢复正常。
排查这类问题可以用Linux下的Ping探测:ping -M do -s 1450 目标IP,不断降低-s值,看哪个大小能通。如果全程不通但小包能通,就需要怀疑MTU和ICMP被拦截的组合问题。还有一个习惯问题:很多安全团队直接在设备上禁掉所有ICMP,导致内网里ping不通,但业务访问正常。这种策略会让排障变得很痛苦。我会建议至少保留“目标不可达”和“超时”这两种错误反馈,否则路径问题全是黑盒。
4. TCP与UDP:一个要确认、一个要速度
4.1 端口与通信五元组
P8开始讲传输层,信息量很大。传输层最重要的任务是给进程编号,这个编号就是端口。IP负责找到主机,端口负责找到主机上的哪个应用。一次网络通信可以用五元组唯一标识:源IP、目的IP、源端口、目的端口、协议号。Wireshark里看连接列表,看到的其实就是五元组组合。常用端口如22(SSH)、80(HTTP)、443(HTTPS)、3389(RDP)。服务端口一般是固定的,但客户端源端口是随机分配的,所以抓包时你经常看到源端口是五万多这种大数字。
TCP和UDP的区别,我一直用快递来类比:TCP是快递员上门要求签收,UDP是平邮包裹扔进信箱就不管了。UDP头部只有源端口、目的端口、长度、校验和,几乎没有额外开销,没有确认机制,所以延迟低,适合DNS查询、视频直播、游戏同步这类场景。TCP则要复杂得多,头部里有序列号、确认号、标志位、窗口大小,它要保证数据按序、不丢、不重,因此必须付出握手、确认、重传、拥塞控制的代价。下面这个表格很直观:
| 特性 | TCP | UDP |
|---|---|---|
| 连接状态 | 面向连接 | 无连接 |
| 可靠性 | 可靠,确认重传 | 尽力而为 |
| 头部开销 | 至少20字节 | 8字节 |
| 数据边界 | 字节流 | 报文边界 |
| 典型场景 | HTTP/FTP/SSH | DNS/音视频/SNMP |
不过要小心一个误区:UDP不等于一定比TCP快。UDP只是省掉了维护可靠性的成本,如果应用想用UDP同时要求可靠,就得自己在应用层实现类似机制,很多游戏引擎就是这么做的。所以选型的关键不是“哪个快”,而是“这个场景需不需要可靠传输”。
4.2 TCP三次握手与四次挥手
三次握手是P8的核心。为什么需要握手?因为连接两端需要同步彼此的初始序列号,才能在后续传输中做确认。流程是:客户端发送SYN包,携带自己的初始序列号x;服务器回复SYN-ACK,携带自己的初始序列号y,同时确认号ack=x+1;客户端再回一个ACK,确认服务器的序列号。到此双方进入ESTABLISHED状态,连接建立。
有人问为什么不能两次握手,最经典的解释是:两次握手无法应对“滞留旧SYN”的场景。如果客户端上一个连接请求因为网络问题延迟了很久才到服务器,服务器只凭一个SYN就直接建立连接分配资源,但客户端实际上已经不想建立这个连接了,服务端的资源就白白浪费。三次握手让客户端在收到SYN-ACK后,能识别出这个回应对应的是自己发出的最新SYN,而不是历史残留,从而避免这种错乱。
四次挥手对应断开连接。主动关闭方发FIN,被动方回ACK;等被动方的数据都发送完,再发FIN,主动方回ACK。这里最容易被忽略的是TIME_WAIT状态。主动断开方收到对方FIN并回完ACK后,不会立刻关闭,而是进入TIME_WAIT,等待两个最大报文生存时间(2MSL)后才彻底结束。原因是怕最后的ACK丢失,对端重发FIN时自己还能应答;另外也让旧连接的所有报文在网络中自然消失,不会干扰新连接。抓包时会看到大量TIME_WAIT,尤其是服务器处理大量短连接时。Linux下可以开net.ipv4.tcp_tw_reuse和tcp_timestamps来缓解,但更要紧的是应用层主动做连接复用,比如HTTP/1.1的keep-alive和HTTP/2的多路复用。
实操上,打开Wireshark抓一个普通网页访问,过滤tcp.port == 443,找到第一个SYN包,然后右键Follow TCP Stream,能非常直观地看到三次握手。命令行里可以用netstat -an看连接状态,如果某一瞬间有几万个TIME_WAIT,基本可以判断应用没有复用连接,需要对服务端配置或者客户端连接池做优化。
5. 路由与数据包旅行:P9终于把前面串起来了
5.1 三层设备怎么选择路径
P9讲路由,把IP、ARP、ICMP全部串了起来。路由器工作在网络层,它维护一张路由表,表里有目标网络、掩码、下一跳、出接口和路由优先级。收到一个IP数据包时,路由器用目的IP逐条匹配路由表,选中最长前缀匹配的表项。比如路由表里同时有0.0.0.0/0和192.168.0.0/16,目标IP是192.168.1.10时,一定优先走/16那条,因为前缀更长、范围更精确。只有全部匹配不上,才会走默认路由0.0.0.0/0。
实际排障里,很多通信问题的根源是默认网关配错。如果主机上默认网关指向了一个不可达地址,那么所有跨网通信都会失败。最简单的检查方法:Windows下route print,Linux下ip route,确认默认路由的网关是否正确。多网卡多网关的机器还要看路由度量值,不然可能出现出口流量绕路的奇怪现象。比如一台服务器有内网和外网两张网卡,如果内网路由的度量值比默认网关还高,访问内网时可能先走外网卡再回来,延迟高还容易触发防火墙策略问题。
5.2 抓包看一次HTTP请求的完整旅程
把P5到P9串起来,最好的方式是自己做一次抓包实验。假设浏览器访问一个网站,完整流程是:
- DNS查询:先向DNS服务器发UDP报文,查询域名对应的IP。
- TCP握手:拿到IP后,浏览器和服务器建立TCP连接。
- HTTP请求:在TCP连接上发送GET请求数据。
- 数据包跨网:如果服务器不在本机局域网内,本机把IP包发给默认网关,网关根据路由表逐跳转发。每经过一个路由器,MAC地址都会改变,但源IP和目的IP保持不变。
- 返回页面,按TCP协议断开连接。
用Wireshark抓包时,我把过滤条件设为host 目标IP,或者直接用tcp.port == 443。第一个看到的一定是DNS请求,过滤器用dns能看到查询和应答;紧接着是TCP三次握手,过滤器用tcp.flags.syn==1能快速锁定SYN包;如果是HTTPS,接下来还有TLS握手;最后才是HTTP数据。能在一堆报文里按顺序挑出这些,网络协议的学习就算真正入门了。我自己还会保存几个抓包文件,比如纯ARP交互、TCP重传、Traceroute的ICMP超时,方便以后复习和对比。
学习这几节课最大的体会是:网络协议不能只看图,一定要动手抓包。哪怕只是ping一下,也能看到ICMP交互;多按几次F5,就能看到TCP连接建立和释放。我在Wireshark里存了常用的几个显示过滤器:arp、icmp、tcp.flags.syn==1、dns,每次遇到新问题就往这些方向缩小范围。等这些基础协议看熟了,再去看HTTP、TLS、QUIC这些更上层的协议,会轻松很多。