网络问题这东西,最气人的地方在于它经常"看起来没问题"。网页能打开、视频能播,但一到传大文件就断断续续;或者白天一切正常,晚高峰就集体卡成幻灯片。很多人遇到这类情况的第一反应是重启设备、换个浏览器、问运营商,一圈折腾下来问题还在。我在机房里待久了发现一个规律:凡是能定位到具体层的问题,解决起来都很快;凡是定位不到层的,基本都在瞎猜。TCP/IP五层模型就是那张帮我们把"瞎猜"变成"定位"的地图——物理层、数据链路层、网络层、传输层、应用层,从下往上,每一层都有自己的活儿和自己的故障特征。这篇文章只聊前四层,也就是物理层到传输层,因为绝大多数"网不通""网很慢""时断时续"的问题,都埋在这四层里,应用层反而是最后才有资格被怀疑的对象。内容会从每层的核心机制讲到实际排障动作,带上参数计算、抓包命令和踩过的坑,不管你是刚学网络的在校生,还是天天跟设备打交道的一线运维,都能从里面捞到能直接用的东西。
1. 先把分层这件事想明白,再谈每层的知识点
五层模型背起来只需要五分钟,但真正理解它为什么这么切,需要一点工程直觉。分层不是学术上的洁癖,而是为了让责任可划分、故障可隔离、演进可独立。你把这一条想通了,后面每一层的知识点才不是零散的名词,而是各自有位置的零件。
1.1 分层本质上是在切一条"责任链"
想象一次跨城的物流运输。货物要装进箱子(应用层协议的数据格式),箱子要贴上单号交给快递网点(传输层负责端到端的可靠送达),网点之间要靠干线卡车跑高速(网络层负责选路和跨网段转发),卡车跑的每一段公路有路基和路面(物理层负责把承载物变成电信号或者光信号),而在相邻两个站点之间的短途倒运,靠的是站点内部的调度和叉车(数据链路层负责同一段链路内的寻址和帧传输)。任何一环出问题,货都到不了,但表现出的症状完全不同:路面塌了是全线瘫痪,调度失误是某一段反复打转,单号丢了是不知道货在哪,箱子破了是内容物损坏。网络排障的思路就是这套逻辑——先判断症状属于哪一种,再顺着责任链往回找。
这个类比里有个关键点值得强调:每一层只关心自己那一段的"交付质量",不关心上下层的细节。网络层不需要知道底下跑的是铜线还是光纤,传输层也不需要知道中间经过了几个路由器。这种"不关心"是刻意的设计,它让无线网络、光纤网络、虚拟网络可以在同一套上层协议下共存。理解了这一点,你就不会再问"为什么换了无线之后IP地址还是一样的"这种问题了。
1.2 从下往上,每层到底背了什么锅
把责任分清楚,排障才不会乱。我在下面用一张表把四层的核心职责、寻址方式、典型故障现象列出来,这张表建议直接记在脑子里,比背协议字段有用得多:
| 层级 | 核心职责 | 本层寻址 | 数据单元 | 典型故障现象 |
|---|---|---|---|---|
| 物理层 | 把比特变成可在介质上传输的信号 | 无(靠端口和介质标识) | 比特 | 链路down、CRC错误暴涨、频繁掉速、收光功率异常 |
| 数据链路层 | 在同一段链路内把帧送到相邻设备 | MAC地址 | 帧 | 同网段通、跨网段不通;ARP表异常;MAC地址漂移;环路广播风暴 |
| 网络层 | 跨网段选路并端到端投递数据包 | IP地址 | 数据包 | 能ping通网关但ping不通外网;路由环路;TTL超时;分片失败 |
| 传输层 | 进程到进程的通信,提供可靠或尽力而为的交付 | 端口号 | 段(TCP)/数据报(UDP) | 端口不通但主机可ping;连接建立失败;传输慢、频繁重传;连接堆积 |
这张表最有价值的地方在于"典型故障现象"这一列。现实里判断问题在哪一层,靠的不是逐个字段排查,而是先用现象做初筛。比如"能ping通但网页打不开",主机可达性没问题,说明下面三层大体是通的,嫌疑立刻缩小到传输层和应用层;再比如"局域网内互传很快,一跨网段就慢",那基本锁死在网络层和链路层的边界上。
1.3 分层思维最容易踩的三个认知坑
第一个坑是把"分层"当成"隔离"。层与层之间是解耦的,但不是互不影响的。物理层出现大量误码,会让传输层疯狂重传,最终表现为"应用很卡"。如果你只在应用层找原因,永远找不到根上。真正的排障原则是:下层的问题会向上传染,但上层的问题不会向下传染。所以自下而上的排查顺序在多数场景下更稳妥。
第二个坑是把"模型"当成"实现"。五层模型是为了教学和理解而抽象出来的,真实协议栈里,网卡做了一部分校验,操作系统内核做了另一部分,硬件卸载又把一些工作从CPU挪到了网卡上。所以你抓包看到的TCP重传,可能根本不是链路问题,而是本机接收窗口太小导致的。搞清楚软件和硬件的分工边界,比背模型更重要。
第三个坑是忽视"时间维度"。很多故障是间歇性的,用一次ping测试得出的结论毫无价值。链路误码往往是累积到一定量才爆发,队列拥塞只在高峰出现。所以排查工具要能长时间观察并输出统计量,而不是只给一次快照。后面每个层级我都会给几个能持续采样、能看错误计数的手段,这类工具才是真排障时的主力。
2. 物理层:所有玄学问题的最终归宿
物理层是唯一一层你无法用抓包工具直接看到内容的层,因为它传的是电平和光强,不是"数据"。但恰恰是这一层,制造了最多"重启一下就好了"的玄学。凡是那种时好时坏、重启就恢复、过几天又犯的问题,八成要往物理层想。
2.1 介质、接口和速率的对应关系
常见的有线介质就三类:双绞线、光纤、同轴(新建设施里基本见不到了)。双绞线靠的是差分信号抗共模干扰,一对线走一正一反,接收端相减,干扰被抵消掉。这就解释了为什么网线必须用双绞结构,也解释了为什么劣质网线在电磁环境复杂的地方特别容易出问题——屏蔽和绞距不达标,抗干扰能力直接崩盘。
速率和线材的对应关系是有硬性门槛的。百兆以太网用两对线(1-2、3-6),千兆必须四对全用上,2.5G和5G对线材的标称等级和长度更敏感。工程上通常以100米作为双绞线信道的长度上限,这个100米不是拍脑袋定的:它是"信号衰减"和"回波损耗"两项指标在标准速率下同时满足要求的最长距离。超过100米,衰减让信噪比下降,误码率上升,链路可能直接协商不上,也可能勉强连上但在高负载下频繁出错。
光纤这边要区分单模和多模。单模芯径小(通常9微米),只能传一个模式,色散小,适合长距离;多模芯径大(50或62.5微米),多个模式同时传播,存在模间色散,距离受限。模块和光纤必须匹配,单模模块配多模光纤或者反过来,轻则距离大幅缩水,重则完全不通。接口侧还要注意收发光功率是否在模块的接收灵敏度范围内,收光太强会烧接收端,太弱则误码。工程上一般留3dB左右的余量,这个余量的意义是给连接器老化、灰尘污染、弯折损耗留出空间。
2.2 编码与信号质量:线长了为什么就掉速
比特本身是抽象概念,要在铜缆上传输必须映射成电平变化,这个映射就是编码。早期以太网用曼彻斯特编码,每个比特中间必有一次跳变,接收端靠跳变沿恢复时钟,好处是自带同步能力,坏处是效率只有50%。千兆及以后普遍采用更高效的线路编码(比如4D-PAM5、PAM4配合复杂的前向纠错),效率提高了,但对信号质量的要求也水涨船高。
这就引出一个非常实用的判断:同一条链路,低速能通但高速不通,基本可以判定是物理层质量问题。因为高速率对信噪比、回波损耗、串扰的要求更严。你在现场遇到"千兆协商不上只能跑百兆",不要急着怀疑设备端口,先换一根确定合格的跳线,再用另一台设备交叉验证,如果换了设备还是百兆,那这条链路的路由、模块、接头就都值得怀疑了。这类问题的根源往往是水晶头压接不良、线序错误、线缆被重物压过导致特性阻抗变化,或者面板模块和线缆不是同一等级。
另一个高频现象是双工不匹配。一端强制全双工、另一端自动协商成半双工,或者一端是千兆固定、另一端因为线材问题退到百兆,都会造成一侧认为链路空闲可以随时发,另一侧却在检测冲突,结果是大量碰撞、迟到冲突、帧校验错误。表现就是"链路是up的,也能通,但速度只有预期的几分之一"。这种坑特别隐蔽,因为从指示灯上看一切正常。
2.3 物理层现场排障的三个动作
真到了现场,别急着敲命令,有三件事先做。
第一件事是看错误计数,这是判断物理层健康度最直接的指标。Linux下可以用两条命令:
# 看接口的收发统计,重点看 errors、dropped、overrun ip -s link show eth0 # 看网卡驱动的细分统计,重点看 crc_errors、rx_length_errors、collisions ethtool -S eth0判断标准很明确:链路稳定运行时,CRC类错误应该接近于零,或者增长极其缓慢。如果每分钟成百上千地涨,那就是物理层在丢帧,上层所有的重传、超时、卡顿都是它的下游反应。这时候换线、换端口、换模块,一次只换一个变量。
第二件事是确认协商结果。
ethtool eth0输出里的Speed、Duplex、Link detected三个字段要一起看。速度对不对、双工是不是Full、链路是否检测到,任何一个不对都要继续查。尤其注意有些设备默认关闭了自动协商,这种配置在混插不同厂商设备时极容易出问题。
第三件事,如果怀疑是MTU或者大包问题,用不分片的ping去验证路径能承载的最大包长:
# 1472 + 8(ICMP头) + 20(IP头) = 1500,正好等于标准以太网MTU ping -M do -s 1472 192.0.2.10 # 如果上面不通,逐步减小,找到能通的最大值 ping -M do -s 1400 192.0.2.10注意:
-M do是禁止分片,这个参数的意义在于让"路径上某一段MTU偏小"这件事暴露成明确的失败,而不是被中间设备静默分片掩盖掉。如果小包通、大包不通,说明路径上存在MTU瓶颈,往下就要看是不是隧道封装、PPPoE、或者某些链路层协议占了额外字节。
3. 数据链路层:同网段内谁把帧送到谁手上
跨过物理层,第一个有"地址"概念的层就是数据链路层。它解决的是一个非常具体的问题:在同一个广播域内,一台设备怎么把数据准确地交给另一台设备。注意"同一个广播域"这个限定,它划定了链路层的能力边界,也是"同网段通、跨网段不通"这类经典现象的理论根源。
3.1 以太网帧结构里那些常被忽略的细节
标准以太网帧从前往后依次是:7字节前导码、1字节帧起始定界符、6字节目的MAC、6字节源MAC、2字节类型/长度、46到1500字节的载荷、4字节帧校验序列。前导码和定界符不计入帧长,它们的作用是让接收端完成时钟同步,告诉对方"数据马上来了"。
这里有两个数字值得记住:最小帧长64字节,最大帧长1518字节(不含前导码和帧间隙)。最小帧长的由来和早期的共享介质冲突检测机制有关——为了让发送方在信号传到最远端的往返时间内仍能监听到冲突,帧不能太短,否则发送方已经发完了才检测到冲突,就没法重传。这个时隙被定为512比特时间,换算下来就是64字节。即使现在的交换网络基本都是全双工、不再有冲突,这个最小帧长作为兼容性约束依然保留着。所以你会看到ARP这种载荷很小的帧(ARP报文本身28字节)在线上实际是60字节左右,剩下的部分靠填充补足。
最大帧长1518字节对应MTU 1500。这个1500是IP层的最大传输单元,减去20字节IP头和20字节TCP头,留给应用数据的最大长度就是1460字节,也就是MSS。这个推导链条必须记牢,因为后面所有关于"大包不通""分片""MSS协商"的问题都从这里出发。
VLAN标签会插入4个字节,把最大帧撑到1522字节,这也是为什么抓包时经常看到"帧比预期长了4字节"的原因。有些老设备不支持超长帧,遇到带标签的帧直接丢弃,这类兼容性问题在混合组网时特别常见。
3.2 MAC地址、ARP与交换机的转发逻辑
MAC地址48位,前24位是厂商标识,后24位由厂商分配。它有两个特殊值必须认识:全F的广播地址,以及组播地址(第一个字节最低位为1)。交换机就是靠MAC地址工作的,它的核心机制是"源地址学习 + 目的地址转发",具体逻辑可以这样归纳:
- 收到帧,先把源MAC和入端口记入MAC地址表,并刷新老化计时器。
- 查目的MAC:命中且不在入端口,从对应端口转发;命中且在入端口,丢弃(不需要自己转发给自己)。
- 没命中,向除入端口外的所有端口泛洪;如果是广播或未知组播,同样泛洪。
这套机制简单高效,但会带来一个问题:未知单播泛洪。如果MAC表项老化或被撑爆,大量帧会被泛洪到所有端口,占用带宽、暴露流量、增加终端负担。MAC表容量是交换机的隐藏规格,低端设备可能只有几百条,接入终端一多就容易溢出,表现形式就是"莫名其妙变慢",而且往往无规律。
ARP是链路层和网络层之间的黏合剂。它的作用是把IP地址解析成MAC地址,流程是广播询问、单播应答。这里有几个非常实用的知识点:
- ARP缓存是有老化时间的,通常几分钟。老化期间如果对端换了网卡,通信就会发往错误MAC,直到缓存刷新。
- 免费ARP用于地址冲突检测和缓存刷新,设备上电时会发一个源和目的IP都是自己的ARP请求,如果有人应答,说明地址冲突了。
- ARP欺骗的防御靠的是端口安全、静态绑定、DHCP Snooping之类的机制,本质是让"谁对应哪个MAC"这件事变得不可伪造。
现场最常见的链路层故障就是IP冲突和ARP表异常。排查手段很直接:
# 查看ARP缓存,注意同一个MAC对应多个IP,或同一个IP频繁换MAC ip neigh show # 抓ARP包,观察是否有异常的大量请求或应答 tcpdump -i eth0 -nn -e arp提示:同一个MAC映射到多个IP,通常是私接路由器或者代理ARP造成的;同一个IP短时间内对应多个MAC,基本可以确定是IP地址冲突。这两种情况在网络里都会造成间歇性断连,而且用ping测的时候往往"看着是通的",极具迷惑性。
3.3 VLAN、Trunk与二层环路
VLAN解决的是广播域隔离问题。没有VLAN,一个二层网络就是一个大广播域,广播帧会送到每一个端口,终端多了之后广播风暴能把网络拖死。VLAN把一个物理交换机切成多个逻辑交换机,广播只在VLAN内传播。
端口有两种典型角色:Access口属于某个VLAN,接终端,收发的是不带标签的帧;Trunk口承载多个VLAN,帧带802.1Q标签,用于交换机之间互联。这里最容易出错的地方是两端VLAN配置不一致,一端Access VLAN 10,另一端Trunk允许VLAN 20,结果就是"链路是up的,但就是不通"。这类问题排查起来很快,看一眼两端配置就清楚了,难的是新手容易在物理层反复折腾。
二层环路是另一个经典灾难。交换机的转发逻辑里没有TTL这种防环机制,一旦出现环路,广播帧会在环里无限循环,短时间内把带宽和CPU吃满,整个广播域瘫痪。STP(生成树协议)就是为此设计的:通过选举根桥、计算最短路径,把冗余链路中的一部分端口阻塞掉,形成无环的逻辑拓扑。
根桥选举的规则是"桥ID最小者胜",桥ID由优先级(默认32768)加MAC地址组成。实际工程里通常会手动把核心交换机优先级调低,确保它稳定当根桥。因为如果让一台接入层设备意外成为根桥,整个网络的流量路径会变得极其别扭,表现为"明明是同机房的设备互访,却要绕到另一栋楼"。
# 查看生成树状态(不同厂商命令差异较大,这里是通用思路) # 关注:谁是根桥、各端口角色(Root/Designated/Alternate)、端口状态 show spanning-tree3.4 用抓包把二层看清楚
链路层的排查,抓包是最有说服力的手段。加上-e参数就能看到MAC地址:
# 抓20个ARP包,显示链路层头部 tcpdump -i eth0 -nn -e -c 20 arp # 抓VLAN标签(需要网卡支持并正确配置) tcpdump -i eth0 -nn -e vlan 10 # 抓特定源MAC的所有流量 tcpdump -i eth0 -nn -e ether src 00:11:22:33:44:55抓包时看三个东西:源和目的MAC是否符合预期、类型字段是什么(0x0800是IPv4,0x0806是ARP,0x8100是带VLAN标签)、帧长是否合理。如果看到大量重复的广播帧在同一个端口进出,环路基本坐实;如果看到ARP请求发出去了但没有应答,问题可能在对端主机或者中间的二层隔离(比如端口隔离、私有VLAN)。
我个人习惯是遇到"局域网内互访异常"先在两端各抓一小段包,对比一下。如果A发出去了B没收到,问题在中间链路;如果B收到了但没回应,问题在B的主机配置(防火墙、ARP、路由表);如果B回应了A没收到,那就是回程路径的问题。这个"三段对比法"能把责任范围压缩到很小,比盲目翻配置高效得多。
4. 网络层:选路、编址和那些绕不开的计算
网络层是整个协议栈里知识密度最高的一层,也是最容易考、最容易用错的一层。它的两件核心工作是编址和选路:给每台设备一个逻辑地址,然后决定数据包从哪条路走。
4.1 IPv4报头里真正要盯的字段
IPv4报头固定20字节,最长60字节。字段不少,但排障时真正高频使用的就那么几个:
| 字段 | 长度 | 排障价值 |
|---|---|---|
| 总长度 | 16位 | 判断是否分片、单包大小是否异常 |
| 标识 | 16位 | 同一数据报的所有分片共享此值,用来重组 |
| 标志位 | 3位 | DF位决定是否允许分片,MF位标记"后面还有分片" |
| 分片偏移 | 13位 | 以8字节为单位,重组时的位置依据 |
| TTL | 8位 | 每过一个路由器减1,减到0丢弃并回ICMP超时,是防环的核心 |
| 协议号 | 8位 | 6=TCP,17=UDP,1=ICMP,用于交付给上层 |
| 首部校验和 | 16位 | 只校验首部,不校验数据 |
TTL这个字段值得单独说。它的设计初衷是防止数据包在网络里无限打转,每经过一台三层设备就减1,减到0就丢弃并回一个ICMP超时消息。这个机制衍生出了两个极其实用的工具:traceroute原理就是发送TTL递增的包,让路径上每一跳依次返回超时消息,从而探测出完整路径;ping返回的TTL值则可以用来粗略判断对端操作系统和中间经过的跳数——初始TTL常见的是64、128、255,收到时看到63,基本可以推断中间过了一台设备且对端初始值是64。
协议号是另一个关键点。它决定了IP层把载荷交给谁。看到协议号6就知道里面是TCP,1就是ICMP。防火墙和ACL配置里经常需要指定协议号,抓包时也能靠它快速筛出关心的流量。
4.2 子网划分:把计算过程彻底走一遍
子网划分是网络层最实操的部分,也是很多人"看得懂但算不对"的地方。核心只有一个公式:块大小 = 256 - 掩码中关键的那一段十进制值。
先看一个标准的等长划分。把192.168.10.0/24划分成4个子网,需要借2位主机位,掩码从 /24 变成 /26,十进制是255.255.255.192。块大小 = 256 - 192 = 64。所以四个子网的起点依次是 .0、.64、.128、.192:
| 子网 | 网络地址 | 可用主机范围 | 广播地址 | 可用数量 |
|---|---|---|---|---|
| 1 | 192.168.10.0/26 | .1 - .62 | 192.168.10.63 | 62 |
| 2 | 192.168.10.64/26 | .65 - .126 | 192.168.10.127 | 62 |
| 3 | 192.168.10.128/26 | .129 - .190 | 192.168.10.191 | 62 |
| 4 | 192.168.10.192/26 | .193 - .254 | 192.168.10.255 | 62 |
可用数量是2的(32-26)次方减2,也就是64减2等于62。减掉的这两个就是网络地址和广播地址,它们不能分配给主机——这一点在给设备配地址时特别容易犯,见过有人把网关配成 .255 然后在群里问为什么不通。
再看一个反向题目,从掩码快速判断容量:/27的掩码是255.255.255.224,块大小 256 - 224 = 32,每个子网32个地址,可用30个。/28是240,块大小16,可用14个。这个规律记住之后,看到一个掩码能在三秒内说出它能装多少台设备,比现场拿计算器靠谱。
路由聚合是反向操作,把多条明细路由合并成一条。比如有192.168.0.0/24到192.168.3.0/24这四条,看第三段的二进制:0是 00000000,1是 00000001,2是 00000010,3是 00000011。前6位完全相同,所以可以聚合成192.168.0.0/22,掩码255.255.252.0,块大小4,正好覆盖 .0 到 .3。聚合要满足"必须是2的幂次且起始地址对齐"这两个条件,否则会多覆盖不属于自己的网段,造成路由黑洞。
注意:规划地址时一定要预留。我见过太多项目把 /24 一刀切成 /26 全部分配出去,半年后要加一个部门,发现没有一个连续的块可用,只能在别处重新起一段地址,路由表越来越碎。实际做法是按业务类型分段、每段留出至少一倍的余量,同类型的业务放在连续地址段里,方便后续聚合。
4.3 路由表和最长前缀匹配
一台设备决定往哪发,看的是路由表。表的每一条包含目的网络、掩码、下一跳和出接口。查表规则是最长前缀匹配:在所有能匹配上目的地址的条目里,选掩码最长的那条。
举个例子,路由表里有:
ip route show # 192.168.0.0/16 via 10.0.0.1 dev eth0 # 192.168.10.0/24 via 10.0.0.2 dev eth0 # 0.0.0.0/0 via 10.0.0.254 dev eth0目标地址是192.168.10.55,三条都能匹配,但 /24 最长,所以走 10.0.0.2。目标地址是192.168.20.1,/24 不匹配,/16 匹配,走 10.0.0.1。目标地址是203.0.113.5,只有默认路由匹配,走网关。
这个规则解释了一个非常常见的现象:明细路由永远优先于默认路由。所以你在做策略路由或者分流的时候,加一条更长的前缀就能把特定流量引到指定出口,不需要动默认路由。
ip route get这个命令值得熟练使用,它能直接告诉你"去往某个地址,系统实际选了哪条路由、从哪个接口出、源地址是什么":
ip route get 203.0.113.5排查"为什么流量走了错误出口"这类问题时,这一条命令比翻整张路由表快得多。尤其在有多个默认路由、策略路由、多网卡的情况下,路由表可能有几十条,肉眼比对容易出错。
4.4 分片、TTL与ICMP的实战用法
分片这事,理论上是网络层的合法行为,实际上是个性能杀手。原因在于分片后的重组要消耗资源,只要丢一个分片整个数据报就得重传,而且很多防火墙对分片的处理策略不一致。所以现代网络普遍通过路径MTU发现机制来避免分片:发送方把IP头的DF位置1,中间设备如果发现包比自己的MTU大,就回一个ICMP"需要分片但DF已置位"的消息,发送方据此调小包长。
这套机制的软肋在于它依赖ICMP。如果链路上有设备把ICMP全封了,路径MTU发现就失效,表现为"小包正常、大包不通、连接建立后卡死"。这类问题极难排查,因为ping小包永远通。判断方法就是用前面提到的ping -M do -s逐步找最大可通包长,如果 1472 不通但 1400 通,说明路径上有设备MTU小于1500,通常出现在隧道封装、PPPoE拨号、某些链路层协议开销较大的场景。
ICMP本身也是排障利器,但经常被误解。除了ping和traceroute用到的echo请求/应答和超时消息,还有几个值得认识的类型:
| ICMP类型 | 含义 | 排障用途 |
|---|---|---|
| 3 | 目的不可达 | 细分码能区分是网络不可达、主机不可达还是端口不可达 |
| 11 | 超时 | TTL减到0时返回,traceroute的基础 |
| 5 | 重定向 | 提示主机存在更优网关,出现频繁说明网关配置不当 |
| 8/0 | 回显请求/应答 | ping的底层实现 |
其中"端口不可达"(类型3码3)特别有用:它意味着包已经到达了目标主机,只是目标主机上没有进程监听那个端口。所以如果ping不通但能收到端口不可达,说明网络是通的,问题在服务本身;反过来,如果连端口不可达都收不到,那才需要怀疑网络层。
5. 传输层:端口、连接和那些决定速度的参数
传输层是应用能直接感知到的最后一层。它把"主机到主机"的通信细化为"进程到进程"的通信,靠端口号做区分。TCP提供可靠的有序字节流,UDP提供简单的数据报服务,两者的取舍贯穿了整个应用架构设计。
5.1 端口和TCP报头的关键字段
端口号16位,范围0到65535。0到1023是知名端口,需要特权才能绑定;1024到49151是注册端口;49152以上是动态端口,客户端发起连接时由系统临时分配。这个划分有个实际影响:一台服务器对外提供服务的端口是固定的,但它的客户端连接会占用大量动态端口,如果连接数极高,可能把本地端口耗尽,表现为"连不上新连接但CPU和带宽都不高"。
TCP报头也是20字节固定部分,几个字段必须吃透:
- 序号(Sequence Number):本段数据第一个字节在整个字节流里的位置。注意SYN和FIN虽然不携带数据,但各消耗一个序号,这个细节在做序列号推算时经常出错。
- 确认号(Acknowledgment Number):期望收到的下一个字节的序号,等于"已收到的最后一个字节序号加1"。
- 数据偏移:指出报头长度,4位,单位是4字节,所以最大60字节,说明选项部分最多40字节。
- 标志位:SYN、ACK、FIN、RST、PSH、URG。RST值得单独提,它表示复位连接,收到RST通常意味着对方没有监听这个端口,或者中间设备主动干预了。
- 窗口:16位,接收方告诉发送方自己还能接收多少字节。这个字段决定了吞吐量的上限。
- 校验和:覆盖伪首部、TCP头和数据,比IP的校验更完整。
5.2 三次握手和四次挥手的状态追踪
三次握手的意义是双向确认收发能力和初始序列号。整个过程:
- 客户端发SYN,携带自己的初始序列号
x,进入SYN_SENT。 - 服务器回SYN+ACK,携带自己的初始序列号
y,确认号x+1,进入SYN_RCVD。 - 客户端回ACK,序号
x+1,确认号y+1,双方进入ESTABLISHED。
为什么是三次不是两次?因为两次只能确认"客户端能发、服务器能收"这一半,服务器无法确认自己的发送能力被客户端接收到了,也无法确认客户端收到了自己的初始序列号。第三次ACK补上了这一环。这个解释听起来像考试题,但它有实际价值:如果你在抓包里只看到SYN和SYN+ACK,没有第三次ACK,说明问题在客户端侧——可能是客户端收到SYN+ACK后崩溃了,也可能是回程路径被防火墙拦了。
四次挥手是因为TCP是全双工的,两个方向要各自关闭:
- 主动关闭方发FIN,进入
FIN_WAIT_1。 - 被动关闭方回ACK,进入
CLOSE_WAIT,主动方进入FIN_WAIT_2。 - 被动方处理完剩余数据后发FIN,进入
LAST_ACK。 - 主动方回ACK,进入
TIME_WAIT,等待2MSL后真正关闭。
TIME_WAIT是最容易被误解的状态。它存在的理由是两条:一是保证最后一个ACK能到达对端,如果ACK丢了,对端会重发FIN,此时连接还在才能重发ACK;二是让本次连接中延迟到达的报文在网络中消亡,避免污染使用相同四元组的新连接。2MSL的等待时间在多数系统上是60秒,这在高并发短连接场景下会造成大量端口占用。
CLOSE_WAIT堆积则是另一类经典问题:说明被动关闭方收到了FIN但没有调用close,通常是应用代码里有连接泄漏,忘了释放资源。看到某个进程的CLOSE_WAIT数量持续增长,直接去查它的连接池和异常分支处理,不用怀疑网络。
# 统计各状态连接数,CLOSE_WAIT和TIME_WAIT的数量变化最能说明问题 ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn # 看整体概览 ss -s5.3 窗口、重传和拥塞控制:速度到底由谁决定
传输速度不是单纯由带宽决定的,实际上是三个限制取最小值:带宽、接收窗口、拥塞窗口。
接收窗口是接收方通告的,表示"我缓冲区还能放多少"。TCP头里窗口字段只有16位,最大65535字节,这个值在长肥管道上远远不够。所以引入了窗口缩放选项,通过移位把窗口放大,最大可放大到约1GB。这个选项只在握手时协商,一旦协商失败,窗口就固定在64KB,链路再好也跑不快——这是"能ping通但传文件极慢"的常见原因之一。判断方法就是抓握手包,看双方是否都带了窗口缩放选项。
拥塞控制是发送方自己维护的,它不知道网络到底有多堵,只能靠丢包和延迟来试探。经典算法分四个阶段:
| 阶段 | 触发条件 | 行为 |
|---|---|---|
| 慢启动 | 连接建立或重传超时后 | 拥塞窗口从初始值开始,每收到一个ACK增加一个MSS,等效每个RTT翻倍 |
| 拥塞避免 | 窗口达到慢启动阈值 | 每个RTT只增加一个MSS,线性增长 |
| 快重传 | 收到3个重复ACK | 不等待超时,立即重传丢失的段 |
| 快恢复 | 快重传之后 | 阈值减半,窗口从新阈值开始线性增长,不回到慢启动 |
慢启动的初始窗口在现代实现里通常是10个MSS,也就是约14.6KB。这个数字解释了一个现象:短连接的首屏加载速度受初始窗口限制,即使带宽充足,前几个RTT能传的数据量也很有限。这也是为什么HTTP/2、HTTP/3要做连接复用——复用省掉的正是反复慢启动的开销。
重传超时的时间RTO是根据RTT动态计算的,大致公式是"平滑RTT + 4倍RTT偏差"。RTT抖动越大,RTO越保守,重传等待越久,用户体验越差。所以单纯的高延迟不可怕,抖动才可怕。
看TCP的重传统计,Linux上有现成的工具:
# 查看TCP连接的详细统计,包括重传段数 ss -tin # 系统级TCP统计,重点看 retrans_segs 和 retransmit 相关计数 nstat -az | grep -i -E 'retrans|timeout'重传率长期高于1%就值得查了,可能是链路误码、可能是拥塞、也可能是对端接收窗口太小导致不必要的重传。区分方法是看RTT和重传的分布,如果RTT平稳但重传集中在特定对端,多半是对端问题;如果整条链路都在抖,回去看物理层。
5.4 UDP的适用边界和常见误区
UDP只做了端口复用和最基本的校验,不保证顺序、不保证送达、不控制拥塞。这听起来像是缺陷,但在特定场景下反而是优势:没有握手延迟、没有重传等待、没有队头阻塞。
适合UDP的场景有个共同特征——单个数据报的价值随时间快速衰减。实时音视频通话里,一个已经延迟300毫秒的语音包重传回来也没用了,不如丢掉。DNS查询也是,一问一答,用UDP省掉握手,快得多。游戏同步、监控指标上报、日志采集,都符合这个特征。
但"用UDP就快"是个误解。UDP快是因为它不做那些TCP做的保障工作,代价是应用层要自己承担。现代的做法往往是在UDP之上重新实现可靠性和拥塞控制,把控制权拿到应用层,典型的思路就是基于UDP的可靠传输协议。这类方案的核心优势在于可以按业务定制重传策略,比如音视频可以只重传关键帧、可以优先保证时延而不是完整性。
UDP还有个坑是数据报边界。UDP保留消息边界,发一次收一次,上层必须自己处理粘包问题。而且UDP不负责分片重组之外的任何事,超过MTU的数据报在IP层分片,任何一个分片丢失整个数据报就废了,所以应用层通常会把单包控制在1200字节以内,给隧道封装留出余量。
6. 排查实录:把四层的知识串成一套动作
知识点的价值在于被调用。前面四层拆开讲了,最后一节讲怎么把它们组织成一套排障流程,以及几个反复出现的坑。
6.1 自下而上还是自上而下
这两种顺序没有绝对对错,取决于你掌握的信息量。
如果你完全不了解现场,只知道"网不通",那就自下而上。先看链路是否up、看错误计数、看ARP、看网关能否ping通、看端口是否可达,一层层往上排除。这个过程慢但不会漏。
如果你已经知道"别的服务都正常,就这一个不行",那就自上而下,直接从应用和传输层入手,因为下层大概率是好的。这时候从物理层开始查纯属浪费时间。
还有一个更高效的做法是二分定位:先ping目标IP。ping通说明网络层及以下基本没问题,直接跳到传输层查端口;ping不通就往上回退到网络层,查路由和ARP。这一刀把四层砍成两半,能省掉大量无效动作。
用一句话概括判断依据:
| 现象 | 首要怀疑层 |
|---|---|
| 完全不通,连网关都ping不到 | 物理层 / 数据链路层 |
| 同网段通,跨网段不通 | 网络层(网关、路由、掩码) |
| 能ping通,端口连不上 | 传输层(监听、防火墙、ACL) |
| 连接能建立,传输极慢 | 传输层(窗口、重传、MSS)或物理层误码 |
| 时通时断,无规律 | 物理层(链路质量、双工)或链路层(环路、地址冲突) |
6.2 常见问题速查表
| 现象 | 最可能原因 | 验证方法 | 处理动作 |
|---|---|---|---|
| 千兆口只能跑百兆 | 线材等级不够、线序错误、水晶头压接不良 | ethtool看协商结果,换合格跳线复测 | 更换线缆或模块,逐段排查 |
| 链路up但速率极低 | 双工不匹配、大量CRC错误 | ethtool -S看错误计数 | 统一两端双工配置,优先自动协商 |
| 局域网内互访断续 | IP地址冲突、ARP表异常 | ip neigh看MAC与IP对应关系 | 定位冲突设备,做地址绑定 |
| 网络突然全瘫、广播量大 | 二层环路 | 抓包看重复广播帧,查STP状态 | 断开冗余链路,修正STP配置 |
| 大包不通、小包正常 | 路径MTU小于1500 | ping -M do -s逐步二分 | 调小MSS,检查隧道和封装开销 |
| 能ping通但服务连不上 | 端口未监听或被拦截 | ss -tlnp看监听,tcpdump看是否收到SYN | 启动服务或调整策略 |
| 传大文件速度上不去 | 窗口未协商、重传率高 | 抓握手包看窗口缩放,ss -tin看重传 | 调整缓冲区,排查链路丢包 |
| 大量CLOSE_WAIT堆积 | 应用未正确关闭连接 | ss -tan统计状态分布 | 排查代码资源释放逻辑 |
| 大量TIME_WAIT堆积 | 短连接过多 | 同上 | 启用连接复用,调整相关参数 |
6.3 几条踩出来的经验
第一条,先确认"变了吗",再确认"哪里变了"。网络突然出问题,绝大多数时候是有人动过配置、换过设备、加过终端。先问清楚最近有什么变更,比直接上工具高效十倍。我见过排查两小时最后发现是插了一根网线形成环路的案例,也见过反复重启设备最后发现是DHCP地址池耗尽的案例,共同点是变更没有记录。
第二条,不要迷信单次测试结果。ping通一下不代表链路稳定,网页能打开不代表带宽够用。真要评估质量,就得挂长时间采样,看错误计数的增长趋势、看重传率、看延迟的分位数而不是平均值。平均值90毫秒但偶尔飙到3秒,用户体感就是"卡",而平均值完全体现不出来。
第三条,一次只改一个变量。同时换线、换端口、改配置,即使问题消失了,你也不知道是哪个动作起的作用,下次遇到同样问题还是不会。排障的价值不只在于解决当前问题,更在于积累可复用的因果判断。
第四条,学会读错误计数的方向。接收方向的CRC错误说明信号在到达本机之前就坏了,问题在链路上游;发送方向的错误说明本机发出时就出了问题,可能和驱动、硬件、协商有关。这个方向性判断能砍掉一半的排查范围,但很多人只看"有错误"就结束了。
第五条,MSS钳制和MTU发现,是跨网段问题的万能钥匙之一。凡是"能连上但传数据就卡死"的诡异现象,先怀疑MTU。这个怀疑成本极低,验证也快,收益却很高。
我个人在实际操作中的体会是,五层模型最大的用处不是让你背出每一层的协议,而是给你一个收敛怀疑范围的框架。真正的排障过程很少是线性的,更像是在几个层之间来回跳,但每一次跳跃都应该有依据——依据就是这一层该负责什么、不该负责什么。等你对每层的边界足够熟悉,很多问题的答案会在几秒钟内浮现出来,那种感觉就像看惯了地图之后,不用导航也知道该往哪个方向走。至于工具,命令会变、界面会变、厂商会换,但这四层的分工几十年没变过,把这块地基打牢,学什么都快。