
1. 一次完整的数据通信到底发生了什么1.1 从浏览器地址栏说起先别急着背OSI七层模型。把电脑打开随便访问一个网站在你按下回车的那一瞬间直到页面内容展示在你屏幕上这中间经历的事情就是数据通信最完整的缩影。我第一次带团队做网络故障排查的时候有个新人跟我说数据不就从A传到B吗有什么好研究的。后来我们花了两个通宵查一个时通时不通的问题最后定位到是MTU分片策略和防火墙状态的组合问题。从那以后我就明白数据通信表面看是一条管道实际上是一整套精密的分工协议。一次请求五分钟看完但背后的每一跳、每一层封装、每一段超时重传都藏着可以写几千字的门道。这篇文章我想把数据通信过程这件事拆开揉碎了讲。不堆概念不背书就顺着一次真实访问逐步拆解数据从产生、打包、上路、寻址、到达、拆包、交付的全流程。可以说把这一条链路吃透了网络基础基本就过关了后面不管是做开发、运维、物联网还是嵌入式遇到通信问题你至少知道该往哪一层去找原因。1.2 用快递寄送来理解通信模型数据通信经常被人为搞得很玄乎其实就是寄快递。你写好一封信应用层数据要寄给远方的朋友。信不能光秃秃出门得装进信封写上收件人地址传输层端口塞进快递袋贴上运单网络层IP地址再把快递袋交给快递站点站点按片区装车数据链路层MAC地址和帧最后货车沿着公路把包裹运到目的城市物理层传输介质。中途包裹经过分拣中心分拣员只看快递单上的城市不看信里写了什么路由器只查IP到了目的城市快递员按门牌号敲门端口映射到进程朋友拆开快递袋、信封才看到你写的正文。这套流程里最反直觉的一件事是数据通信每一层都只关心自己该关心的事。上一层不关心下一层怎么做下一层也不关心上一层说了什么。这种分层各干各的设计是整个互联网能兼容全世界几十亿设备的前提也是你可以把一台电脑拔了网线换成Wi-Fi而不用改任何上层应用的原因。2. 发送端数据是如何一层层打包出去的2.1 应用层的第一次加工假设你在浏览器地址栏输入了https://example.com回车。浏览器做的第一件事不是发请求而是把普通人能读懂的域名翻译成IP地址这一步叫DNS解析。你的电脑会向配置好的DNS服务器发一条UDP查询报文问example.com的IP是多少DNS服务器回一个A记录应答比如93.184.216.34。拿到IP以后浏览器开始组装HTTP请求。这个请求长这样GET /index.html HTTP/1.1 Host: example.com User-Agent: Mozilla/5.0 ... Accept: text/html,application/json这里要特别提一下应用层不负责怎么把数据送到对面它只负责我到底要表达什么。HTTP协议规定了请求的格式、方法GET、POST、状态码200、404、头部字段等等这些都是语义层面的约定。真正干传输脏活累活的是下一层。在生产环境排查问题的时候我经常先抓应用层看请求是否正常再看是不是下层问题。很多新手一上来就抓TCP一顿操作猛如虎最后发现是应用自己发了个残缺请求白费劲。1.2 传输层给数据加上分拣标签应用层数据准备好以后传给传输层。大多数应用用的是TCP少数实时性要求高的比如DNS、视频通话用UDP。TCP是三段式先建立连接、再传数据、最后关连接。建立连接的那个过程叫三次握手就是双方互相确认我能听到你你能听到我。来个简化版的理解方式第一次握手客户端发SYN告诉服务器我想跟你建立连接并带上一个初始序列号比如1000。第二次握手服务器回SYNACK说收到你的请求我也准备好了同时带上自己的初始序列号比如5000并把客户端序列号1也就是确认号1001表示我期待你下一个报文从1001开始。第三次握手客户端回ACK确认号5001说我也收到你的准备了咱们开始传吧。为什么要三次不是两次因为网络是不可靠的客户端发的连接请求可能超时重传服务器如果收到一个迟到的旧请求就直接建立连接资源就浪费了。三次握手能让双方都确认我的发送没问题你的接收也没问题这是防止历史重复连接初始化干扰的唯一可靠办法。TCP把应用层下来的数据切成合适大小的块每一块前面加上TCP头。TCP头里最关键的信息有两个源端口和目标端口。目标端口用来说这个包要交给服务器上的哪个进程比如80是HTTP443是HTTPS3306是MySQL。源端口是客户端随机挑的一个高位端口用来让服务器回包时能找到对应的进程。端口这个设计我多说一句。一台服务器上同时跑着Web服务、数据库、SSH数据到达服务器时IP地址是一样光靠IP根本分不清这个包是给谁。端口就是大楼里的门牌号。没有端口你发出去的数据到了服务器门口就迷路了。这一层还有一个数字值得记MSS最大分段大小默认是1460字节这是MTU 1500减去TCP头20字节再减去IP头20字节得到的。TCP要按MSS把数据切成段切完的每一段才能塞进链路层帧里不然就会触发IP分片后面性能会非常难看。这些细节等到你排查大包不通小包通的问题时就全是命根子。1.3 网络层贴上目的地门牌TCP段封装完成传给网络层。网络层把TCP段当成自己的载荷前面再套一个IP头形成一个IP数据报。IP头里最重要的就是源IP地址和目标IP地址。很多人以为这个打包过程是层层穿越、每层都修改一下数据其实不是。正确的理解是套娃应用层数据外面包TCP头再外面包IP头再外面包以太网头。每一层只操作自己该管的那部分头部载荷内容原封不动往下一层递。如果你用Wireshark抓过包看到的每一帧就是这样一个多层结构。但要注意在发送端真正出手之前还差最后一步这数据还没到线上要交给网卡。要发给谁光知道目标IP还不行还得知道目标设备的MAC地址。IP地址是逻辑寻址MAC地址才是物理实体的身份证。2. 从网卡到网线数据真正离开电脑的那一刻2.1 ARP拿到对方的MAC地址假设你的电脑和服务器不在同一个子网数据要先发给网关。网关是连接你所在局域网和外部网络的那台路由器。你的电脑配置里都有一个默认网关IP比如192.168.1.1。当你发数据给一个不跟自己同网段的IP时数据链路层会把目标MAC填成网关的MAC而不是服务器的MAC。这挺反直觉的但非常重要。那怎么拿到网关MAC靠ARP协议。你的电脑在局域网里广播一条消息谁是192.168.1.1你的MAC是多少 网关收到后单播回复我是网关我的MAC是xx:xx:xx:xx:xx:xx。你的电脑把这个映射记到ARP缓存表里下次直接用。提示观察ARP缓存可以用arp -a命令。如果你怀疑局域网内IP冲突或者网关被人冒用第一步就是看ARP表里网关IP对应哪个MAC再和设备实物核对。2.2 帧的封装与冲突域MAC地址拿到以后数据链路层在IP数据报外面再包一个以太网帧头帧头里有源MAC、目标MAC还带一个类型字段标明上一层是IPv4还是ARP。帧尾还有FCS帧校验序列用来检查数据在传输过程中有没有被破坏。以太网帧的标准负载区是1500字节多出来的数据要么由TCP分段提前切好要么触发IP分片。这也是我之前说的MTU问题的根源。两条链路之间MTU不一致大包过去被中间路由器丢弃又没开启PMTUD就会出现特定网站打不开偶尔能开这种邪门故障。把帧通过网线发出去的活归物理层。网卡把帧里的每个比特转换成电信号用双绞线传到交换机。到这里数据才真正离开了你的电脑。2.3 交换机记住端口不迷路交换机工作在数据链路层本身看不懂IP只认MAC和端口。交换机内有一张MAC地址表把MAC地址和端口号绑在一起谁从哪个口进来下一条发往哪查表决定。第一次通信交换机不知道目标MAC在哪个端口就会把帧广播到除了入端口之外的所有口目标设备回应后交换机记录这个口的MAC地址以后就精确转发。这个过程叫MAC地址学习。所谓冲突域也是这个概念一个广播域里同时刻只允许一台设备发数据否则会冲突所以早期网络里人多一卡、抓包全是冲突碎片。现在的交换网络已经用全双工解决了这层但理解帧转发逻辑依然是排障基本功。从电脑到交换机的这一段属于局域网内部的事。局域网内部能互通靠MAC跨网段就必须交给路由器。3. 路由器数据在广域网上的接力赛3.1 路由表每个路口都有导航路由器收到你电脑发出的帧剥掉链路层头露出IP数据报看一眼目标IP地址然后查自己的路由表。路由表不是一张无边无际的全世界地图而是下一步该往哪走的路口指示牌。表中每一条记录包含目标网段、下一跳地址、出接口、度量值比如跳数路由器只负责把数据交给下一跳再由下一跳继续接力最终送到目的地。最常见的路由来源有三种直连路由路由器自己接口所在的网段天生就知道。静态路由管理员手动配置适合拓扑固定的小网络。动态路由由OSPF、BGP等协议自动学习和交换路由信息适合大型复杂网络。在Linux上你可以用ip route查看本机路由表在Windows是route print在华为设备是display ip routing-table。排除能ping通网关却上不了网的时候第一件事就是看路由表里有没有默认路由。3.2 TTL与数据包的有限寿命IP头里有一个字段叫TTL全称Time To Live每经过一台路由器就减1。TTL到0的时候路由器会把这个包丢弃并回一条ICMP超时消息给源地址。这么做是防止数据包在网络里死循环——万一某两台路由器的路由表互相指对方数据包的寿命得有个上限否则就成永动机了。不同操作系统初始TTL不同Windows默认128Linux默认64。你ping一台机器看返回TTL值就能大致猜出对面是什么系统比如ping出来TTL是55说明初始值64经过了9跳ping出来59可能是128初始值经过了69跳。这个技巧在网络拓扑不明时非常实用。3.3 NAT换门牌但不是坏事数据包到达你家的出口路由器时你写的源IP是内网私有地址比如192.168.1.100这种地址在公网上根本不通因为全球有无数台设备都在用192.168.x.x。出口路由器要做一件事把源IP换成自己公网口的IP同时记录下这个连接来自内网哪台设备的哪个端口数据回来时再反向翻译回去。这个技术在行业内叫NAT网络地址转换。NAT在解决IPv4地址短缺问题上立了大功但也带来一些麻烦内网设备无法主动被外部连接因为公网上没有它的真实IP。某些协议比如FTP主动模式、IPSec携带了IP地址信息NAT改完包就乱套了需要配合ALG或者换用其他协议模式。排查NAT相关问题特别依赖连接追踪表设备上一般用display nat session或conntrack -L。我踩过的一个经典坑是公司内网访问外部FTP服务器很慢抓包发现FTP数据连接一直建立不起来。后来查NAT会话表发现是FTP主动模式要求外网主动连接内网NAT设备转发不了。解决方案是改成被动模式或者设备上开启FTP ALG。这种问题不深入理解数据通信链路光靠猜很难定位。4. 接收端逆向拆包从比特流到页面渲染4.1 服务器的接包与逐层解封装数据经过广域网上的多跳接力终于到达目标服务器所在的机房。它要从公网路由一层层进到服务器网卡这就是接收端的解封装流程和发送端完全相反物理层收到电/光信号还原成比特流。数据链路层识别帧头帧尾检查FCS校验丢弃损坏帧。网络层查看目标IP是不是本机不是就转发是本机就剥掉IP头把TCP段交给TCP协议栈。TCP协议栈检查端口号找到对应进程把数据放到接收缓冲区等到数据完整再交给应用层。应用层收到完整HTTP请求处理完业务逻辑把响应发回。这里有件很多人忽略的事一片数据到达时可不是按发送顺序整整齐齐排队。网络可能先到了序号300的段后到了序号100的段。TCP有乱序重排的机制接收方先收着不交付等缺口补齐、顺序正确了再交给内核的socket缓冲区而应用层感知不到这些它读到的始终是一个有序的字节流。TCP还有流控策略叫滑动窗口。接收方会告诉发送方我的接收窗口现在还有多大发送方据此调整发送速度这就是流量控制。另外TCP的拥塞控制机制很多样比如有的用慢启动、有的用快速重传。但基础的思路都是先小批量探路没丢包就翻倍增速丢包就退避。这些机制协同作用才让网络在不确定的信道上还能尽量高效、可靠地传输。4.2 端口争抢与并发处理的真相大家可能会好奇一台服务器同一时刻可能同时有几万几十万请求进来怎么分得清哪个数据属于哪个连接答案还是端口和五元组源IP、源端口、目标IP、目标端口、协议。TCP协议栈维护一张连接表五元组唯一标识一条连接。你访问同一台服务器从你电脑随机生成了不同的源端口服务端就能把它们区分成两条连接。服务器端的80端口不断有数据进来内核按五元组哈希查找再把数据从正确的socket送进对应的进程。服务端接收大量并发连接时光靠多进程多线程是扛不住的所以现在流行的Nginx、Node.js都是事件驱动加epoll模型让一个线程同时管理成千上万个连接。你用netstat看连接状态时会看到大量TIME_WAIT、ESTABLISHED状态这里每一个状态背后对应的都是TCP状态机的一次迁移。5. 实战视角通信过程里的隐患与排查手段5.1 抓包看数据一眼分辨健康不健康写了这么多理论最后说点实用的。遇到通信问题我最先做的一定是抓包在发送端用Wireshark或在服务器上用tcpdump把通信过程中的分组原原本本记录下来。看抓包时我一般关注几个点有没有TCP重传重传比例超过一定范围就要警惕线路质量。三次握手是不是每次都成功Syn重传多不多。有没有RST包突然把连接切掉一般应用层报错前先来一个RST多半是有人主动拒绝。响应时间分布从抓到HTTP请求包到抓到第一个返回包用了多久这段延迟是服务端处理时间加上网络RTT能帮你快速区分是网络慢还是服务慢。给你一个亲测有效的排查套路客户端和服务器同时在两台机器抓包对时间看数据从客户端发出到服务器收到花了多少毫秒再看服务器回包到客户端收到花了多少毫秒。这个双向时间差能非常直观地把故障范围切到A到B链路还是B到A链路。我们之前排查过好几个服务器很慢的投诉一抓包发现时间全花在下行链路的丢包重传上根本不是业务代码的问题。5.2 高延迟、丢包和时通时不通三种经典症状网络故障的症状通常就那几类但每类背后的原因都不同高延迟但没丢包大概率是链路拥塞、跨运营商绕路或者中间设备处理能力到了瓶颈。可以先检查路由路径再逐跳看时延分布。有少量丢包重点怀疑无线干扰、网线质量问题、交换机端口错误或MTU设置不当。一个非常常见的原因是巨帧MTU 9000只配置在了服务器上而交换机或中间链路不支持这么大的帧造成大包全部丢弃小包正常。时通时不通多半和连接状态有关。比如NAT会话表老化时间太短长连接被中途杀掉或者某台设备上有基于时间/会话数的策略达到阈值就拒绝新连接。注意排查时通时不通千万别只看应用层报错。先在客户端ping和telnet ip 端口分别做几百次长测试确认三层连通和端口连通性是否稳定再逐步缩小范围。5.3 新手必踩的三个通信坑最后分享几个我做网络支持这些年最常见的新手错误第一只ping IP不通IP不测端口。在很多环境里ICMP是被人为禁掉的ping不通不代表网络不通但telnet测端口成功经常能证明服务是通的。别因为一个ping结果就断言网络不通。第二忘了清楚本机ARP和路由缓存。你改了IP、换了网关后旧缓存可能导致你一段时间内还在往旧地址发数据表现跟网络故障一模一样。实际操作里遇到改了配置还是不通先刷新缓存可能就恢复了。第三忽略MTU。平时网络速度挺正常但一到传大文件就卡死、或者某些网站打不开检查MTU几乎都能找到答案。最简单的方法是两端把MTU统一设成1500别轻易开巨帧除非你确定整条链路都支持。数据通信看起来很庞大但拆到底就是封装、接力、解封装六个字。理解了这一条主链路再看任何高大上的网络名词都不会慌——它们不过是这条链路上某一环的技术细节。希望这篇长文能帮你把那层纱彻底揭开。