恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
计算机网络基础知识:从OSI七层到TCP三次握手与抓包排查实战
首页
资讯中心
/
计算机网络基础知识:从OSI七层到TCP三次握手与抓包排查实战
计算机网络基础知识:从OSI七层到TCP三次握手与抓包排查实战
发布时间:2026/10/5 7:05:39
简介这份PDF资料聚焦计算机网络基础知识面向准备技术面试的求职者与需要夯实网络底层的IT从业者系统梳理了面试中的高频核心考点。内容以网络模型为主线从OSI七层参考模型讲起逐层说明物理层、数据链路层、网络层、传输层、会话层、表示层与应用层的职责划分再过渡到工程实际使用的TCP/IP四层模型并对比两者的区别与联系。资源对TCP/IP协议族展开细致讲解涵盖TCP、UDP、SCTP三种传输层协议的特性差异重点剖析TCP三次握手建链、四次挥手关闭、状态机流转、TIME_WAIT状态成因、端口号作用、超时重传与快速重传、Header结构、可靠传输、流量控制与拥塞控制等机制同时介绍IPv4、IPv6、ICMP、ARP、RARP、IGMP等网络层协议。资源包为1个PDF文件大小约2.08MB目录层级清晰便于按知识点检索复习。目前已有969人学习适合用于面试突击、知识查漏补缺与网络原理系统梳理。1. 计算机网络基础知识从一根网线到一次 HTTP 请求中间到底发生了什么很多人对「计算机网络基础知识」的印象停留在背 OSI 七层模型的名字但真正让一线工程师翻车的往往不是记不住分层而是不知道一个数据包从浏览器发出到服务端收到中间被谁改过、在哪一层丢的。你写了个 TCP 客户端连不上报address already in use你抓包看到三次握手完成了但业务没响应你配了 Modbus TCP 主站却读不到寄存器——这些问题的答案全在分层模型里只是没人告诉你每一层对应哪个具体现象。这篇笔记面向三类人准备系统补网络基础的开发者、需要排查线上连接问题的后端和 DevOps 工程师、以及做嵌入式或工控通信比如 Modbus TCP、串口转以太网的从业者。我会把 OSI 和 TCP/IP 的对应关系讲清楚然后落到能直接跑的 socket 代码、抓包命令和参数调优上让你从「知道有七层」变成「能定位是哪一层出的问题」。2. OSI 七层与 TCP/IP 四层分层设计到底解决了什么工程问题2.1 为什么要有分层一次协议替换的真实代价分层的本质是把变化隔离在单层内部。假设没有分层你换一块网卡就要重写 HTTP 解析代码因为物理介质和业务逻辑耦合在一起。有了分层网卡驱动只关心帧的收发IP 层只关心寻址和路由TCP 层只关心可靠传输HTTP 只关心请求响应语义。每一层只暴露固定的接口给上下层这就是「协议」的意义。OSI 七层是理论参考模型TCP/IP 四层是工程落地模型。它们的对应关系必须记牢因为排查问题时你说的「二层问题」和「三层问题」指向完全不同的工具OSI 层TCP/IP 层典型协议排查工具常见故障现象物理层网络接口层以太网电信号网线测试仪、ethtool网卡灯不亮、link down数据链路层网络接口层Ethernet、ARP、VLANarp -a、tcpdump -eMAC 冲突、ARP 欺骗、VLAN 不通网络层网际层IP、ICMP、路由协议ping、traceroute、ip route路由不可达、TTL 耗尽传输层传输层TCP、UDPss -tlnp、netstat端口未监听、连接超时、RST会话层应用层RPC、TLS 会话openssl s_client会话中断、证书错误表示层应用层编码、序列化抓包看 payload乱码、字节序错误应用层应用层HTTP、DNS、Modbuscurl、dig、Modbus Poll业务逻辑错误、超时提示实际排查时不要死记七层记住「物理→链路→网络→传输→应用」这条自下而上的顺序就够了从最底层开始排除能省掉大量玄学时间。2.2 数据封装与解封装一个 HTTP 请求的完整旅程当你在浏览器输入一个地址数据是这样一层层包上去的应用层生成 HTTP 请求报文GET /index.html HTTP/1.1加上 Host、User-Agent 等头部。传输层加上 TCP 头源端口随机高端口、目的端口 80、序列号、确认号、窗口大小、标志位SYN/ACK/FIN。网络层加上 IP 头源 IP、目的 IP、TTL、协议号TCP 是 6UDP 是 17。数据链路层加上以太网帧头源 MAC、目的 MAC通过 ARP 获取网关 MAC、类型字段0x0800 表示 IPv4。物理层把帧转成电信号或光信号发出去。到达服务端后反向解封装每一层剥掉自己的头把 payload 交给上层。理解这个过程的价值在于你知道每一层加了什么头就知道抓包时该看哪个字段。比如tcpdump默认只显示到传输层加-e才能看到 MAC 地址加-v能看到 TTL 和窗口大小。2.3 用 tcpdump 验证分层抓一次 ping 和一次 HTTP理论讲完必须动手验证。下面这条命令抓取经过 eth0 的 ICMP 包ping 属于网络层# -i 指定网卡-n 不解析域名-e 显示链路层 MAC 地址icmp 是过滤表达式 sudo tcpdump -i eth0 -n -e icmp执行后另开终端ping 8.8.8.8你会看到类似输出12:00:01.123456 aa:bb:cc:dd:ee:ff 11:22:33:44:55:66, ethertype IPv4 (0x0800), length 98: 192.168.1.100 8.8.8.8: ICMP echo request, id 1, seq 1, length 64这里aa:bb:cc:dd:ee:ff是本机 MAC11:22:33:44:55:66是网关 MACethertype IPv4说明链路层承载的是 IPv4后面才是 IP 层和 ICMP 层的信息。一条命令同时看到了二层、三层和 ICMP 协议这就是分层在抓包里的直观体现。再抓一次 HTTP 请求# -A 以 ASCII 显示 payload-s 0 抓完整包不截断port 80 过滤 HTTP sudo tcpdump -i eth0 -n -A -s 0 port 80你会看到 TCP 三次握手Flags [S]、Flags [S.]、Flags [.]之后才出现GET / HTTP/1.1。如果只看到 SYN 没有 SYN-ACK问题在服务端或中间网络如果三次握手完成但没看到 GET问题在客户端应用层。这就是分层排查法的实际用法先确定卡在哪一层再深入那一层。3. TCP 三次握手与四次挥手连接建立和断开的每个状态怎么排查3.1 三次握手的每个包长什么样TCP 是面向连接的可靠传输协议连接建立需要三次握手客户端发SYN序列号seqx进入SYN_SENT状态。服务端回SYNACK序列号seqy确认号ackx1进入SYN_RCVD状态。客户端发ACK确认号acky1双方进入ESTABLISHED状态。用tcpdump抓一次完整握手# 抓取与 192.168.1.200 的 80 端口通信-S 显示绝对序列号 sudo tcpdump -i eth0 -n -S host 192.168.1.200 and port 80典型输出IP 192.168.1.100.54321 192.168.1.200.80: Flags [S], seq 1000, win 64240, options [mss 1460,sackOK,TS val 123 ecr 0,nop,wscale 7], length 0 IP 192.168.1.200.80 192.168.1.100.54321: Flags [S.], seq 2000, ack 1001, win 65535, options [mss 1460,sackOK,TS val 456 ecr 123,nop,wscale 7], length 0 IP 192.168.1.100.54321 192.168.1.200.80: Flags [.], ack 2001, win 64240, length 0关键字段解读Flags [S]是 SYNFlags [S.]是 SYNACK点表示 ACK 标志置位Flags [.]是纯 ACK。win是接收窗口mss是最大段大小wscale是窗口缩放因子。如果只看到第一个 SYN 没有第二个包说明服务端没响应或中间被防火墙拦截如果第二个包是Flags [R.]RST说明服务端端口没监听。3.2 四次挥手与 TIME_WAIT 状态断开连接需要四次挥手因为 TCP 是全双工的每个方向要单独关闭主动关闭方发FIN进入FIN_WAIT_1。被动关闭方回ACK进入CLOSE_WAIT主动方进入FIN_WAIT_2。被动关闭方处理完数据后发FIN进入LAST_ACK。主动关闭方回ACK进入TIME_WAIT等待 2MSL通常 60 秒后关闭。TIME_WAIT是最容易被误解的状态。它不是 bug而是保证最后一个 ACK 能到达对端、以及让旧连接的延迟包在网络中消散。但高并发短连接场景下大量TIME_WAIT会耗尽本地端口报address already in use。查看当前状态分布# 统计各 TCP 状态的数量 ss -tan | awk NR1 {print $1} | sort | uniq -c | sort -rn如果TIME_WAIT数量异常高可以调整内核参数# 允许 TIME_WAIT 状态的 socket 被新连接复用 sudo sysctl -w net.ipv4.tcp_tw_reuse1 # 扩大本地端口范围默认 32768-60999可扩到 1024-65535 sudo sysctl -w net.ipv4.ip_local_port_range1024 65535 # 减少 FIN_WAIT_2 超时时间 sudo sysctl -w net.ipv4.tcp_fin_timeout15注意tcp_tw_recycle在较新内核中已移除不要再用tcp_tw_reuse只对主动发起连接的一方有效且需要时间戳支持。3.3 用 Python 写一个最小 TCP 客户端验证握手光看抓包不够自己写一个客户端能加深理解。下面是最小可运行代码import socket import time # 创建 TCP socketAF_INET 表示 IPv4SOCK_STREAM 表示 TCP client socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置连接超时 5 秒避免 SYN 无响应时一直阻塞 client.settimeout(5) try: # connect 触发三次握手成功返回说明进入 ESTABLISHED client.connect((192.168.1.200, 80)) print(TCP connected, local port:, client.getsockname()[1]) # 发送一个最小 HTTP 请求 request bGET / HTTP/1.1\r\nHost: 192.168.1.200\r\nConnection: close\r\n\r\n client.sendall(request) # 接收响应recv 返回空字节表示对端关闭 while True: data client.recv(4096) if not data: break print(data.decode(errorsignore)) except socket.timeout: print(connect timeout: SYN 无响应检查服务端是否监听或防火墙) except ConnectionRefusedError: print(connection refused: 收到 RST服务端端口未监听) finally: client.close()这段代码的关键点connect()内部完成三次握手如果超时说明 SYN 或 SYN-ACK 丢了如果收到ConnectionRefusedError说明对端回了 RST通常是端口没开。sendall()保证数据全部发出recv()返回空表示对端发了 FIN。运行这个脚本的同时用tcpdump抓包你能亲眼看到握手和挥手过程比看十遍理论都管用。4. TCP 与 UDP 怎么选从 Modbus TCP 到实时控制的取舍4.1 可靠性 vs 实时性一张表说清适用场景TCP 和 UDP 的选择不是「哪个更好」而是「哪个更适合当前需求」。核心差异维度TCPUDP连接方式面向连接三次握手无连接直接发可靠性确认重传保证有序到达不保证到达不保证顺序头部开销20 字节起8 字节传输效率较低有拥塞控制较高无拥塞控制适用场景HTTP、文件传输、数据库、Modbus TCPDNS、视频流、游戏、实时采集典型问题队头阻塞、TIME_WAIT 堆积丢包、乱序、无反馈Modbus TCP 虽然名字带 TCP但它把 Modbus RTU 的帧封装进 TCP payload用 TCP 保证可靠传输适合工控场景中对数据完整性要求高的读写。而如果是高频传感器采集每秒几千个点丢一两个无所谓但延迟必须低UDP 更合适。4.2 用 UDP 写一个最小通信示例UDP 的代码比 TCP 简单得多没有握手和挥手import socket # SOCK_DGRAM 表示 UDP server socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 绑定本地地址和端口 server.bind((0.0.0.0, 9999)) print(UDP server listening on 9999) while True: # recvfrom 返回数据和发送方地址 data, addr server.recvfrom(1024) print(freceived {data.decode()} from {addr}) # 原样回发模拟一个 echo 服务 server.sendto(becho: data, addr)客户端import socket client socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 设置接收超时UDP 没有连接状态只能靠超时判断丢包 client.settimeout(2) client.sendto(bhello udp, (127.0.0.1, 9999)) try: data, addr client.recvfrom(1024) print(reply:, data.decode()) except socket.timeout: print(no reply: 包可能丢了或服务端未启动)UDP 没有connect和acceptsendto直接指定目标地址。注意settimeout在 UDP 里是判断丢包的唯一手段因为协议本身不提供确认机制。如果你需要可靠性要么在应用层自己实现 ACK 和重传要么直接用 TCP。4.3 端口号与常见服务对照端口号是传输层的概念TCP 和 UDP 各有独立的 65535 个端口。排查连接问题时先确认端口是否监听# -t 只看 TCP-l 只看监听-n 不解析服务名-p 显示进程 ss -tlnp # 看 UDP 监听 ss -ulnp常见端口对照端口协议服务排查命令22TCPSSHss -tlnp | grep 2280TCPHTTPcurl -v http://host443TCPHTTPSopenssl s_client -connect host:44353UDP/TCPDNSdig host domain502TCPModbus TCPnc -zv host 5023306TCPMySQLmysql -h host -P 3306提示nc -zv host port是验证端口连通性最快的命令-z表示只扫描不发数据-v显示详细结果。如果显示succeeded说明三次握手成功refused说明端口没监听timed out说明被防火墙拦截。5. 避坑与排查网络编程里最容易翻车的 5 个场景5.1 坑一address already in use不是端口被占用那么简单现象重启服务时报OSError: [Errno 98] Address already in use但ss -tlnp看不到任何进程监听该端口。原因上一次连接处于TIME_WAIT状态socket 还没完全释放。TCP 规定主动关闭方要等 2MSL 才能复用同一四元组。解决在bind之前设置SO_REUSEADDRimport socket s socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 允许绑定处于 TIME_WAIT 的地址 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((0.0.0.0, 8080)) s.listen(128)SO_REUSEADDR让内核忽略TIME_WAIT状态直接复用端口这是服务端代码的标配。注意它和SO_REUSEPORT不同后者允许多个进程绑定同一端口做负载均衡。5.2 坑二TCP 客户端重连时报地址已在使用现象Java 或 C# 客户端断线重连时抛Address already in use但客户端用的是随机端口。原因客户端主动关闭连接后进入TIME_WAIT短时间内大量重连导致本地端口耗尽。常见于高频短连接场景比如 Modbus TCP 轮询。解决两个方向。一是客户端设置SO_REUSEADDR并改用长连接避免频繁建连二是调整内核参数扩大端口范围并开启tcp_tw_reuse。如果是 C# 的TcpClient确保Close()后正确Dispose()否则 socket 句柄泄漏会加剧问题。5.3 坑三抓包看到三次握手完成但业务无响应现象tcpdump显示 SYN、SYN-ACK、ACK 都正常但客户端发数据后没有响应连接最终超时。原因三次握手是内核完成的即使应用层没调用accept()内核也会回 SYN-ACK。所以握手成功不代表应用层就绪。常见于服务端进程卡死、线程池耗尽、或accept队列满了。解决检查服务端listen的 backlog 参数默认值可能太小# backlog 是已完成握手但未被 accept 的队列长度 s.listen(512)同时用ss -tln查看Send-Q列如果Send-Q满了说明 accept 队列溢出。应用层要确保及时调用accept()或者用多线程/异步模型处理连接。5.4 坑四Modbus TCP 读不到寄存器但连接正常现象Modbus TCP 客户端能连上 502 端口但读寄存器返回异常码或超时。原因Modbus TCP 的帧格式是 MBAP 头7 字节 PDU。常见错误是事务标识符不匹配、单元标识符填错、或者功能码和寄存器地址不匹配。比如功能码 03 读保持寄存器地址从 0 开始还是从 1 开始不同设备实现不一样。解决先用 Modbus Poll 或mbpoll工具确认设备能正常读写再对照抓包检查 MBAP 头。事务标识符每次请求要递增单元标识符对应从站地址。如果设备文档说寄存器 40001 开始实际协议地址是 0要减 1。5.5 坑五tcpdump抓不到包现象执行tcpdump后没有任何输出但网络明明是通的。原因可能是抓错了网卡、过滤表达式写错、或者权限不够。容器环境里还要注意抓的是容器网卡还是宿主机网卡。解决先用tcpdump -D列出所有可用网卡确认抓的是正确的接口。过滤表达式用host、port、net组合避免用and连接太多条件导致逻辑错误。容器里抓包用docker exec进容器执行或者抓宿主机的docker0网桥。如果还是不行检查是否有CAP_NET_RAW权限。6. 进阶技巧用 Wireshark 过滤器把排查效率提升一个量级命令行tcpdump适合快速定位但复杂分析还得靠 Wireshark。它的显示过滤器Display Filter比抓包过滤器强大得多能在已抓到的包里精确筛选。几个我常用的过滤器# 只看 TCP 三次握手和四次挥手 tcp.flags.syn 1 or tcp.flags.fin 1 # 只看重传包定位丢包问题 tcp.analysis.retransmission # 只看零窗口定位接收方处理不过来 tcp.analysis.zero_window # 按 TCP 流跟踪右键 Follow TCP Stream 更直观 tcp.stream eq 0 # 过滤 Modbus TCP端口 502 tcp.port 502 # 过滤特定 IP 的 HTTP 请求 ip.addr 192.168.1.100 and http.requesttcp.analysis.retransmission是我用得最多的一个。线上服务响应慢抓包后过滤重传如果大量重传说明网络质量差或对端接收窗口太小。tcp.analysis.zero_window则说明接收方缓冲区满了应用层读取太慢这时候调 TCP 参数没用得优化应用层消费速度。另一个技巧是用 Wireshark 的「专家信息」Analyze → Expert Information它会自动标记重传、乱序、窗口满、连接重置等异常相当于一个内置的排查助手。我一般抓包后先看专家信息有红色警告就直接跳过去看对应的包比从头翻快得多。最后说一个习惯每次排查网络问题我都会同时开三个终端——一个跑tcpdump抓包一个跑ss看连接状态一个跑应用日志。三个信息源对照基本能在几分钟内定位到是哪一层的问题。这个习惯帮我省掉了大量「重启试试」的玄学时间。希望帮到你。本文还有配套的精品资源点击获取