干了几年后端,说实话最怕的不是业务逻辑写出bug,而是线上突然来一句“接口超时”“连接被拒绝”“数据库连不上了”。这时候你翻日志,代码层面一切正常,问题几乎都卡在网络链路上的某一环。每次被这种问题折磨完,我都会重新翻一遍计网知识点——这套东西平时躺在教科书里显得很理论,真到排查现场就成了救命地图。所以我想把这些年在实际中用得上的计算机网络知识做一个系统梳理,从OSI分层、IP子网划分,到TCP的握手和可靠性机制,再到HTTP、DNS这些每天打交道的应用层协议,最后收在排查实战上。不管是正在准备面试的读者,还是跟我一样偶尔要和交换机、路由器、抓包工具较劲的同行,这份知识点清单应该都能直接用上。
1. 先搭一张地图:计网知识体系的整体框架
1.1 为什么所有网络问题都可以先归到某一层
很多初学者学计网最痛苦的地方,是不知道从哪儿看起。教材先讲物理层、数据链路层,第二页就开始讲曼彻斯特编码,看了三天还在底层打转,完全看不到全貌。我的建议是反过来:先把OSI七层的名字和作用记牢,再把TCP/IP四层作为实际工作的主线,之后所有知识都往这张地图上挂。
OSI七层从下到上依次是物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。这里值得强调的是,会话层和表示层在现实世界的协议栈里基本被融合进应用层了,所以实际使用的TCP/IP模型只有四层:网络接口层、网络层、传输层、应用层。面试时如果被问“TCP/IP四层和OSI七层的对应关系”,核心考点就在这两层“消失”了,以及网络接口层对应了OSI的物理层和数据链路层。
为什么要分层?我用寄快递来打比方。你写好的信(应用层数据)不会被直接扔进邮车,而是要先装进信封,写上收件人和地址(这一层相当于网络层加IP头部),再贴上快递单号(传输层端口),最后装进快递袋(链路层加MAC头部)。每一层只关心自己的那一层封装,不需要知道上层内容是什么。网络分层解决的是“复杂问题拆解”的问题:每一层只需要和上下层通过固定接口对接,内部怎么实现是自由的。这就带来一个实际好处——排查网络故障时,可以按层缩小范围。物理层丢包就往网线、光纤上查;链路层问题看MAC地址、ARP;网络层问题查IP路由;传输层问题查端口、连接状态。这个分层思维是整套计网知识的骨架,没有它,后面所有细节都只是零散碎片。
1.2 数据包封装与解封装:一次请求完整走过的路
理解了分层,下一步要看数据在每一层是怎么处理的。这里有个关键词叫“封装”。比如你发起一个HTTP请求,应用层先构造一个HTTP报文,包含请求行、请求头和请求体。传到传输层,TCP会把这个报文当成自己的载荷,在它前面加上TCP头部,里面最重要的是源端口和目标端口——端口号决定了这个数据到了对方机器后交给哪个进程。继续往下进入网络层,IP协议加上IP头部,里面最重要的就是源IP和目标IP,这一步决定了数据怎么从一台机器路由到另一台机器。最后到网络接口层,加上以太网头部,包含源MAC地址和目标MAC地址,尾部还有校验字段,这就是一帧。
接收方做的动作是反向的,叫解封装:每层剥掉对应的头部,把载荷向上交给更高层。这个“套娃”过程是整个计网数据流动的核心,几乎所有的协议抓包内容本质上都在展示这个过程。
我为什么花这么多篇幅讲封装?因为很多面试题和实际排查场景都会落到这里。比如你问“为什么网络抓包看不到HTTP内容,只能看到TCP报文”,因为数据在传输层已经被加密或者被分段了,只有重组之后才能看到应用层内容。再比如“MTU是什么”,就是网络接口层一帧能承载的最大载荷,超过这个值IP层就要做分片,分片之后遇到防火墙某些设置还会被丢弃,这类问题是网络性能优化里的经典坑。
1.3 用抓包把抽象概念变成可见的信号
只看理论和图解,计网知识很容易记成死背。我自己的转折点是开始用抓包工具之后——所有抽象概念都被展开了,TCP握手变成了一条条带标志位的报文,DNS查询变成了一次次请求响应。
常用的抓包工具就是Wireshark和tcpdump。Wireshark适合图形化分析,tcpdump适合在服务器上命令行抓包。一个最基础的用法是这样:
# 抓取本机所有HTTP流量,不解析域名,保存到文件 tcpdump -i eth0 'tcp port 80' -w http.pcap # 抓取某个IP的所有进出流量 tcpdump -i eth0 host 192.168.1.100 # 抓TCP三次握手包,只看SYN和ACK标志 tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0'抓包的意义在于帮你建立验证感。书上说“TCP三次握手”,你抓一次包,亲眼看到SYN、SYN-ACK、ACK三条报文,就再也不会忘。说“DNS用UDP 53端口”,你自己发一个nslookup指令,再抓包看到一条条UDP查询,记忆会非常牢。我建议学计网知识点的人不要纯刷题,每个关键协议都实际抓一次包,效率会高很多。
2. 硬骨头:IP地址与子网划分
2.1 IP地址结构:网络位和主机位决定了一切
IP地址是整个网络层最重要的基础。IPv4地址是32位的二进制数,表示成点分十进制,比如192.168.1.10。这个地址看起来只是四组数字,其实内部被分成两部分:网络位和主机位。网络位标识设备所在的网络,主机位标识网络内的具体设备。
这个概念是子网划分的前提。知道怎么区分网络位和主机位,靠的是子网掩码。比如255.255.255.0这个掩码,二进制是连续的24个1加8个0,说明IP前24位是网络位,后8位是主机位。网络位的价值在于路由寻址——路由器在转发数据包的时候,只看目标IP的网络位,不看主机位,就能确定应该往哪个方向转发。这就像快递员送件只看城市名一样,不需要知道每条街每个门牌号,先送到城市节点再说。
额外的常见考点是IP地址分类。A类地址范围是1.0.0.0到127.255.255.255,默认掩码是8位;B类是128.0.0.0到191.255.255.255,默认掩码16位;C类是192.0.0.0到223.255.255.255,默认掩码24位。还有三个特殊的地址段要记牢:127.0.0.0/8是回环地址,本机自己访问自己用,数据根本不会出网卡;169.254.0.0/16是链路本地地址,DHCP分配失败时系统会自动拿一个,看到这种IP基本可以判断网卡没拿到IP;224.0.0.0/4是组播地址。
2.2 子网掩码和CIDR的实际计算
面试和实际配置里逃不掉的,是CIDR和子网计算。CIDR的表示法很简单,就是在IP后面加个斜杠,写上网络位的个数,比如192.168.1.0/24,意思是前24位是网络位。这时候你看到一个IP地址,要能快速算出它的网络地址、广播地址和可用主机范围。
我平时用的计算思路是这样的:
- 先看斜杠后面的数字n,主机位就是32-n位
- 可用主机数量是2^(32-n)-2,减掉的2个分别是网络地址和广播地址
- 网络地址就是把IP的主机位全部置0,广播地址就是把主机位全部置1
举个例子,192.168.1.66/26。n=26,主机位6位,所以可用地址数是2^6-2=62个。这个地址属于哪个子网?把66转成二进制是01000010,前26位是网络位,后6位是主机位,所以网络位最后的两个bit是01,也就是说这个IP所在的子网,是从192.168.1.64开始的,网络地址是192.168.1.64,广播地址是192.168.1.127,可用范围是192.168.1.65到192.168.1.126。
这个计算不用手算也行,服务器上装个ipcalc,一行命令就出结果:
ipcalc 192.168.1.66/26 # 输出会直接给出Network、Netmask、Broadcast、HostMin、HostMax但考试和面试时不一定让你用工具,所以手算的套路还是要熟。我的经验是把2的幂次背熟:2^8=256,2^7=128,2^6=64,2^5=32,2^4=16。多数场景下只需要和这几个数打交道。
2.3 私有地址、网关和路由:配置网络时的三个关键点
搞清楚了地址计算,还要知道哪些地址能用在公网上,哪些只能在内网用。RFC 1918规定了三个私有地址段:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16。这三个段在公网上不会被路由,专门给内网使用。家里路由器默认网段192.168.1.0/24就是私有地址段的典型例子。
实际配网络的时候,有四个东西必须配对:IP地址、子网掩码、网关、DNS。IP和掩码决定了你属于哪个网络,网关是你的数据发往其他网络的出口,DNS负责把域名解析成IP。我碰过不少案例,用户说“上不了网”,排查下来IP和掩码都对,网关ping不通,最后发现是网关IP写错了一个数字。排查这类问题,记住一句话就够了:先看本机网卡是否up,再看IP掩码网关是否匹配,最后ping网关、ping公网IP、ping域名,逐层定位。
3. 传输层:TCP是怎么做到“靠谱”的
3.1 三次握手不是三次打招呼,而是一套状态同步机制
TCP被誉为“可靠传输”的基石,而三次握手就是它建立连接的方式。很多人把三次握手理解为“你好了吗”“我好,你呢”“我也好”,这个比喻够亲切,但不够精确。三次握手的真正作用是同步双方的初始序列号。
客户端先发一个SYN报文,把自己的序列号设为x,同时进入SYN_SENT状态。服务端收到后,回复SYN-ACK报文,确认号是x+1,同时把自己的序列号设为y。客户端收到后再发一个ACK报文,确认号是y+1,连接建立。这时候双方都知道了对方的初始序列号,后续数据传输时可以用序列号做可靠性的验证。
这背后的门道在于:TCP是面向字节流的协议,数据被拆成一个一个段,每个字节都有一个序列号。收发双方只有知道了对方的序列号起点,才能正确排序和去重。如果没有握手过程直接发数据,接收方无法判断哪个段先到哪个段后到,也无法判断有没有丢段。
面试题里常问“为什么不能两次握手”。从安全性角度说,两次握手会导致一个明显问题:重复的SYN请求会让服务端误建连接,白白浪费资源,而且服务端无法确认自己回复的SYN-ACK是否被客户端收到。有了第三次握手,服务端只有在收到客户端ACK之后才会进入ESTABLISHED状态,就能有效规避这种情况。
实际排障时,我见过服务器上大量SYN_RECV状态的连接,这是半连接队列满了的表现,常见原因是服务端处理不过来或者被恶意连接请求塞满。处理思路是调大somaxconn参数,同时配合SYN cookies机制,而不是简单重启服务。
3.2 四次挥手和TIME_WAIT:连接关闭里藏着的坑
断开连接要四次挥手,这比建立连接多了一次,原因是TCP是全双工的,两个方向的数据通道要各自独立关闭。客户端先发FIN,表示“我不再发数据了”;服务端回ACK,表示“收到”,但服务端可能还有数据要发,所以不急着关闭;等服务端把数据发完,再发FIN;客户端再回ACK。整个关闭过程是四次报文交互。
其中TIME_WAIT是高频考点。主动关闭连接的一方,在发出最后一个ACK之后不会立刻进入CLOSED状态,而是要等2MSL(最大报文生存时间的两倍)。为什么等?两个原因:一是确保最后的ACK能到达对方,如果丢了,对方会重发FIN,主动方通过TIME_WAIT还能处理;二是让这条连接上的所有旧报文在网络中自然消失,防止它和下一次使用相同四元组的连接混淆。
这个机制在实际服务器排障中很常见。短连接服务如果频繁关闭连接,你会发现服务器上TIME_WAIT连接特别多,如果超出系统限制,新连接就可能建不起来。生产环境我一般会结合业务场景调整这个参数:对内部服务,可以开启tcp_tw_reuse来复用TIME_WAIT连接,但要确认前提是时间戳选项开启。而对外提供服务的场景,我会优先考虑让应用层使用长连接,而不是粗暴调整内核参数。
3.3 可靠传输的四个关键机制
TCP可靠性不只是靠三次握手那一下,它是靠一套组合机制撑起来的。第一是序列号与确认应答,每个数据段都有序列号,接收方收到后回确认号表示“你发到这儿为止的数据我都收到了”。第二是超时重传,发送方发出数据后启动定时器,如果在规定时间内没收到ACK,就重传这一段。第三是滑动窗口流量控制,接收方通过窗口大小告诉发送方“你最多可以连续发多少数据不用等确认”,发送方按这个窗口发,就不会把接收方缓冲区塞爆。第四是拥塞控制,它解决的是网络内部的拥堵问题,和流量控制解决的“接收方能力”问题不一样。
拥塞控制里有几个核心概念:慢启动、拥塞避免、快重传、快恢复。慢启动是从一个很小的拥塞窗口开始,指数增长,直到到达阈值;之后进入拥塞避免阶段,窗口线性增长。一旦发生丢包,说明网络可能拥塞了,阈值会降到当前窗口的一半,窗口回到初始值重来(或者走快重传快恢复路径)。这套机制解释了为什么新建立的连接前期吞吐量不高——窗口还不够大,需要慢慢“探路”。
实际开发里,理解这些机制能避免很多误判。比如一条TCP连接在大带宽高延迟链路(所谓的BDP场景)上跑不满带宽,原因往往不是带宽不够,而是拥塞窗口还没增长到能填满管道的程度。这时候开启TCP窗口缩放选项,或者用BBR这类更现代的拥塞控制算法,效果会立竿见影。
3.4 UDP不“简单”,TCP也未必总适合你
面试官经常追问“既然TCP这么可靠,为什么还有UDP存在”。答案是很多场景根本不需要可靠传输,或者自己可以在应用层做可靠性。UDP没有握手、没有确认、没有重传,头部只有8字节,比TCP头部小了将近一半,也没有连接状态,这意味着开销低、延迟低、转发性能高。视频通话、实时语音、游戏同步这些场景,一个丢包的重传代价远高于直接丢掉一个旧帧。DNS查询也是典型的UDP应用,一个请求一个响应,丢了重发一次就行,用TCP反而增加握手成本。
后来UDP上还长出了不同方向的改进协议。传输方向有QUIC,它在UDP之上自己实现了类似TCP的可靠性、加密和连接迁移能力,专门解决HTTP/2时期TCP队头阻塞的问题。HTTP/3就是跑在QUIC上的。理解这条演进线索,面试和实际选型都能用上:不是所有可靠性都必须由TCP提供,关键看业务对延迟和吞吐的需求。
4. 应用层的两只看门狗:HTTP与DNS
4.1 HTTP请求过程与状态码速查
应用层是离开发人员最近的协议层,HTTP更是每天都要打交道。一个HTTP请求分成请求行、请求头、空行、请求体四部分。请求行里有方法、URL路径和协议版本,比如GET /api/v1/user HTTP/1.1。请求头里最常用的有Host(指定域名)、User-Agent(客户端标识)、Content-Type(请求体格式)、Authorization(认证信息)。响应也一样,有状态行、响应头和响应体。
状态码按5个大类记:
- 2xx表示成功,200是OK,201是创建成功,204是无内容
- 3xx表示重定向,301永久重定向,302临时重定向,304未修改,命中缓存时会看到
- 4xx表示客户端错误,400参数错误,401未认证,403无权限,404资源不存在,429请求太频繁
- 5xx表示服务端错误,500服务器内部错误,502网关收到无效响应,503服务不可用,504网关超时
我实际排障中最常见的状态码是504和502。出现502,通常是后端服务崩了或者连接超时;出现504,通常是网关和后端之间等待时间过长。这时候先看后端服务日志,再看网关配置里面的超时时间,基本能定位。而304出现次数多,不代表服务器有问题,它说明浏览器缓存正常工作,我遇到过不少同事把304当异常,其实不是。
另外,HTTP是无状态的协议,为了维持登录状态,引入了Cookie和Session机制。Cookie存在浏览器端,每次请求自动带上;Session存在服务器端,通过Session ID关联。后续演进出了Token机制,用JWT这类自包含的Token做认证,让服务器不需要存会话信息,在分布式环境下更好扩展。
4.2 HTTPS加密链路:TLS握手保证了什么
HTTPS解决的问题,本质上是HTTP的三桩罪:明文传输、不验证通信方身份、数据容易被篡改。它的底层是TLS协议,在TCP之上加了一层加密会话。
TLS握手的核心逻辑是混合加密。第一步,服务器把证书发给客户端,证书里有服务器的公钥和CA机构的签名。第二步,客户端验证证书有效性,验证通过后用对方的公钥加密一个临时对称密钥发回去。第三步,服务器用私钥解出这个对称密钥,之后双方都用对称密钥加密通信数据。这样既解决了对称密钥分发难题(通过非对称加密传输密钥),又保证了后续通信的性能(对称加密处理速度远快于非对称)。
搞懂这个流程,很多面试问题就迎刃而解。比如“为什么要证书”,因为公钥裸露在网络里,你不知道它是不是真的来自你访问的网站,CA的作用就是给公钥做背书。又比如“中间人攻击是怎么回事”,就是黑客在客户端和服务器之间插入自己,伪造证书骗取信任。客户端会报证书不可信,这就是为什么公共WiFi下访问HTTP网站容易出问题,而HTTPS的安全性从握手阶段就立起来了。
在实际运维里验证TLS连接,我常用openssl命令:
# 查看某个域名的证书信息和TLS握手详情 openssl s_client -connect example.com:443 -servername example.com这个命令能直接看到证书链、加密套件和握手结果,排查证书过期、加密套件不支持之类的问题非常方便。
4.3 DNS解析全流程与故障排查
很多网络问题的根子其实卡在DNS上。一个域名解析的完整路径是:浏览器先查本地浏览器缓存,再查操作系统缓存(hosts文件优先),然后向本地DNS服务器发起递归查询,本地DNS服务器再替你走迭代查询,依次问根DNS服务器、顶级域名服务器、权威DNS服务器,拿到最终的IP地址返回。
关键参数是TTL,它告诉缓存服务器这个解析结果能存多久。TTL设太长,域名切换IP后生效慢;TTL设太短,解析请求量大。做域名迁移时,标准做法是提前把TTL调小,等切换完成后过一段时间再调回来,这样能把缓存失效时间缩短到几分钟级别。
我遇到过的DNS故障主要有几种表现:一是域名解析到旧IP,检查TTL和本地缓存;二是解析不到任何IP,先看本地DNS配置,再看权威服务器是否正常;三是部分地域解析异常,通常是CDN调度问题,需要查分线路解析配置。排查工具就三个,nslookup查看解析结果,dig查看详细的查询过程,再到公共DNS服务器上对比解析。系统上还能用/etc/resolv.conf查看配置的DNS服务器地址——不过现在很多系统都用systemd-resolved管理了,直接改这个文件不一定生效,更稳妥的是用resolvectl或nmcli。
5. 网络排查实战与避坑心得
5.1 我屡试不爽的五步排查法
网络问题最忌讳乱试。我习惯的排查流程是固定的,沿着OSI分层从底往上走,每层都有对应的命令:
第一步,确认物理连接。看网卡状态:ip link show,如果状态是DOWN或者没有carrier,基本就是网线、交换机端口的问题。第二步,确认网络层配置。用ip addr看IP地址和掩码,用ip route看默认路由,先ping自己的网关IP,通了再往外走。第三步,确认到目标可达。用ping测延迟和丢包率,用traceroute(或mtr)看路径上哪一跳异常。第四步,确认端口和连接状态。用ss -tnp看本机监听端口和所有TCP连接状态,用telnet ip port或nc -vz ip port快速验证远端某个端口是否开放。第五步,看应用层请求。用curl -v看完整HTTP响应,用浏览器开发者工具看请求耗时。
这套流程走下来,80%的问题都能定位到某一层。印象最深的一次是生产环境接口偶发超时,从应用层一直查到链路层,最后发现是物理机上某个网卡的多队列中断不均匀,CPU单核跑满导致收包延迟。如果不是按分层排查,很难把“接口超时”和“网卡中断”联系起来。
5.2 高频故障速查表
我先说几个最常见的故障和它们对应的方向:
| 现象 | 可能原因 | 优先排查命令/手段 |
|---|---|---|
| ping不通网关 | 网卡未up、IP配置错、交换机端口隔离 | ip link、ip addr、arp -n |
| 能ping通IP但域名解析失败 | DNS服务器配置错误、DNS服务异常 | nslookup、dig、resolvectl status |
| 连接被拒绝 | 端口未监听、防火墙拦截、服务未启动 | ss -tlnp、systemctl status、telnet |
| 请求超时 | 中间设备丢包、防火墙静默丢弃、TCP握手过慢 | mtr、tcpdump抓握手包 |
| 大量TIME_WAIT | 短连接过多、连接复用配置未开启 | ss -s统计、ss -tan |
| 服务偶发卡顿 | 拥塞控制算法不合适、网卡丢包、CPU软中断高 | ethtool -S、top看si% |
| HTTPS证书报错 | 证书过期、证书链不完整、主机名不匹配 | openssl s_client、curl -v |
这张表不是万能答案,它能帮你在紧急时刻快速圈定方向。真实环境下经常是多因素叠加,比如某个供应商的专线抖动造成的丢包,会被运维误判成服务端慢,结果调了一晚上代码,最后发现是运营商光衰过大。
5.3 抓包定位问题的完整示例
我给个真实的小案例。有一次某服务对外接口偶发延迟高,持续了半个小时又恢复正常。先用ping测网关,零丢包;再测公网IP,也没问题;但一旦调用业务接口就慢。用tcpdump在客户端抓包,命令是这样的:
tcpdump -i eth0 -nn 'tcp port 443' -w /tmp/trace.pcap然后立刻复现一次慢请求,再抓包分析。一看包才发现,TCP握手的第一个SYN发出去之后,隔了整整1秒才收到SYN-ACK,后续每个包都有接近1秒的间隙。这说明问题不在服务器处理逻辑,而在TCP握手路径上。后来追到链路层,发现是安全设备对某些TCP选项做了重组,遇到特定窗口大小就延迟转发。如果当时直接去优化应用代码,这个问题根本不可能解决。
这就是抓包的价值——它让网络层面的问题不再靠猜。我强烈建议每个做后端和运维的同事,至少掌握tcpdump的常用用法和Wireshark的基本过滤表达式。不会抓包之前,网络问题像玄学;会了抓包之后,一切都有据可查。
5.4 避坑心得:关于内核参数和“调一下就好”
排查过程中有一个容易走极端的点——动不动就调内核参数。网上很多文章说“把net.ipv4.tcp_tw_reuse设为1,把tcp_max_tw_buckets调大”,问题就解决了。我在生产环境上吃过亏,这些参数是链路级的,一个参数改动会影响整个节点所有连接,而不只是你家那一个service。
我的原则是:能通过应用层解决的不动内核,能通过架构解决的不动单机。短链接太多,先考虑连接池;连接池解决不了,再考虑长连接;真到了非调不可的程度,先在测试环境做对比压测,确认影响面后再上生产。另外,调完内核参数要在重启后验证是否持久化,用sysctl的配置要确保写进了/etc/sysctl.conf之类的持久化文件。
还有个小技巧值得分享:记着每个服务的正常延迟基线和连接数基线。没有基线,你无法判断当前数据算不算异常。我会定期把一些核心指标的ss输出、ping延迟存下来,哪怕只是存到本地日志里,关键时刻对比一下,比翻半天监控来得快。
网络知识点这套东西,看起来庞杂,拆开之后其实就是一张分层地图加几个高频协议。每次遇到网络问题,我都会在心里默念“从底往上,逐层排除”——这个概念帮我在无数次抓狂的凌晨稳定了心态。也希望这篇梳理能让你在下次面对“连接超时”的时候,心里不是一句“重启试试”,而是一个清晰的排查路径。