ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

TCP/IP四层模型详解:从链路层到应用层的排障实战

TCP/IP四层模型详解:从链路层到应用层的排障实战 TCP/IP四层模型这个名词只要跟网络打过交道应该都不会陌生。我做了好几年后端和运维相关工作说实话刚入行那会儿我也觉得分层模型就是个面试题——把四层名字背出来每层对应几个协议就算学完了。但后来在实际项目里一次次排查网络故障我才发现这个模型根本不是一张用来背诵的理论图它其实就是一张帮你定位问题、理解系统行为的地图。最近我专门抽空把这套体系重新复盘了一遍从链路层到应用层把每一层的职责边界、关键协议和容易踩的坑都重新梳理了一遍这篇文章就是这次复盘的完整记录。无论你是刚入门的学生、写业务代码的后端还是天天跟服务器打交道的运维把这四层吃透对你理解网络、排查问题都会有实实在在的帮助。1. 先理解四层模型到底解决了什么问题1.1 没有分层网络会乱成什么样很多教材一上来就列分层图却很少解释“为什么要分层”。这个问题如果没想明白后面学多少协议都容易是一堆死记硬背的碎片。假设没有分层一个写网页程序的开发者需要自己解决多少事情至少包含这些要把数据变成电信号知道网线接口怎么工作要处理路由器、交换机这些中间设备的转发规则搞清楚数据怎么从一台机器到另一台机器要保证数据在传输过程中不乱序、不丢失丢了还得自己想办法重传还要考虑对方机器上同时跑着很多程序这份数据到底该交给哪个程序。如果这些事全得由每个应用开发者自己搞定那互联网根本发展不起来。分层设计的本质就是把这些复杂问题拆成一个一个互相独立的子问题每一层只负责自己那一块并为上层提供清晰的接口。上层不需要知道下层是怎么实现的只要按接口调用就行。这个思路和写代码很像你在业务层调用一个发邮件的方法不需要关心这个方法底层是通过什么协议、走什么链路把邮件送出去。TCP/IP四层模型就是把整个网络通信固定成了这么一条标准流水线。1.2 对等通信与封装解封装理解一切网络报文的前提分层模型里有一个特别重要的概念叫对等通信。意思是从逻辑上讲每一层只跟对端的同一层“对话”。举个例子。你在浏览器里输入一个网址会生成一个HTTP请求。这个请求先被应用层封装成HTTP报文然后交给传输层传输层给报文加上TCP头变成TCP段再往下网络层加上IP头变成IP包链路层再包上以太网帧头最后变成0和1的比特流从网卡发出去。对端收到比特流后每上一层就解开一层头链路层把帧头去掉、网络层把IP头去掉、传输层把TCP头去掉最后的HTTP报文才交给对端的应用进程。这个过程就是封装和解封装。我的经验是带着这个画面去学任何协议都会很顺因为每一层协议的字段说到底都是为了满足这一层在对端对应层需要实现的功能。1.3 为什么先记四层忘掉七层这里多聊两句模型层数的问题。市面上有的教材讲OSI七层有的讲TCP/IP四层还有人讲五层。第一次接触这些概念很容易被绕晕。我的建议是学习主线放在TCP/IP四层上OSI只当背景了解一下就行。原因在于TCP/IP是事实上的网络协议标准你抓包看到的、实际跑在网线上的都是TCP/IP体系的东西。OSI里的会话层、表示层这两个概念在真实协议族中几乎没有独立实现硬去记它反而容易夹缠不清。四层模型大致对应关系是这样的TCP/IP四层对应OSI大致范围典型协议/概念应用层应用层表示层会话层HTTP、DNS、SSH、FTP传输层传输层TCP、UDP、端口网络层网络层IP、ICMP、路由链路层数据链路层物理层以太网、MAC、ARP、MTU后面所有排障、配置、调优都围绕这张表展开就够了。2. 逐层拆解链路层、网络层、传输层、应用层都有哪些关键点2.1 链路层最不起眼却经常埋雷链路层在四层模型里负责同一物理网络内相邻节点之间的数据传输。核心工作包括用MAC地址标识设备、把IP包封装成以太网帧、通过ARP协议把IP地址解析成MAC地址、以及检测帧在传输中是否损坏。这一层最大的特点是容易被忽略。很多学网络的人觉得链路层就几个概念翻过去就完了但实际故障里链路层非常容易埋雷。第一个雷是MTU。以太网帧默认的MTU是1500字节意思是这个网络接口一次最多能传输1500字节的载荷数据。如果上层的IP包超过这个数值就需要在网络层做分片或者由操作系统在发送前先把TCP段的MSS协商小一点。分片在真实网络里往往是性能杀手因为任何一个分片丢失整个IP包都无法重组接收方只能丢弃发送方还得整个重传。于是出现了最经典的现象大包不通、小包通网页偶尔能打开、偶尔打不开文件传输特别容易卡。第二个雷是ARP。ARP通过广播查询“谁的IP是这个请把你的MAC地址告诉我”。如果同一个网段里有两台设备误配了相同IPARP缓存会在这两台设备之间来回跳变表现就是主机时通时不通非常隐蔽。排查的时候可以连续ping同时反复看arp缓存表一旦发现MAC地址在跳基本就是这个原因。还有一个容易忽略的点链路层的帧是面向局域网的它不能跨路由器传递。数据每经过一台路由器帧头都会被剥离再用新帧头重新封装而IP头基本保持不变。所以抓包时在A机器上抓到的以太网帧头部和在B机器上抓到的往往不一样——这一点我在后面抓包实验中会详细验证。2.2 网络层IP寻址与“尽力而为”的转发逻辑网络层负责端到端的寻址和路由选择核心协议是IP辅助协议有ICMP。跨过这条网线、那台交换机数据最终能不能到目标网络靠的就是网络层。要理解网络层先抓两个关键词无连接、尽力而为。所谓无连接是指IP协议本身不维护连接状态。路由器拿到一个IP包只看目的IP地址查自己的路由表然后把包转发到对应的下一跳就算完事。两台主机之间哪怕有成千上万个IP包在走路由器也不需要记住它们彼此是什么关系。所谓尽力而为是指IP层不保证可靠。包可能丢、可能乱序、可能重复IP层一概不负责。设计上把可靠性交给传输层的TCP去处理。很多初学者会觉得“那IP层也太没用了吧”其实这正是模块化的好处让每一层专注解决一个问题比让某一层把所有事都包了要高效得多也灵活得多。IP寻址本身很基础但特别重要。IPv4地址是32位用点分十进制表示配合子网掩码区分网络位和主机位。判断两台主机是否在同一子网就把两个IP分别和子网掩码做按位与运算结果相同就在同一子网内。实际排障时子网配置错误导致的“跨网段访问失败”是我见过的高频问题之一每次都要反复确认掩码。ICMP协议也属于网络层。ping和traceroute都是基于ICMP的它们也是网络层最常用的排障工具。关于这两个工具的具体用法我会在后面单开一节详细讲这里先记住一点ping通了不代表应用层没问题但ping不通问题一定出在这条链路或者更底层。2.3 传输层端口、三次握手与可靠传输传输层是四层模型里最值得花时间的一层。它提供两种风格完全不同的服务TCP和UDP一个追求可靠一个追求高效。先讲端口。端口的本质是同一台主机上区分不同进程的编号传输层用它来保证数据能被正确地交给对应的应用程序。一个完整的TCP连接由四元组唯一确定源IP、源端口、目标IP、目标端口。这个四元组的概念是后面理解服务器“为什么一个端口能扛住海量连接”的关键——因为千千万万的连接里四元组各不相同。TCP的核心特性是有连接、可靠、全双工。可靠性来自一整套机制序列号与确认每个字节都有一个序列号接收方通过ACK确认自己收到了哪些字节发送方超时没收到ACK就重传。三次握手建立连接时客户端发SYN服务端回SYNACK客户端再回ACK。三次握手的背后逻辑是让双方都确认“我能发、你能收、你能发、我能收”同时还能防止历史失效的连接请求被误当成新连接。四次挥手关闭连接时因为TCP是双工的两个方向需要分别关闭所以会有FIN、ACK这样的四步交互。流量控制接收方通过窗口大小告诉发送方“我还能收多少”避免发送太快把接收方缓冲区冲爆。拥塞控制发送方根据网络状况用慢启动、拥塞避免、快速重传等算法动态调整发送速率避免把网络堵死。做后端或者运维TCP状态机一定要熟练。一个连接从创建到关闭会经过LISTEN、SYN_SENT、SYN_RCVD、ESTABLISHED、FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT、CLOSE_WAIT、LAST_ACK、CLOSED等状态。看到大量TIME_WAIT意味着主动关闭连接的一方在大量关闭短连接看到大量CLOSE_WAIT基本可以断定应用层有连接没有正确关闭绝大多数时候是代码里忘了调用close或者释放资源。UDP跟TCP走的是另一条路线。它无连接、不可靠、不重传、没有拥塞控制好处是头部小、延迟低、处理简单。像实时音视频、DNS查询、日志上报这类场景UDP往往比TCP合适。选UDP还是TCP本质上是一个“可靠性、延迟、吞吐、实现成本”的权衡题没有绝对的优劣。2.4 应用层协议千变万化底层只有一套应用层离用户最近协议也最丰富。HTTP、HTTPS、DNS、SSH、FTP、SMTP、NTP、WebSocket统统都长在这一层。应用层协议有自己的文本或二进制格式但它们几乎都跑在TCP或者UDP之上。应用层不用关心底层包怎么封装、怎么路由、怎么保证可靠——这些基础能力下面三层都已经准备好了。这也正好呼应了分层模型的思想应用只管定义业务语义网络传输的脏活累活交给下层。应用层里最容易出问题的协议是DNS。很多“网页打不开”的案例根因不在网络不通而是域名解析失败、解析超时、或者解析到了错误的IP。排查时我一般按顺序看下面几项/etc/resolv.conf 里配置的DNS服务器地址对不对用dig或者nslookup手动解析域名看返回结果和耗时检查本机hosts文件有没有被改污染。HTTP这一层也有不少细节。除开常见的状态码、请求方法更值得关注的是耗时分布。用curl -v请求一个接口可以看到整个流程的时间消耗DNS解析、TCP连接、TLS握手、服务端处理、响应传输各花了多少。哪个阶段耗时异常就说明问题出在哪个环节这也是“分层排障”思路在应用层的体现。3. 学习四层模型最实用的3个工具和一套完整实验3.1 Wireshark一次抓包胜过背十遍协议我在学习四层模型时最深的体会是任何协议光看文字描述特别容易忘但只要用Wireshark抓一次包亲眼看到报文的形状基本就刻在脑子里了。拿TCP三次握手来说。起一个本地Nginx服务用Wireshark在回环接口上抓包过滤条件写tcp.port 80然后浏览器访问一次。你会看到三个报文排在那里第一个包客户端发SYN标志位是0x002第二个包服务端回SYNACK标志位是0x012第三个包客户端发ACK标志位是0x010。每个包里还能看到序列号、确认号、窗口大小、MSS、时间戳这些字段。亲手看过一遍三次握手的来龙去脉再也不用背了闭着眼都能画出来。Wireshark的过滤器还是值得花一点时间学的新手先掌握这几个常用写法就够用了ip.addr 192.168.1.1 tcp.flags.syn 1 tcp.flags.ack 0 tcp.port 443 dns http.request抓包时有个点要注意如果需要分析HTTP和TCP层的内容最好在客户端或者服务端本地抓或者用交换机镜像口抓同网段的包跨多个路由器抓包链路层的帧头早就被重写过了你看到的以太网头并不代表原始情况。3.2 用ping的MTU参数实测链路层边界MTU是链路层的一个硬指标但它对网络层和传输层的影响非常大。想验证一条路径的MTU最直接的办法就是用ping命令设置DF标志然后逐步调整包大小。Linux下的用法ping -M do -s 1472 -c 3 目标IP ping -M do -s 1400 -c 3 目标IP-M do的意思是设置DF位禁止中间设备对包做分片-s指定ICMP数据部分的大小。为什么从1472开始试因为1472加上28字节20字节IP头8字节ICMP头刚好等于1500即最常见的MTU值。如果目标路径上的MTU就是1500-s 1472能通再往上调比如-s 1500能通-s 1501就会报错“Frag needed and DF set”。这个报错说明包太大了路径上有某一段的MTU不够大。通过不断调整包长找到能通的最大数据长度再加28就得到了这条路径的实际MTU。Windows下命令是ping -f -l 1472 目标IP-f表示不分片-l指定缓冲区大小意思跟Linux的-M do -s一样。这个测试我建议每个搞后端的人都做一次尤其当你经常处理容器网络、隧道封装、云上负载均衡这些场景时。理解MTU之后很多“微信能用但网页打不开”“视频卡顿但文字消息正常”的诡异问题往往就有了解释。3.3 traceroute和mtr看清网络层的每一跳网络层排障最常用的两个工具是traceroute和mtr。traceroute的原理很巧妙。它利用IP头部里的TTL生存时间字段每经过一台路由器TTL减1减到0时路由器会丢弃这个包并向源地址发回一个ICMP超时消息。于是traceroute先发一个TTL1的包第一跳路由器会回超时消息这就知道了第一跳是谁再发TTL2的包知道第二跳以此类推就把从本机到目标的整条路径摸出来了。实际使用中我推荐用TCP模式的探测因为不少网络设备默认不回应普通的ICMP探测导致显示星号。Linux下可以这样traceroute -n --tcp -p 443 目标IP mtr -n 目标IP-n表示不做反向域名解析避免每次查询DNS拖慢速度。mtr相当于“持续版的traceroute”能统计每一跳的丢包率和延迟变化跨网络访问变慢时用mtr一眼就能看到瓶颈在哪一跳。不过要注意中间某一跳丢包率高不代表整条路径就有问题。很多路由器出于性能考虑故意不回应探测包或者只做十分有限的回应。判断标准要看最后一跳也就是目标主机的丢包率——只要最终目标正常中间部分跳数的丢包可以暂时不用理会。3.4 最小实验亲手打通一次HTTP请求的全过程如果你有时间我强烈建议做一个最小实验亲手把四层模型跑一遍。只需要两台虚拟机或者一台机器加一个容器。实验目标从A机器访问B机器上的Nginx服务同时在A机器抓包用Wireshark观察整个过程中出现的每一层报文。步骤如下在B机器安装并启动Nginx默认监听80端口在A机器执行抓包tcpdump -i eth0 -nn host 192.168.1.2 and port 80 -w http.pcap在A机器发起请求curl -v http://192.168.1.2/停止抓包用Wireshark打开http.pcap这个文件。你会看到几组报文依次出现先是ARP广播询问目标IP的MAC地址接下来TCP三次握手然后才是HTTP的Request和Response最后是四次挥手。把报文和四层模型一一对应起来你会非常有画面感。这个实验我让团队里的新人都做过一遍反馈都说比看十篇文章都有用。4. 四个高频坑从OSI误区到TIME_WAIT该不该调4.1 OSI七层与TCP/IP四层先别把两套体系搞混我见过太多人在这里栽跟头。先是背了个OSI七层背得滚瓜烂熟然后学TCP/IP四层两套名字一多全乱了。这里其实不必紧张。TCP/IP四层与OSI七层本来就是不同视角的抽象TCP/IP是“实际协议体系”OSI是“理论参考模型”。学习主线应该放在TCP/IP上OSI只当作背景知识重点理解它跟TCP/IP之间的映射关系。会话层、表示层这两个概念在TCP/IP体系里没有独立实现它们的职责被并进了应用层所以在排障时它们并不存在想多了反而干扰。4.2 “三次握手”背后的两个关键问题不少人对三次握手的理解就停在“客户端发一次、服务端回一次、客户端再回一次”这个表象上觉得这有什么好学的。但面试时或者排障时问题往往更深一层为什么一定是三次至少有两个关键答案。第一个双方需要互相确认收发能力正常。第一次握手后服务端知道“客户端能发”第二次握手后客户端知道“服务端能收能发”第三次握手后服务端才知道“客户端能收”。只有经历完三轮双方才都确认自己和对端收发都没问题。第二个防止历史失效的连接请求。假设客户端曾经发过一个SYN因为网络问题迟到很久才到达服务端。如果没有第三次握手服务端一收SYN就建立连接、分配资源这个已经失效的连接就会白白占用服务端资源。有了第三次握手客户端发现这个连接其实是自己早就放弃的旧请求可以回复一个RST服务端看到RST就不会建立连接了。理解了这两点三次握手就不再是一条需要背的规则而是一套必然的推理。4.3 TIME_WAIT太多调参数不是唯一答案TIME_WAIT是TCP连接关闭过程中主动关闭方会进入的状态持续时间为2MSL。设计它的目的是确保最后一个ACK一定能到达对端避免对端重发FIN时本机已经关闭导致连接错误关闭。在高并发的短连接场景下TIME_WAIT堆积非常常见。很多新手第一反应是调小等待时间或者疯狂开端口复用。但我自己踩过的坑是调参数很可能掩盖真正的问题。曾经有个内部服务大量短连接导致TIME_WAIT堆到几万偶发“Connection reset by peer”报错。我一开始也去调内核参数结果治标不治本。后来抓包一看其实是对端收到重复请求后按协议回了RST应用层却没有正确处理才导致异常。真正解决问题的是改成连接池复用长连接短连接数量降下来TIME_WAIT自然消失。所以看到TIME_WAIT多先判断这是不是业务的正常形态。如果是高吞吐的短连接业务优先优化业务层让连接复用只有确认是内核参数问题再去动系统配置。4.4 端口不是协议排查时别被默认端口带偏还有一个挺常见的坑是看到端口号就默认协议。80就认为是HTTP443就认为是HTTPS22就认为是SSH——大多数时候没错但做排障的人不能这么想。端口的本质是一个服务标识它跟跑在上面的应用协议没有必然绑定。同一个端口完全可能跑不同的协议不同的服务也可以用同一个端口。遇到端口相关的问题正确做法是先确认端口上跑的到底是什么应用用nc或者nmap探测一下nc -vz IP 端口 nmap -sV IP -p 端口我遇到过一次8080端口上跑的是数据库而不是Web服务导致排查方向完全跑偏的例子。从那以后每次接手一个“端口不通”的问题我都会先确认端口背后的真实服务而不是想当然。5. 真实故障复盘网页打不开我如何按层定位根因5.1 故障现象请求超时一切指标却正常有一次线上服务出现大面积请求超时监控面板上CPU、内存、磁盘、数据库都正常应用日志没有明显报错只有大量调用方反馈“请求超过3秒没响应”。团队里几个开发上来就开始查代码翻数据库慢查询折腾了快一天没有任何线索。这时候我们回到网络模型用四层分层思维重新排。5.2 按层排查从应用层一路查到链路层排查的第一步先确认应用层。用curl设置连接和超时时间模拟一次最朴素的HTTP请求curl -v --connect-timeout 3 --max-time 10 http://服务IP:8080/health结果一直卡在连接建立阶段根本没有发出HTTP请求可见问题不在业务代码而是连接建立就有问题。第二步看传输层。确认服务端口是否在监听ss -tnl | grep 8080端口确实在监听服务进程没挂。但用nc去连端口明显比正常情况慢TCP握手很难完成。这就把问题锁定在了传输层以下的某个环节。第三步看网络层。ping目标IP是通的但延迟抖动特别大丢包率在路径中段很高。再用TCP模式做traceroutemtr -n --tcp -p 8080 服务IP看到路径中间某一段持续高丢包但最终跳还能通判断可能是大包在路径上的某个中间设备被限制了。第四步回到链路层验证MTU。用ping -M do -s 1472去测结果直接返回“Frag needed and DF set”。把包长降到1400通了。这下基本确定目标路径上某个设备使用了比1500更小的MTU大包无法通过。5.3 根因复盘一套完整的四层思维示范根因很快就清楚了服务端返回的业务响应包比较大而客户端与服务端之间的一段网络经过了一种隧道封装该隧道的MTU小于1500。TCP在握手时协商的MSS基于1500算出来发出去的包一旦超过路径MTU且被设置了DF位后无法分片就会被中间设备直接丢弃。小包SYN、ACK都不受影响所以看起来“端口通、ping通”但真正的大包HTTP响应过不去于是表现为请求超时。解决办法是在网络设备上启用TCP MSS clamping强制把TCP握手包里的MSS改小或者直接调整服务端所在网卡的MTU让TCP重新协商出更匹配的MSS。这次故障最让我印象深刻的不是技术细节而是排查思路。我们用四层模型把问题范围从“代码层”一路缩小到“某个网络设备的MTU配置”每一层都有明确的工具和判断标准。没有这个框架大概率还是回到代码里去瞎猜查一天都不一定有结果。6. 从“会背”到“会用”我的四层模型学习路径建议6.1 先搭地图再抠细节学习四层模型最容易犯的错误是过早陷入细节。我第一次学TCP的时候花了一整周去背TCP头部每个字段学完几乎全忘了。后来复盘发现顺序反了。正确的做法是先花很少的时间把“数据从一端到另一端经过哪些层、每一层大致做什么”理解清楚脑子里有一张整体地图。然后再去研究每个协议而且每个协议都要遵循“先框架、后细节”的顺序。比如TCP先把三次握手、四次挥手、可靠传输、流量控制这几个核心机制想明白再看头部字段时你会惊喜地发现每个字段都能跟机制对应上根本不用硬背。6.2 把知识点写成一个一个故障故事这是我个人觉得最有效的学习法。每排一次网络故障我都把过程整理成一条笔记包含四个部分现象用户或者监控报了什么错分层定位每一层分别做了哪些检查、结果如何根因最后是哪一层、哪个参数、哪台设备出了问题收获这条经验对应了教材里哪个知识点。这个习惯坚持一段时间后你再看四层模型就不再是抽象的图了每一层都挂着你亲手经历过的真实案例。知识一旦跟场景绑定想忘都难。6.3 推荐资源与下一步方向书的话推荐两本。入门看《图解TCP/IP》图示多语言轻松看完对整体模型有个清晰的印象进阶啃《TCP/IP详解 卷1协议》经典必读但建议等有了一定基础再回来看否则会被细节劝退。工具方面把日常命令用熟就够tcpdump、Wireshark、ping、traceroute、mtr、nc、nmap、ss、curl。这些命令本身就是你验证四层模型的最佳教具。四层模型吃透之后有两个方向值得继续延伸一个是网络与安全方向涉及访问控制、NAT、防火墙策略、会话追踪这些全都建立在分层模型之上另一个是性能优化方向比如TCP内核参数调优、TLS握手优化、HTTP协议升级、负载均衡设计。无论选哪一条地基都是你手里的这套四层模型。最后分享一点我自己的体会。我用了很长时间才真正意识到TCP/IP四层模型不是一门只能用来应付面试的理论课而是一套给复杂系统做“分层定位”的思维方式。遇到网络问题我会先强迫自己冷静下来问一句这个问题现在发生在第几层只要这个定位足够准确排查就已经成功了一大半。如果你也正在学这块我建议别只捧着书本背多抓几次包、多排几次障等你亲手验证过封装和解封装的每一步这套模型就会变成你自己的思维工具。
返回列表