ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux网络基础:协议栈、TCP握手与排障命令详解

Linux网络基础:协议栈、TCP握手与排障命令详解 有朋友问我Linux网络到底该怎么学是不是把常用命令背熟就够了我说命令只是手协议栈才是骨架。今天这篇就把Linux网络基础中最容易绕晕的部分——协议与网络传输基本原理——摊开讲一遍从数据包怎么封装、路由怎么转发到TCP三次握手四次挥手、tcpdump和ss怎么用一次说透。适合刚从Windows切到Linux的运维也适合准备Linux面试的开发者。1. 先说清楚Linux网络基础到底学什么1.1 协议栈是内核里的一套“翻译规则”很多人一开始就把“协议”想复杂了。协议这东西说白了就是双方约定好的沟通格式。就像两个快递公司之间要交接包裹面单上写清楚从哪来、到哪去、多重、什么品类双方按照同一个标准填和读包裹就不会乱。网络里的协议也一样。TCP/IP协议栈就是一套分层约定的规则Linux内核里把这套规则实现成了实实在在的代码。你在Linux上跑任何网络服务无论是Nginx还是sshd发出的请求都会从应用层往下走经过内核的TCP/UDP模块、IP模块、ARP模块最后从网卡发出去。内核会帮你把数据切成合适的大小、加上对应的头部对端收到后按同样的规则剥掉头部把数据交给对端的应用。所以Linux网络基础学的不是某个工具而是“内核如何按照协议规则收发数据”。理解了这条线后面看防火墙、看路由、看抓包、看性能问题都是一通百通。1.2 四层模型和七层模型Linux更看中哪套教科书上最爱讲OSI七层模型物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。这个模型适合教学把每一层的职责分得非常细但Linux实际跑起来并不是严格按七层实现的。内核里更贴合的是TCP/IP四层模型链路层、网络层、传输层、应用层。为什么这么说因为代码里根本不存在独立的“会话层”和“表示层”。你写socket程序的时候会话管理是内核和业务逻辑一起干的数据加密、压缩这些“表示层”的事往往直接放在应用层里比如HTTPS就是在HTTP外面套了TLS本质上还是应用层自己处理。学习的时候我建议以四层模型为主线先把每一层的核心职责记牢链路层管MAC地址和帧网络层管IP地址和路由传输层管端口和可靠传输应用层管具体业务格式。你抓包时看到的TCP、UDP、IP、ARP、ICMP全部是在下面几层。热搜词里的“7层协议”可以当作补充知识但别让七层的分层把你绕晕真正排障时你盯着四层模型去分析就够了。2. 数据在网络上是怎么“走”出去的封装与传输原理2.1 从应用层到网卡的封装过程很多人能背出“封装”两个字但没亲眼看过一个数据包长什么样。我帮你拆一遍。假设你在Linux上用curl请求一个HTTP页面比如curl http://example.com。应用层先把HTTP请求报文交给内核内核看到目标端口是80就把数据交给TCP模块。TCP模块干的第一件事是给这堆数据编上序号算出校验和加上TCP头部源端口、目的端口、序号、确认号、标志位等形成一个TCP段。接着这个段交给IP模块。IP模块查路由表确定下一跳该往哪走然后加上IP头部源IP、目的IP、协议号TCP是6UDP是17、TTL等等形成一个IP包。如果数据包大于链路层的MTU通常以太网是1500字节IP模块还可能把包分片。再往下链路层要把它封装成以太网帧加上目标MAC地址、源MAC地址、类型字段然后交给网卡驱动程序由网卡转成电信号发出去。这一层层套起来的结构跟俄罗斯套娃一模一样应用数据 TCP头部 IP头部 以太网帧头部 数据 帧尾所以在抓包工具里看到的每一个包都是带着好几种“标签”的完整帧。这也是为什么tcpdump抓包输出里会同时出现MAC地址、IP地址、端口号——它们都在同一个包的不同区域里。2.2 解封装收包方向走一遍接收方向完全反过来。网卡收到一个以太网帧后先校验帧尾的FCS帧校验序列通过后剥掉以太网头部看到里面的协议号是0x0800IPv4于是把IP包交给IP模块。IP模块检查目的IP是不是本机地址是的话剥掉IP头部再看协议号是6还是17把里面的数据交给TCP或UDP模块。TCP模块根据目的端口找到正在监听的socket把数据放入接收缓冲区应用调用read()/recv()就能取到内容。这里有个容易忽略的细节数据包在每一层都要校验一次。以太网有CRC校验IP头有校验和TCP段也有校验和。任何一个校验失败包都会被直接丢弃不向上层传递。所以如果你看到一个网络“通但慢”的现象有可能是链路噪声导致校验失败重传而不是带宽不够。2.3 IP地址、端口、协议号三要素别搞混要定位一个网络连接光知道IP地址不够。我用一个比喻IP地址是“楼址”端口是“房间号”协议号是“用什么语言沟通”。你找到了楼还得找到具体房间且双方都得会同一门语言才能聊起来。一个完整的TCP连接是由四元组唯一确定的源IP源端口目的IP目的端口比如你用浏览器访问https://192.168.1.10:443源IP是你本机的192.168.1.20源端口是随机分配的一个高位端口比如52341目的IP是192.168.1.10目的端口是443。这个四元组里任何一个变了都可能变成一条完全不同的连接。用ss -tan在Linux上查看时输出里每一行都包含类似192.168.1.20:52341 192.168.1.10:443的地址对左边是本地地址右边是对端地址。看到这个格式你就知道内核记录连接时就是靠四元组区分的。这也是为什么端口资源不够时系统会报“Cannot assign requested address”——因为可用的四元组组合被占满了。3. Linux网络协议栈里的关键机制3.1 路由表数据包出门前必须问路数据包从IP层出发前内核一定要先查路由表这个包该交给哪块网卡、下一跳是谁。Linux上用ip route查看路由表典型输出长这样default via 192.168.1.1 dev eth0 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.20第一条是默认路由所有没匹配到更精确条目的包都发给网关192.168.1.1。第二条是直连网段路由目的IP在192.168.1.0/24这个网段内直接通过eth0发出去不需要网关。路由匹配的核心原则是“最长前缀匹配”。比如同时存在0.0.0.0/0和192.168.1.0/24要访问192.168.1.5时/24的掩码更长所以优先走直连路由要访问8.8.8.8时只有默认路由能匹配所以走网关。实际排障中我遇到最多的情况是Linux主机上开了多块网卡默认路由指向了错误的接口导致跨网段访问时通时不通。这时候用ip route replace default via 目标网关 dev 对应网卡改回来就行。另外Linux做软路由或NAT时需要确认net.ipv4.ip_forward是否已打开sysctl -w net.ipv4.ip_forward1忘了开这个数据包到了Linux却转发不出去表现就是“隔壁机器能ping通Linux但ping不通Linux后面的网络”。3.2 TCP的状态变迁三次握手和四次挥手TCP可以算整个Linux网络面试里出现频率最高的考点没有之一。我建议你把状态迁移完整过一遍。三次握手的过程是客户端发SYNSeq设为随机数x状态变成SYN_SENT。服务端收到SYN回复SYNACKSeq设为yAck为x1状态变成SYN_RCVD。客户端收到SYNACK回复ACKSeq为x1Ack为y1连接建立状态变成ESTABLISHED。为什么一定要三次握手因为TCP要解决“确认双方收发能力都正常”的问题。两次握手的话服务端没法确认客户端是否收到自己的SYNACK如果丢了服务端以为建好了客户端却不知情就会白白等待。四次挥手过程则是因为TCP连接是双向的每一方向都需要单独关闭主动方发FIN状态变成FIN_WAIT_1。被动方回ACK进入CLOSE_WAIT。被动方也发FIN进入LAST_ACK。主动方回ACK进入TIME_WAIT等待2MSL后再关闭。排障时最常被问到的就是大量TIME_WAIT。这个状态是主动关闭方在等旧连接的延迟报文彻底消失防止新连接收到脏数据。短连接越频繁TIME_WAIT越多。调整TCP参数可以缓解但如果对端是NAT环境要非常小心别随便开tcp_tw_recycle它会因为时间戳失效导致连接被对端丢弃这个坑我踩过。3.3 内核参数与网络性能的微妙关系Linux网络性能问题七成最后都能落到内核参数上。以下几个是高频调整项net.core.somaxconn全连接队列长度Nginx、Redis这些服务端都在用队列满了客户端就会连接被重置。net.ipv4.tcp_syncookiesSYN攻击时挽救半连接队列的开关建议开启。net.ipv4.tcp_fin_timeoutFIN_WAIT_2的等待时长默认60秒短连接多可以适当调低。net.ipv4.ip_local_port_range本机主动发起连接时可用的临时端口范围范围太小会导致高并发下端口不够用。net.core.rmem_max和net.core.wmem_maxsocket接收/发送缓冲区上限。调参数前一定要知道自己要解决什么。比如客户端大量出现“connect失败”先看半连接队列和全连接队列有没有溢出ss -lnp | grep 端口 # 或者用 ss -lnt | grep -E SYN|LISTEN如果队列溢出内核日志或netstat -s里会看到对应计数器在涨。只有确认了瓶颈再去调参数才是有效的否则就是瞎调。4. 常用Linux网络命令与排障实战4.1 ping、telnet/nc、tcpdump的定位不一样排障第一步要分清工具的使用范围不同工具检测的是不同层不能用错。ping用的是ICMP协议工作在IP层之上主要看主机是否可达、链路时延是多少。但它只证明“IP层通”证明不了“端口通”“服务正常”。telnet IP 端口或nc -vz IP 端口用来测TCP端口是否可连。能建成TCP连接基本说明服务端在监听且防火墙放行。curl是应用层测试工具能直接测HTTP接口的响应码、响应时间、TLS握手等情况。tcpdump是抓包工具能看到协议栈里真实发生的交互适合分析三次握手、重传、丢包、异常响应等复杂问题。我见过很多人一上来就ping结果ping不通就断言网络断了。实际上有些服务器为了安全禁止ICMP回显ping不通但TCP 443端口完全正常。所以正确姿势是先确认目的IP是否可达再测端口再抓包看协议交互。4.2 快速看懂ss、netstat的输出老运维习惯用netstat新一点的环境里ss更快、输出更全。两者核心信息一致我以ss -tan为例State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 0.0.0.0:22 0.0.0.0:* ESTAB 0 40 192.168.1.20:45210 192.168.1.15:80LISTEN服务端正在监听的端口0.0.0.0:22表示所有IPv4地址都监听22端口。ESTAB一条已建立的连接注意本地端口是45210说明是本机发起的连出请求。Recv-Q和Send-Q接收和发送队列积压字节数。如果Recv-Q长期不降说明应用没及时读数据可能卡在业务逻辑上。要看进程名和PID加-p参数但需要root权限。排查端口被谁占用时用ss -tlnp | grep :80输出里会直接显示占用端口的进程名和PID比lsof -i:80在有些场景下更直观。4.3 一个典型连接故障的排查过程我举一个真实排障例子帮你把命令串起来用。某天同事反馈应用服务器访问数据库超时但服务器本身能ssh登录。我先在应用服务器上ping 数据库IP通了延迟1ms说明IP层和链路层没问题。接着测端口telnet 数据库IP 3306结果卡住不返回说明TCP连接建不起来。可能是防火墙丢SYN包也可能是数据库没在监听。到数据库服务器上看监听ss -tlnp | grep 3306结果3306确实在LISTEN。于是怀疑是中间防火墙或安全组拦截。在应用服务器上抓包tcpdump -i eth0 tcp port 3306 -n -c 100抓包发现SYN包发出后没有收到SYNACK也没有RST。这说明包在某个环节被静默丢弃了最典型的就是防火墙DROP策略。去检查iptablesiptables -L -n | grep 3306果然有一条DROP规则命中。联系网络管理调整安全策略后连接立刻恢复。这个案例里如果一开始只ping只会得到“网络通”的结论根本定位不到防火墙后面的tcpdump和iptables检查才是关键。5. 高频面试与实操陷阱整理5.1 那些容易答错的概念面试Linux网络基础时有几个经典概念最容易踩坑。第一个是“TCP粘包”。很多人以为粘包是TCP协议的问题其实TCP是字节流协议它只管把字节按顺序可靠地送到对端不替应用划分消息边界。粘包问题本质上是你应用层没定好协议格式接收方无法判断一条消息到哪结束。解决办法是应用层自定义分隔符或固定长度头。第二个是“MTU和MSS的区别”。MTU是IP层能承载的最大包大小通常以太网是1500字节。MSS是TCP数据段的最大长度通常是MTU减去IP头部和TCP头部差不多1460字节。TCP握手时双方会协商MSS避免IP分片。分片会导致性能下降和丢包敏感所以TCP一般都会尽量协商一个合适的段大小。第三个是“半连接和全连接队列”。TCP三次握手时服务端收到SYN后连接先进入半连接队列等ACK后再移入全连接队列应用accept()从全连接队列拿连接。两个队列都有长度上限满了就会表现出连接建立缓慢、超时甚至被RST。很多你调了net.core.somaxconn却不起作用的情况是因为应用层自己的listen backlog没同步调大。第四个是“TCP是可靠的但UDP不一定不可靠”。UDP虽然不保证送达但可以基于UDP实现可靠传输比如QUIC就是在UDP之上实现了类似TCP的可靠性、拥塞控制只是把控制逻辑搬到了用户态。面试时如果只说“UDP不可靠”就太浅了能说出这个层次才算真正理解。5.2 常见问题速查表平时排查问题最快的方式是按“现象 - 可能原因 - 排查命令”对照着来。我整理了一张速查表覆盖面比较广。现象可能原因排查方向ping不通物理链路、IP地址配置、路由缺失、对端禁ICMPip addr、ip route、ping -I 源IP 目标IPping通但telnet端口不通服务未监听、防火墙DROPss -tlnp、telnet IP 端口、tcpdump连接直接拒绝服务未启动、防火墙REJECT、端口被占用ss -tlnp、iptables -L -n连接超时防火墙丢包、路由黑洞、对端负载过高tcpdump看SYN是否发出、是否收到SYNACK大量TIME_WAIT短连接太多ss -tan state time-wait考虑连接复用大量SYN_RECV半连接队列满、SYN攻击netstat -s、调大队列、开syncookie大量CLOSE_WAIT服务端应用没关socket检查应用代码处理连接释放服务能连但响应很慢DNS解析慢、TLS握手慢、后端处理慢curl -w观察各阶段耗时丢包率很高网卡队列满、链路噪声、驱动问题ethtool -S、ifconfig看drop计数无法主动发起大量连接本地端口范围太小cat /proc/sys/net/ipv4/ip_local_port_range这张表没法覆盖所有网络问题但能帮你快速建立一个排查入口。遇到一时定位不了的情况记住一个原则从底层往上层一层层试。链路层、IP层、TCP层、应用层哪一层先断就在哪层停下。5.3 想深入学习Linux网络下一步怎么看纸上谈兵容易真正能把网络基础吃透我的建议是两件事。第一件学用tcpdump和wireshark。开一个抓包窗口然后做一次ssh登录、一次curl请求、一次TCP三次握手看每个包的细节比看十遍理论都有用。第二件自己用socket写一个最小的echo服务端和客户端在Linux上跑通然后故意制造故障比如把listen队列改小、把对端延迟调大观察程序表现。这些实操经验才是你面试和排障时真正能拿出手的东西。我个人这几年最深的体会是网络问题不像程序bug它不会给你一个清晰的报错栈而是表现为“慢”“卡”“时断时续”。这时候唯一的敲门砖就是老老实实分层排查从物理链路到DNS再到应用一条条排除最终总会在某一层找到那个不起眼的丢包点或配置错误。多抓几次包、多踩几个坑之后你对Linux网络基础的理解就会从“背概念”变成“看得见、摸得着”。
返回列表