ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

TCP/IP协议栈详解:从数据传输入门到网络排查实战

TCP/IP协议栈详解:从数据传输入门到网络排查实战 写网络相关的文章我一般都不太爱用“一文读懂”这种标题因为网络通信这个领域真要往深里钻一本书都未必够用。但这篇我破个例因为TCP/IP协议栈恰恰是那种“看起来很难拆开其实就那么几层”的东西。我跟不少刚入行的同事聊过发现很多人写了几年业务代码一提到TCP/IP就只知道“三次握手”再往下问“那数据发出去之后经过了哪些层”“为什么FTP传文件不会乱序”“为什么连不上数据库”就答不上来了。其实这不是你笨是市面上的资料要么太学术一上来就是七层模型、RFC编号要么太零碎今天讲TCP重传明天讲IP分片串不成一条线。这篇文章我想换个讲法不背概念不贴RFC直接从“数据到底怎么从一台机器到另一台机器”这条主线出发把TCP/IP协议栈里每一层在干什么、为什么要这么设计、出了问题怎么排查一步一步讲透。零基础可以看有基础但体系混乱的也可以看看完你至少能对着抓包工具说清楚每个包是谁发的、发给谁、干了什么。1. 协议栈到底是个什么东西一个分工明确的分层体系1.1 协议是约定栈是层次先说“协议”这个词。你平时跟人约饭会说“周六晚上六点某某餐厅门口见”——时间、地点、事件这就是一种约定。网络通信也一样两台设备要交换数据得先约定数据用什么格式、怎么开始、怎么结束、出错怎么办。这些约定就叫协议。那“栈”又是什么栈就是堆叠。TCP/IP协议栈字面意思就是一堆网络协议按层次叠在一起。为什么要叠着放因为网络通信这件事太复杂了复杂到没法让一个人或者一个模块从头管到尾。你发一条微信消息既要考虑对方手机号编址、要经过多少个路由器路由、中间丢了包怎么办可靠传输、消息在屏幕上显示成什么样应用格式——如果这些全塞进一个协议里这个协议会复杂到没法维护。所以人们把通信过程拆成若干层每一层只负责自己那一摊子事上层依赖下层下层对上层提供服务。这一整套东西就是协议栈。1.2 为什么要分层把复杂问题拆小才是解决问题的正确姿势分层的好处你把它类比成一家快递公司就清楚了你寄快递时只需要填好面单、把东西打包好不用关心包裹是坐飞机还是坐卡车那是运输部门的事。运输部门只需要按地址把包裹从一个分拣中心送到下一个不用关心包裹里面是文件还是手机。分拣中心只需要按面单上的城市代码分拣不用关心面单具体是哪台打印机打的。每一层都在为上一层“干活”每一层又不需要知道上一层和下一层的全部细节。这样最大的好处是某一层可以独立升级。比如你家里宽带从百兆换成千兆物理链路变了但你的电脑、你的微信、你的路由器配置都不用变因为链路层以下的变化被“吸收”掉了。那分层有没有代价有。每一层都要加一些自己的“管理信息”也就是头部这会让实际传输的数据多出一些开销。但这点开销跟“能让全世界几十亿台设备互通”这个收益比起来完全可以忽略。这就是TCP/IP设计者当年最正确的决定——用一点点冗余换来巨大的灵活性和可维护性。1.3 OSI七层和TCP/IP四层到底该学哪个你如果去查资料一定会看到两个概念OSI七层模型和TCP/IP四层模型。很多人在这里就被绕晕了其实没那么复杂。OSI七层模型是国际标准化组织提的一个理论参考框架物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。它很完整也很学院派但它从来没有成为互联网真正运行的蓝本。真正在跑的是TCP/IP四层模型链路层也叫网络接口层负责在同一个物理网络里传数据比如以太网、Wi-Fi。网络层负责跨网络寻址和路由核心协议是IP。传输层负责端到端的通信核心协议是TCP和UDP。应用层负责具体的业务数据格式比如HTTP、DNS、FTP。四层模型就是把七层里“会话层”和“表示层”的活儿合并进了“应用层”把“物理层”和“数据链路层”合并进了“链路层”。学的时候以四层模型为准七层了解一下就行。顺便说一句你可能会听到“蓝牙协议栈”“Modbus协议栈”“Wi-Fi协议栈”“物联网协议栈”这些词。它们跟TCP/IP协议栈一样都是“一堆协议按层次组织起来”的思路只不过面向的场景不同蓝牙面向短距离无线Modbus面向工业现场总线物联网里的MQTT/CoAP则是在TCP/IP之上针对低功耗弱网环境做了裁剪。先把TCP/IP这套基础吃透再看那些会轻松很多。1.4 封装与解封装数据是怎么“穿衣服”和“脱衣服”的分层之后数据在发送端要一层一层“穿衣服”在接收端要一层一层“脱衣服”。这个过程叫封装和解封装它贯穿了整个TCP/IP协议栈。我举个例子。你在浏览器里输入一个网址并回车要发送的数据从应用层产生应用层把“给我首页”这句话按HTTP协议格式整理成一个HTTP请求。传输层给这段数据前面加一个TCP头指定源端口和目标端口然后把这个整体交给网络层。网络层再在前面加一个IP头指定源IP地址和目标IP地址然后交给链路层。链路层又加一个MAC帧头和帧尾指定源MAC地址和目标MAC地址最后转成比特流通过网线或无线电发出去。接收端的过程刚好反过来链路层先去掉MAC头看到里面是IP包网络层去掉IP头看到是TCP段传输层去掉TCP头看到原始HTTP请求最后应用层才把这段数据交给浏览器解析。这个过程用一张表看就是层级发送端动作接收端动作数据形态应用层生成业务数据解析业务数据HTTP报文传输层加TCP头去TCP头TCP段网络层加IP头去IP头IP包链路层加MAC头去MAC头以太网帧很多初学者搞不清楚“帧”“包”“段”有什么区别其实就是因为在不同层叫了不同名字。记住这句话叫法跟着层走内容始终是那一份数据加上各层的“管理信息”。后面你抓包看到一长串十六进制能认出来“这是IP头”“这是TCP头”这一关就算过了。2. 四层模型逐层拆解每一层到底在干嘛2.1 链路层局域网里的门牌号和广播链路层是整个协议栈里最容易被忽略却又最接地气的一层。它解决的核心问题是在同一根网线上或者同一个Wi-Fi环境里两个设备怎么把数据直接送到对方手里。这里的“地址”就是MAC地址。你可以把MAC地址理解成设备的物理门牌号它在出厂时就烧录在网卡里正常情况下全球唯一。但MAC地址有个特点它只在局域网内有效。就像门牌号只能在同一栋楼里定位出了小区就没用了。那一个设备怎么知道目标设备的MAC地址这里就要提到ARP协议地址解析协议。当你的电脑要给同网段的另一台设备发数据只知道对方IP、不知道对方MAC时就会在局域网里发一个ARP广播“谁的IP是192.168.1.10请把你的MAC地址告诉我。”目标设备收到后单播回复“我是192.168.1.10我的MAC是xx:xx:xx:xx:xx:xx。”你的电脑把这个映射记到ARP缓存表里下次直接用就不用再广播了。这里有个常见的性能坑如果局域网里设备很多ARP广播会很频繁所以交换机一般会做MAC学习、路由器会做ARP代理来减少广播。而像Wi-Fi这类无线环境链路层还额外处理了信号冲突、加密、漫游等问题——你在看Wi-Fi协议栈链路层时其实就是在看802.11协议族的这些机制。链路层的核心数据结构是“帧”里面包含目标MAC、源MAC、上层协议类型比如0x0800表示IP包以及真正的数据。交换机就是靠MAC地址表来转发帧的它不关心IP地址所以它工作在链路层。2.2 网络层IP地址、路由与快递分拣链路层只管一个局域网但互联网显然不止一个局域网。网络层解决的就是“跨多个网络寻址”的问题核心协议是IP协议。IP地址就是设备在网络世界里的“逻辑地址”它分两部分网络号和主机号。通过子网掩码你可以判断哪些设备跟你在同一个网段、哪些不在。比如你的IP是192.168.1.5子网掩码是255.255.255.0那192.168.1.x这个网段里的设备都在同一个局域网直接走链路层通信如果目标IP是公网地址那就必须把数据交给路由器转发——也就是“网关”。路由器做的事本质上就是快递分拣。每台路由器维护一张路由表表里写着“去哪个网段走哪个出口下一跳是谁”。当一个IP包到达路由器路由器查路由表找到匹配项然后把包从对应接口转出去。所以数据在互联网上传输的路径就是由沿途所有路由器逐跳决策拼出来的。这里要特别强调一点IP协议本身是“尽力而为”的。它只负责把包送出去但不保证不丢、不重、不乱序。真正让数据“可靠地”到达对方手里的是上一层TCP要做的事。这个“网络层不管可靠传输层才管可靠”的划分是整个TCP/IP设计里最精华的思想之一。还有一个现代互联网绕不开的话题就是NAT网络地址转换。因为IPv4公网地址不够用绝大多数家用网络是内网IP比如192.168.x.x这整个内网只有路由器一个公网IP。电脑把数据发给路由器路由器把源IP换成自己的公网IP、记下映射关系再把数据发出去回包到达路由器后路由器根据映射表把目标IP改回内网IP转给对应的电脑。这个机制让你家几十台设备能共用一个公网IP上网但它也导致了一个结果外部设备没法主动向内网设备发起连接——因为没有那个映射关系。这解释了为什么很多内网服务要做“端口映射”才能从外面访问。2.3 传输层端口与可靠性是谁给的传输层是四层模型里最重要的一层也是面试最爱问的一层。它解决两个问题一是“数据该交给哪个应用”二是“数据怎么可靠到达”。第一个问题靠端口解决。你电脑上同时开着微信、浏览器、邮件客户端数据到了你这台机器后怎么知道该交给谁端口号就是门牌号指定应用程序。比如HTTP默认80端口HTTPS默认443DNS默认53SSH默认22。数据包里写明了目标端口操作系统就能准确地把数据送给对应的进程。第二个问题TCP用一整套机制来保证。它是有状态、面向连接的协议。什么叫“面向连接”我打个比方你给朋友寄一个易碎品不能把东西直接丢到快递柜里就走你要先打电话跟对方说“我明天寄一个花瓶给你你留意一下”然后发货对方收到的时候再回个电话“我收到了”。TCP的连接就是发送前建立、接收后确认的这一套过程。建立连接就是大家熟知的“三次握手”客户端发一个SYN包里面带一个初始序列号比如seq1000表示“我想跟你建立连接”。服务端收到后回一个SYNACK包表示“我收到你的请求我的初始序列号是2000”。客户端再回一个ACK包表示“我收到你的确认连接建立”。为什么是三次不是两次核心原因是要让双方都确认“自己”和“对方”的收发能力都正常。如果只有两次服务器能确认自己发得出、收得到但客户端无法确认服务器是否收到了自己最后的确认——万一上次的确认丢了服务器会认为连接没建立。断开连接则是“四次挥手”因为TCP是全双工的两条方向的数据通道要各自关闭主动方发FIN表示“我的数据发完了想关闭我这边的通道”。被动方回ACK表示“我收到了但我可能还有数据要发”。被动方发完剩余数据后发FIN表示“我的数据也发完了可以关了”。主动方回ACK双方关闭。这里有两个经典坑主动关闭方会进入TIME_WAIT状态并等待2MSL报文最大生存时间的两倍后才真正关闭。这是为了处理那些“迟到”的旧包以及防止被动方的重传FIN包找不到人确认。如果你在服务器上看到大量TIME_WAIT连接通常意味着这个服务在频繁地主动关闭短连接需要调整连接复用参数而不是惊慌失措。除了握手挥手TCP还做了可靠传输每个数据段都有序列号接收方收到后要回复确认ACK如果发送方超时没收到ACK就重传接收方可以用滑动窗口控制发送方的速度防止处理不过来。正是因为这些机制你下载文件时哪怕遇到网络抖动最终拿到的文件也不会缺字节。代价就是TCP头比UDP大、连接有建立和断开的过程、传输效率不如UDP。所以像视频通话、游戏实时对战这类能容忍“丢几帧”但受不了“卡顿”的场景会用UDP。2.4 应用层HTTP、DNS、WebSocket 与一堆你每天都在用的协议应用层是最“接地气”的一层因为它直接跟你的需求打交道。HTTP、HTTPS、DNS、FTP、SSH、SMTP、WebSocket都在这层。HTTP是网页通信的基石。它本身是一个“请求-响应”模型客户端发一个请求比如GET /index.html服务器返回一个响应比如200 OK带上网页内容。HTTP协议本身没有状态每次请求都是独立的所以后来有了Cookie、Session、Token这些机制就是为了在无状态协议上弥补状态管理。HTTPS不是另一个协议它就是在TCP之上加了一层TLS/SSL加密。数据先经过TLS加密再交给TCP传输这样中间人即使截获了数据看到的也是密文。很多人以为HTTPS只是“经过了加密的HTTP”其实更准确的说法是“HTTP over TLS”。DNS是“电话簿”。你输入www.example.com电脑根本不知道这是哪台服务器得先问DNS服务器“www.example.com的IP是什么”DNS服务器返回IP后浏览器才用这个IP去发起TCP连接。DNS查询有自己的缓存机制浏览器缓存、操作系统缓存、本地DNS服务器缓存每一层都有可能命中所以有时候DNS配置改了但全网生效需要时间。WebSocket也是一个应用层协议它跟HTTP最大的不同是一旦建立连接服务器可以主动向客户端推送数据不需要等客户端先请求。HTTP像“发一封邮件问一句回一句”WebSocket更像“拨通电话后随时说话”。所以实时聊天、行情推送、网页协作都离不开它。你在QT里做客户端用QWebSocket类就是实现了这个协议它的底层一样是TCP。开发人员常听到的Socket编程其实也是这一层的概念操作系统给应用层提供的网络API让应用可以用TCP或UDP来收发数据。socket IP地址 端口号 协议这句话是很多教材的原话理解了这个再看具体的编程例程就不会觉得云里雾里。3. 从输入网址到页面出现一次完整通信的走读3.1 第一步不是发数据而是先问路DNS解析现在我们把前面所有知识串起来完整走一遍从输入网址到页面出现的全过程。假设你在浏览器地址栏输入www.example.com按下回车。浏览器首先要获取www.example.com的IP地址。它先在浏览器自己的DNS缓存里找找不到就去查操作系统缓存再找不到就去查本机配置的DNS服务器。整个过程是递归和迭代混合的本机问本地DNS服务器本地DNS服务器再替你去问根域名服务器、顶级域名服务器、权威域名服务器最后拿到IP返回给本机。这一步非常重要很多人碰到“网页打不开”以为是网络断了其实可能只是DNS解析失败了。你在命令行ping www.example.com如果显示“找不到主机”而ping IP地址能通那问题大概率出在DNS配置上。DNS解析完成之后浏览器拿着IP地址发起TCP连接。接下来就是前面讲的三次握手。3.2 建立连接不是瞬时完成三次握手的状态转换三次握手不是敲一下回车就瞬间完成的TCP的每一次状态转换都有严格定义客户端先发送SYN进入SYN_SENT状态。服务端收到SYN回复SYNACK进入SYN_RCVD状态。客户端收到SYNACK回复ACK进入ESTABLISHED状态。服务端收到ACK也进入ESTABLISHED状态。从这里开始双方的数据通道就打开了握手期间协商好的初始序列号会用于后续数据的编号。如果你在wireshark里抓包会看到三条记录seq0的SYNseq0的SYNACKseq1的ACK。很多初学者看到第一个包seq1很奇怪其实这是相对序列号Wireshark默认做了显示优化。3.3 数据出发从HTTP请求到以太网帧连接建好后浏览器发送HTTP请求。这个请求在应用层生成内容大概是这样的GET /index.html HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0这段文本向下传递TCP层把它当成应用数据加上TCP头源端口随机如54321目标端口443形成一个TCP段。IP层加上IP头源IP你的IP目标IPDNS解析出的服务器IP形成一个IP包。链路层加上MAC头源MAC你的网卡MAC目标MAC你的网关MAC形成一个以太网帧然后通过网线发出。这里有个细节如果你的电脑和网关不在同一个地方包到了路由器后路由器会重新封装MAC头把源MAC改成自己的出口MAC、目标MAC改成下一跳路由器的MAC但IP头基本不变。所以IP地址是“端到端”的MAC地址是“一跳一跳”的。理解这个区别是看懂网络拓扑的关键。数据包经过若干路由器最终到达服务器。服务器的链路层去掉MAC头IP层去掉IP头TCP层根据目标端口找到对应的Web服务器进程把HTTP请求交上去。Web服务器处理完返回一个HTTP响应。响应数据再走一遍“应用层生成 → TCP加头 → IP加头 → 链路层加头”的封装流程沿原路返回你的电脑。你的浏览器收到响应后解析HTML、加载CSS和JS、渲染页面最终你看到了网页。这个完整过程我用文字拆解一遍可能花了半页纸但真实时延通常只有几十到几百毫秒。如果你用curl -v https://www.example.com就能看到这整个过程的时间线先是DNS解析、TCP连接、TLS握手、发送HTTP请求、接收响应。3.4 想亲眼看到协议栈运转抓包是最好的老师文字讲一百遍不如自己抓一次包。抓包工具我用得最多的是tcpdump和Wireshark。比如你想看访问example.com时发生了什么可以这样操作sudo tcpdump -i any -n host example.com and port 443然后在另一个终端里执行curl -k https://www.example.comtcpdump会打出一堆包你会看到15:03:22.123456 IP 192.168.1.5.54321 93.184.216.34.443: Flags [S], seq 1000, length 0 15:03:22.234567 IP 93.184.216.34.443 192.168.1.5.54321: Flags [S.], seq 2000, ack 1001, length 0 15:03:22.234800 IP 192.168.1.5.54321 93.184.216.34.443: Flags [.], ack 2001, length 0Flags [S]是SYNFlags [S.]是SYNACKFlags [.]是ACK。这三条就是前面说的三次握手。后面跟着的是TLS握手和HTTP请求的包你可以按这个思路一层一层看下去。我强烈建议每一个学网络的人都亲手在Wireshark里开着“Follow TCP Stream”功能把一次HTTP请求的所有包从头看到尾。你对协议栈的理解会在这一刻产生质变。另外提一句嵌入式场景里常用的lwIP协议栈就是一个用C语言实现的轻量级TCP/IP协议栈它在FPGA、MCU上运行时的包结构、三次握手、状态迁移跟你在电脑上看到的完全一致。换平台换实现但协议栈的逻辑是通用的这也是网络通信最有魅力的地方——一套规则全世界通行。4. 零基础最容易栽跟头的 7 个概念4.1 端口到底是什么门牌号不是“几号窗”很多初学者会把端口理解成“电脑上开着的几个窗口”这是错的。端口其实是一个16位的数字范围0~65535用来在操作系统中定位到具体进程。可以这样想IP地址是“某某小区”端口就是“某某单元某某室”。数据包到了小区还要找到具体房间这个过程就是靠端口。常见的坑包括端口被占用你想启动服务但报错“bind: Address already in use”安全组/防火墙没放行端口云服务器上经常遇到程序明明在跑但从外部连不上把端口号跟进程号搞混不是一回事。排查端口问题Linux下可以用ss -lntp或netstat -lntp查看监听状态Windows下用netstat -ano。4.2 MTU、MSS和IP分片为何一个包会拆开送MTU是链路层能承载的最大数据量以太网默认1500字节。MSS是TCP在不触发IP分片的情况下能放的最大数据段长度通常等于MTU减去IP头和TCP头也就是1460字节。如果你发送的数据超过1460字节TCP层会先把它切成多个TCP段每个段都控制在1460字节以内这样IP层就不会再去分片。那IP分片什么时候发生常见于UDP或某些隧道协议里。比如你在一个MTU 1500的网络上发了一个UDP数据报大小为2000字节它会超出链路层限制IP层会把它分成两个分片。分片会增加丢包概率因为只要一片丢了整个数据报就废了。所以做网络调优时第一件事就是确认整条链路的MTU必要时调整TCP MSS。4.3 面向连接 vs 无连接UDP的“快”与“险”TCP和UDP的选择本质上是在“可靠”和“效率”之间做取舍。TCP有连接、有序列号、有确认重传、有拥塞控制所以它“靠谱”但也“慢”。UDP无连接、无确认、尽力而为所以它“快”但可能丢包、乱序。很多新手写网络程序不管什么场景都用TCP觉得“可靠肯定是好事”。但视频通话和实时游戏如果用了TCP遇到延迟就会等确认重传反而会让画面越来越卡。这类业务宁可丢一个没用的帧也不愿意等一整个迟到的帧所以用UDP或基于UDP的QUIC更合适。反过来传输文件、请求网页、转账交易这些场景丢一个字节都是灾难必须用TCP。理解这个选择逻辑比背“TCP可靠UDP不可靠”有用得多。4.4 长连接、短连接、Keep-Alive连接不是越多越好一次HTTP请求结束连接就断开这是短连接。一个连接建立后多次请求复用这是长连接。HTTP/1.1默认开启Keep-Alive就是希望复用一个TCP连接多次请求避免每次重新握手。但这里有个容易混淆的点HTTP的Keep-Alive和TCP的KeepAlive不是一回事。前者是应用层“帮我保住这个连接我还要继续用”后者是传输层“每隔一段时间探一下对方还在不在防止空连接占着资源”。实际工程里连接池、超时时间、最大连接数这些参数都是围绕“到底该用多少连接”来设计的。一个常见的坑是程序里每发一个请求就创建一个新连接又没设置超时结果大量连接堆积在TIME_WAIT或CLOSE_WAIT状态把端口和内存耗尽。这也是为什么很多框架会默认使用连接池。4.5 粘包和半包自己定义协议时怎么处理边界TCP是流式协议它只保证字节流的顺序不保证对方一次性“收到”你发送的完整消息。所以做Socket通信时如果你发一条消息对端可能一次收到两条粘包也可能收到半条半包。这不是协议栈出bug而是TCP没有消息边界的概念。处理方式有几种固定长度每条消息都定长比如4字节不够就补分隔符比如每条消息以换行符结尾HTTP也是类似思路更通用的是TLV格式消息头里带长度字段收端先读消息头再按长度读取消息体。我在自己写二进制协议时基本都用“2字节魔数 2字节长度 负载 CRC校验”这种格式。很多人第一次遇到粘包都以为是自己代码写错了其实是你没处理消息边界。这个问题只要理解一次以后写任何网络库都不怕。4.6 NAT一个公网IP如何带一堆设备上网前面提过NAT但这里必须再说一遍因为它是理解现代家庭网络和物联网设备通信的关键。你家路由器是内网的“大门”它的WAN口有一个公网IPLAN口给每个设备分配内网IP。当设备访问外网时路由器做地址转换并记录“哪个内网IP的哪个端口发起的连接”。外网数据回来时路由器根据记录转给对应设备。NAT带来一个问题外网设备无法主动向内网设备发起连接。为了解决这个问题就有了“端口映射”和UPnP把路由器公网IP的某个端口映射到内网某台设备的IP和端口上这样外网就能通过公网IP端口找到内网设备。这也是你在家搭网站、做NAS、跑智能家居时必须配置的东西。4.7 网络字节序为什么组二进制协议时要转大小端如果你自己写网络协议或者是做嵌入式、工控、游戏服务器开发一定会遇到“字节序”问题。不同CPU存储多字节整数的方式不同x86是“小端”低字节在前网络协议规定的是“大端”高字节在前。所以你在组包时要把本机的小端整数转成大端再发送接收时再把大端转回小端。C语言里有ntohs/htonlJava的DataOutputStream默认大端Python的struct.pack也有对应格式。我见过太多人第一次写TCP协议时因为没转字节序导致两端解析出来的数值完全不对自己发的是259对方解析出来是64000多。所以只要你的协议里有多字节整数字段第一步就是约定好字节序。不搞清楚这个连“数据是什么”都会理解错更别提后面的业务逻辑。5. 网络出问题的时候按这个思路排查5.1 先用命令行做快速体检网络问题排查最忌讳一上来就抓包。先从最基础的命令开始一层一层排除。首先是ping它验证“目标主机是否可达”。ping通说明IP层以下通ping不通说明链路、网络或目标主机的防火墙有问题。然后是ipconfigWindows或ifconfig/ip addrLinux查看本机IP、网关和DNS配置。如果本机IP是169.254.x.x说明DHCP获取地址失败如果网关都ping不通说明你所在的局域网有问题。接下来用route或ip route看路由表确认去往目标网段的包走哪个网关。用nc -vz ip port或telnet ip port测试目标端口通不通这一步能区分是“网络不通”还是“服务没起来”。最后用curl -v看HTTP层细节它能告诉你请求发出去没有、响应收到了没有、TLS握手卡在哪一步。我自己的排查顺序一般是ping网关 → ping外网IP → 解析域名 → telnet目标端口 → curl -v看协议。每一步都能缩小问题范围基本能在两分钟内定位到是链路、路由、DNS还是端口的问题。5.2 连接超时、RST、重传、丢包各自说明什么问题用命令行排完如果还没解决就到了抓包看细节的阶段。这里几个现象值得重视连接超时SYN发出去没有回应通常是防火墙拦截或目标主机宕机。SYN_SENT状态一直挂着你就得检查目标主机的安全组、本地防火墙、路由是否可达。RST连接重置通常说明对端主动拒绝了这个连接常见原因是端口没监听、防火墙返回了拒绝策略、或者协议不匹配。最典型的现象就是你访问一个没开服务的端口对端直接回RST。大量重传Retransmission说明这条链路有丢包或延迟抖动。网络不稳定、带宽打满、MTU设置错误都会导致重传。你会看到Wireshark里同一个seq反复出现配合重传统计就能判断出问题的严重程度。丢包的排查要结合整体来看ping丢包说明链路层或网络层有问题TCP不重传但应用层很多超时可能是中间设备静默丢包UDP丢包就更常见因为UDP没有补偿机制必须靠应用层自己加序号和重传。5.3 问题定位速查表现象可能原因排查方向ping不通网关网线/无线连接问题、DHCP异常检查网卡状态、重新获取IPping通网关ping不通外网IP路由配置、运营商问题检查默认路由、看路由表域名解析失败DNS配置错误、DNS服务器故障nslookup/dig 测试解析端口连不上服务没起、防火墙拦截telnet/nc 测试检查监听状态SYN一直超时防火墙拦截、路由不通抓包看SYN是否有回应连接被RST端口未监听、被主动拒绝检查服务状态、防火墙策略页面间歇性卡顿网络丢包、带宽跑满抓包看重传率、流量监控TIME_WAIT或CLOSE_WAIT大量堆积连接未复用、代码未关闭连接调整keep-alive参数、检查代码这张速查表不可能覆盖所有情况但它能帮你建立排查网络问题的基本框架。再遇到问题先判断问题出现在哪一层再决定用哪个工具效率会高很多。我个人在踩了无数网络坑之后最大的体会是TCP/IP协议栈真不是靠背就能学会的最好的方式是把数据包的完整路径走一遍——从自己的电脑出发经过每一层封装穿过路由器到达服务器再解封装回来。这个过程里每一个字段、每一个状态你都可以用抓包工具看得清清楚楚。最后再分享一个小技巧如果你刚开始学别急着啃源码和RFC文档先把自己电脑上的一次网页访问抓包下来对照这篇讲的封装过程把每个包的头字段逐个认一遍。认完五六个包你对整个协议栈的理解就超过很多人了。等你真正理解了数据流将来无论是看Linux内核的TCP协议栈代码、调嵌入式lwIP还是做上层应用开发都会顺畅很多。这个基础值得花一个晚上打好。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进