简介:这份PDF资料面向准备技术面试的IT求职者与网络初学者,系统梳理了计算机网络的核心考点,帮助读者在面试与工程实践中快速建立知识框架。内容围绕OSI七层参考模型与TCP/IP四层模型展开,涵盖TCP/IP协议族、TCP/UDP/SCTP协议对比、三次握手与四次挥手、TCP状态机、TIME_WAIT状态、端口号、超时重传与快速重传、TCP Header结构、可靠传输、流量控制与拥塞控制,以及IPv4、IPv6、ICMP、ARP、IGMP等网络层协议,并附有常见面试问题解析。资源包共1个PDF文件,大小约2.08MB,结构清晰、便于按章节检索复习。目前已有969人学习,适合需要系统梳理网络知识、查漏补缺或冲刺面试的读者参考。
1. 从一次“网络不通”的排查说起:计算机网络基础知识到底该学什么
很多人第一次被网络问题卡住,不是不会写代码,而是不知道从哪里看。应用层日志显示请求超时,传输层可能连 SYN 都没发出去;ping通了但业务端口连不上,问题可能出在防火墙策略而不是路由。计算机网络基础知识之所以重要,是因为它决定了你排查问题时有没有一张“分层地图”。这张地图最常用的两个版本,一个是 OSI 七层模型,一个是 TCP/IP 模型。前者适合教学和职责划分,后者才是真实协议栈的落地形态。学它的目标不是背概念,而是能回答三个问题:数据从网卡到应用经过了哪些层、每层可能在哪里丢、用什么命令能验证。本文面向需要做后端开发、嵌入式联网、测试运维或准备 408 考研的读者,把 OSI、TCP/IP、TCP、UDP 这些高频词串成一条可复现的排查路径。
2. OSI 七层与 TCP/IP 四层:分层模型怎么对应到真实协议栈
2.1 两张模型图的映射关系与选型理由
OSI 七层模型把网络通信拆成物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。TCP/IP 模型通常讲四层:网络接口层、网际层、传输层、应用层。实际工程里,会话层和表示层的功能大多被应用层协议或库消化了,比如 TLS 放在应用层和传输层之间,JSON 序列化属于表示层职责但由业务代码完成。所以排查问题时,我一般用 TCP/IP 四层做主线,用 OSI 七层做细分定位。
| OSI 七层 | TCP/IP 四层 | 典型协议/设备 | 排查命令或现象 |
|---|---|---|---|
| 物理层 | 网络接口层 | 网线、光模块、网卡 | 网卡灯不亮、ethtool看链路 |
| 数据链路层 | 网络接口层 | 以太网、ARP、交换机 | arp -a、MAC 地址表 |
| 网络层 | 网际层 | IP、ICMP、IGMP、路由器 | ping、traceroute、ip route |
| 传输层 | 传输层 | TCP、UDP | ss -tunlp、端口连通性 |
| 会话层 | 应用层 | RPC 会话、Socket 连接 | 连接复用、超时重连 |
| 表示层 | 应用层 | TLS、编码、序列化 | 抓包看明文/密文 |
| 应用层 | 应用层 | HTTP、DNS、Modbus TCP | curl、dig、业务日志 |
这张表的价值在于:当你说“网络不通”时,先判断是哪一层的问题。物理层看链路状态,网络层看 IP 和路由,传输层看端口和握手,应用层看协议格式和业务逻辑。很多“系统检测到异常流量”的提示,实际是应用层风控或网关策略,不是 TCP/IP 协议栈本身故障。
2.2 用ping、traceroute、ss做一次分层验证
下面这组命令适合在 Linux 上按顺序执行,用来判断一个目标服务是否可达。假设目标 IP 是192.168.1.100,端口8080。
# 1. 网络层:看目标 IP 是否可达,ICMP 是否被放行 ping -c 4 192.168.1.100 # 2. 网络层:看路径经过哪些路由器,定位在哪一跳开始丢包 traceroute -n 192.168.1.100 # 3. 传输层:看本机监听端口和已建立连接 ss -tunlp | grep 8080 # 4. 传输层:测试目标 TCP 端口是否开放,不依赖应用协议 nc -zv 192.168.1.100 8080 # 5. 应用层:用 curl 看 HTTP 响应头和状态码 curl -v --max-time 5 http://192.168.1.100:8080/health逻辑说明:ping走 ICMP,属于网络层,能通不代表 TCP 端口开放;traceroute看路径,如果某一跳后全是*,可能是中间设备禁了 ICMP 或路由不可达;ss看本机 socket 状态,LISTEN表示服务在听,ESTAB表示连接已建立;nc直接测 TCP 三次握手,比ping更接近业务;curl验证应用层协议。参数上,-c 4表示发 4 个包,-n表示不解析域名,-tunlp分别表示 TCP、UDP、数字端口、监听状态、进程名。如果ping通但nc不通,优先查防火墙和 iptables/nftables 规则,而不是怀疑网卡。
2.3 数据在 TCP/IP 模型中的封装与解封装过程
发送端从应用层往下走:应用数据加 TCP 头变成段,加 IP 头变成包,加以太网头变成帧,最后变成比特流。接收端反过来,每层剥掉自己的头,交给上层。这个过程中,MTU 和 MSS 是常见坑。以太网 MTU 通常 1500 字节,IP 头 20 字节,TCP 头 20 字节,所以 TCP 有效载荷 MSS 一般是 1460 字节。如果应用层一次写超过 MSS,TCP 会自动分段;UDP 不会分段,超过 MTU 时 IP 层会分片,接收端重组失败就丢包。这就是为什么iperf3用 UDP 打流时,如果包长设得太大,会出现大量丢包和乱序。
# 用 iperf3 测试 UDP 吞吐,包长 1400 字节,带宽 100M,持续 10 秒 iperf3 -c 192.168.1.100 -u -l 1400 -b 100M -t 10参数说明:-u表示 UDP,-l 1400是应用层载荷长度,加上 UDP 头 8 字节和 IP 头 20 字节,总 IP 包长 1428 字节,小于 1500,避免分片;-b 100M是目标带宽;-t 10是持续时间。如果-l设成 1472 以上,IP 层就会分片,在丢包环境下重组成功率下降,测出来的吞吐会失真。这个细节在嵌入式 UDP 调试和 C# UDP 分包组包里同样适用。
3. TCP 三次握手与四次挥手:连接建立和释放的每一步怎么验证
3.1 三次握手的报文序列与状态迁移
TCP 是面向连接的可靠传输协议。三次握手的过程是:客户端发 SYN,服务端回 SYN+ACK,客户端再回 ACK。服务端收到 ACK 后进入 ESTABLISHED,客户端在收到 SYN+ACK 后也进入 ESTABLISHED。为什么不是两次?因为两次握手无法确认客户端的接收能力和服务端的发送能力同时正常,且容易受历史重复 SYN 影响。第三次 ACK 让服务端确认客户端确实收到了自己的 SYN+ACK。
用tcpdump可以完整看到这个过程:
# 抓取本机与 192.168.1.100 的 8080 端口之间的 TCP 握手包 tcpdump -i any -nn -S 'tcp port 8080 and host 192.168.1.100'输出里会看到[S]、[S.]、[.]三种标志。-S让序列号显示绝对值而不是相对值,方便对照。-nn不解析域名和端口名。如果只看到 SYN 没有 SYN+ACK,说明服务端没监听、防火墙拦截或路由不可达;如果看到 SYN+ACK 但客户端没回 ACK,可能是客户端被安全软件拦截或路由不对称。
3.2 四次挥手的主动关闭与被动关闭
TCP 是全双工的,所以关闭需要四次挥手:主动关闭方发 FIN,对方回 ACK;对方处理完数据后发 FIN,主动方回 ACK。主动方最后进入 TIME_WAIT,等待 2MSL 后彻底关闭。TIME_WAIT 的作用是确保最后一个 ACK 能到达,并让旧连接的重复报文在网络中消失。高并发短连接服务如果出现大量 TIME_WAIT,会占用本地端口,导致无法建立新连接。
# 统计本机 TIME_WAIT 连接数量 ss -tan | awk '$1=="TIME-WAIT" {count++} END {print count}' # 查看内核相关参数 sysctl net.ipv4.tcp_tw_reuse sysctl net.ipv4.tcp_max_tw_bucketstcp_tw_reuse允许将 TIME_WAIT 状态的 socket 复用于新的出站连接,但只对客户端有效,且需要时间戳支持。tcp_max_tw_buckets限制 TIME_WAIT 数量,超过后内核会直接清理并打日志。生产环境更推荐用连接池或长连接,而不是盲目调这两个参数。如果是服务端主动关闭,大量 TIME_WAIT 会落在服务端,此时应让客户端主动关闭,或使用 SO_REUSEADDR。
3.3 用 Python 写一个最小 TCP 服务端和客户端验证握手
下面这段代码可以在本地跑通一次完整的 TCP 连接建立、数据收发和关闭。服务端监听127.0.0.1:9000,客户端连接后发送一条消息。
# tcp_server.py import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(("127.0.0.1", 9000)) server.listen(5) print("listening on 127.0.0.1:9000") conn, addr = server.accept() print("connected by", addr) data = conn.recv(1024) print("received:", data.decode()) conn.sendall(b"ack from server") conn.close() server.close()# tcp_client.py import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 9000)) client.sendall(b"hello tcp") resp = client.recv(1024) print("response:", resp.decode()) client.close()逻辑说明:SO_REUSEADDR让服务端重启时不必等待 TIME_WAIT 结束就能绑定同一端口。listen(5)是半连接队列长度,不是最大连接数。recv(1024)一次最多读 1024 字节,TCP 是字节流,不保证一次recv对应一次send,所以应用层需要自己定义消息边界,比如长度前缀或分隔符。这也是 C# UDP 分包组包和 Modbus TCP 解析时同样要处理的问题。
4. UDP 与 TCP 的选型边界:什么时候该用 UDP,怎么测
4.1 TCP 和 UDP 的区别不只是“可靠”和“不可靠”
TCP 提供可靠、有序、面向连接的字节流,有流量控制和拥塞控制。UDP 提供无连接、不可靠的数据报,不保证顺序和到达,但头部开销小、延迟低、没有队头阻塞。选型时不要只看“可靠”两个字。实时音视频、游戏状态同步、DNS 查询、DHCP、SNMP 这类场景更适合 UDP,因为丢一两个包比等重传更可接受。Modbus TCP 虽然名字里有 TCP,但它是基于 TCP 的工业协议,不是 UDP。IGMP 是网络层的组播管理协议,用于主机告诉路由器自己加入了哪个组播组,和 TCP/UDP 不在同一层。
| 对比项 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接,三次握手 | 无连接,直接发 |
| 可靠性 | 确认、重传、排序 | 不保证 |
| 头部开销 | 20 字节起 | 8 字节 |
| 传输方式 | 字节流 | 数据报 |
| 拥塞控制 | 有 | 无 |
| 典型场景 | HTTP、SSH、Modbus TCP | DNS、视频、游戏、iperf3 UDP |
4.2 用 Python 发 UDP 包并观察分片
UDP 发送端不需要握手,直接sendto。下面代码发送一个 2000 字节的 UDP 包,超过 MTU 后会触发 IP 分片。
# udp_sender.py import socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) payload = b"A" * 2000 # 超过常见 MTU 1500,会触发 IP 分片 sock.sendto(payload, ("127.0.0.1", 9001)) sock.close()# udp_receiver.py import socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(("127.0.0.1", 9001)) data, addr = sock.recvfrom(4096) print("received bytes:", len(data), "from", addr) sock.close()逻辑说明:SOCK_DGRAM表示 UDP。recvfrom(4096)的缓冲区要大于可能的最大数据报,否则多余部分会被截断。在本地回环上,MTU 通常很大,2000 字节不会分片;但在以太网上,1500 字节 MTU 下,2000 字节会被分成两个 IP 片。如果中间设备禁止分片或重组超时,接收端就收不到完整数据。排查时可以用tcpdump -i any -nn 'udp port 9001'看是否有分片标志。read udp: unknown error (code=10054)这类错误在 Windows 上通常表示对端不可达,ICMP 端口不可达报文被返回给了 UDP socket。
4.3 用 iperf3 做 UDP 打流和丢包统计
iperf3是常用的带宽测试工具,UDP 模式下能给出丢包率和抖动。
# 服务端 iperf3 -s # 客户端:UDP 模式,带宽 50M,包长 1200,持续 20 秒,每 1 秒报告一次 iperf3 -c 192.168.1.100 -u -b 50M -l 1200 -t 20 -i 1参数说明:-s是服务端,-c是客户端,-u是 UDP,-b是目标带宽,-l是包长,-t是时长,-i是报告间隔。结果里重点看Lost/Total Datagrams和Jitter。如果丢包率随带宽上升而急剧增加,说明链路或接收端处理能力到瓶颈了。包长建议设为 1200 左右,留出余量避免分片。嵌入式设备如 ESP01S 发 TCP 消息时,也要注意发送缓冲区大小和分包策略,不能一次写入超过可用缓冲区的数据。
5. 避坑与排查:网络基础知识落地时最容易翻车的 5 个点
5.1 现象:ping通但业务端口连不上
原因:ICMP 和 TCP 是不同协议,防火墙可能放行 ICMP 但拦截 TCP 端口。解决:用nc -zv或telnet测目标端口,检查 iptables/nftables、安全组和云平台网络 ACL。不要用ping的结果判断业务可用性。
5.2 现象:TCP 连接建立后立刻被重置
原因:服务端进程崩溃、连接队列满、或中间设备发送 RST。解决:用tcpdump抓包看 RST 来自哪一方。如果是服务端发 RST,检查listenbacklog 和somaxconn;如果是中间设备,检查负载均衡健康检查配置。
5.3 现象:UDP 收不到数据但发送端无报错
原因:UDP 无连接,发送成功只表示数据交给了内核,不代表对端收到。解决:在接收端用tcpdump确认包是否到达网卡,检查接收缓冲区net.core.rmem_max和SO_RCVBUF。如果包到达但应用没读到,可能是缓冲区溢出。
5.4 现象:大量 TIME_WAIT 导致端口耗尽
原因:短连接频繁主动关闭,TIME_WAIT 默认 60 秒。解决:改用长连接或连接池;客户端可开启tcp_tw_reuse;服务端避免主动关闭。不要随意把tcp_fin_timeout调得过小,可能引发旧连接数据错乱。
5.5 现象:抓包看到分片但应用层数据不完整
原因:UDP 或 IP 分片在中间设备被丢弃,或接收端重组缓冲区不足。解决:控制应用层单包大小在 MTU 以内,通常 UDP 载荷不超过 1472 字节,推荐 1200 字节左右。如果必须发大包,改用 TCP 或应用层分片。
6. 把分层排查变成习惯:一个可复用的检查清单和抓包技巧
我自己的习惯是:遇到网络问题先画四层,从下往上排除。物理层看网卡和链路状态,网络层看 IP、路由和 ICMP,传输层看端口和握手,应用层看协议和日志。这个顺序能避免一上来就翻业务代码。下面这张检查清单可以贴在工位上。
| 层级 | 检查项 | 命令或工具 | 通过标准 |
|---|---|---|---|
| 物理层 | 网卡是否 UP | ip link | 状态 UP,有 MAC |
| 数据链路层 | ARP 是否解析 | arp -a或ip neigh | 目标 IP 有 MAC |
| 网络层 | IP 和路由 | ip addr、ip route | 有默认网关,路由可达 |
| 网络层 | ICMP 可达 | ping | 有回包,丢包率低 |
| 传输层 | 端口监听 | ss -tunlp | 目标端口 LISTEN |
| 传输层 | TCP 握手 | tcpdump、nc | 看到 SYN/SYN+ACK/ACK |
| 应用层 | 协议响应 | curl、dig、业务日志 | 状态码正常,数据完整 |
抓包时我一般用tcpdump先看有没有包,再用 Wireshark 看协议细节。tcpdump的-w可以把包存成 pcap 文件,方便后续分析。
# 抓取 8080 端口的前 100 个包,保存到文件 tcpdump -i any -nn -c 100 -w /tmp/tcp8080.pcap 'tcp port 8080'-c 100表示抓满 100 个包就停,-w写文件。分析时重点看三次握手是否完成、有没有 RST、有没有重传。如果看到大量重传,说明链路质量差或拥塞;如果看到零窗口,说明接收端处理不过来。这些信号比应用日志更早暴露问题。
最后说一个我踩过的坑:曾经在一个嵌入式项目里,UDP 发送端一直正常,接收端偶尔丢包,查了两天才发现是接收缓冲区太小,突发流量一来就溢出。后来把SO_RCVBUF调大并加了应用层序号,问题才稳定。网络基础知识不是背出来的,是抓包抓出来的。希望帮到你。
本文还有配套的精品资源,点击获取