计算机网络 —— TCP/IP

📅 发布时间:2026/7/11 23:37:23
计算机网络 —— TCP/IP 1 为什么学习网络协议1.1 日常开发中的网络问题即使你写的业务代码大部分是CRUD网络问题也会冷不丁找上门接口超时调用下游服务偶尔报read timeout但下游明明很快。排查后发现是 TCP 连接池不够大量连接在排队等待释放连接断开客户端长连接莫名其妙断开日志里出现Connection reset by peer。可能是服务端 CLOSE_WAIT 堆积也可能是防火墙 idle timeout 踢掉了连接慢请求同样的 API有的用户快有的慢。抓包发现 DNS 解析耗时 2 秒或者 TCP 握手阶段丢包导致重传数据乱序或者丢包用 UDP 传日志偶尔少了几条。不了解 UDP 不可靠性就会以为是代码 bug这些问题背后全是网络协议的知识。不懂协议你只能重启大法、加日志盲猜懂了协议你能通过tcpdump和netstat精准定位根因。1.2 网络协议的分层思想计算机网络的复杂性决定了它必须分层设计。每一层只关心自己职责范围内的事下层为上层提供服务上层无需关心下层实现细节。OSI 七层模型理论标准层次名称功能典型协议/设备7应用层为用户提供网络应用HTTP、FTP、SMTP、DNS6表示层数据格式转换、加密、压缩SSL/TLS、JPEG、ASCII5会话层建立、管理和终止会话NetBIOS、RPC4传输层端到端可靠或不可靠传输TCP、UDP3网络层路由选择、分组转发IP、ICMP、OSPF2数据链路层相邻节点间的帧传输Ethernet、WiFi、ARP1物理层比特流传输光纤、双绞线、集线器七层网络可以记为“物链网输会示用”物联网叔会使用TCP/IP 四层模型事实标准层次名称对应 OSI 层主要协议4应用层5~7HTTP、DNS、FTP、SSH3传输层4TCP、UDP2网络层3IP、ICMP、IGMP1网络接口层1~2Ethernet、ARP、PPP四层网络模型可以记为“接网输用”接网叔用2 TCP/IP基石 —— IP与路由IP协议是TCP/IP协议的核心所有的TCPUDPIMCPIGMP的数据都以IP数据格式传输。要注意的是IP不是可靠的协议这是说IP协议没有提供一种数据未传达以后的处理机制这被认为是上层协议TCP或UDP要做的事情。2.1 IP地址、子网掩码、CIDRIP 地址是网络层的“门牌号”用于唯一标识一台主机。IPv4 地址长 32 位通常写成点分十进制如192.168.1.1子网掩码Subnet Mask​ 用来划分 IP 地址中的网络号和主机号。例如IP 地址 192.168.1.10 → 11000000.10101000.00000001.00001010 子网掩码 255.255.255.0 → 11111111.11111111.11111111.00000000 网络号 192.168.1.0 IP 掩码 主机号 0.0.0.10 IP ~掩码CIDR无类别域间路由​ 用IP/前缀长度表示例如192.168.1.0/24等价于子网掩码255.255.255.0表示前 24 位是网络号后 8 位是主机号。/24可用主机数2^(32-24) - 2 254去掉全 0 的网络地址和全 1 的广播地址常见前缀/8A 类、/16B 类、/24C 类面试可能会问10.0.0.0/8有多少可用 IP答案是 2^24 - 2 16,777,214。2.2 数据包如何在网络跳跃当你的电脑访问www.example.com时数据包从你的网卡出发经过多个路由器最终到达目标服务器。这个过程依靠路由表和下一跳机制。路由表​ 每个路由器都有一张路由表记录着目标网络、子网掩码、下一跳地址和出接口。关键点是最长前缀匹配路由器会选择掩码最长的匹配项。数据包跳跃过程简化你的电脑查询路由表发现目标不在局域网内将包发给默认网关。家用路由器查表转发给运营商的接入路由器。运营商骨干网路由器通过 BGP 协议逐跳转发直到抵达目标服务器。目标服务器所在局域网的交换机根据 MAC 地址将包交给服务器网卡。每一跳数据包的源/目的 IP 不变但源/目的 MAC 地址会随链路改变。这里我通过抓包软件抓取curl http://nginx.org/LICENSE的整个流程阶段 1DNS 解析并行查询与响应竞赛第 5254、5255 行87.7435s你的主机10.33.3.236向主 DNS222.200.115.251同时发起AIPv4和AAAAIPv6域名解析请求查询nginx.org。第 5260、5261 行87.7700s仅隔26ms你的主机可能因主 DNS 响应稍慢又向备 DNS10.1.3.38重传/并行发起了同样的 A 和 AAAA 查询glibc 的 resolver 常会并行发送。第 5262、5263 行87.7893s备 DNS10.1.3.38迅速响应返回 A 记录nginx.org A 52.58.199.22数据包长度 85包含 IP。返回 AAAA 记录响应包长度 69无 IPv6 地址或因未设置而返回空。第 5277、5278 行88.0827s主 DNS222.200.115.251此时才姗姗来迟地返回响应且返回了一个不同的 IP3.125.197.172。关键决策由于备 DNS 提前 300ms 返回了52.58.199.22你的curl已经采用了该 IP 建立连接因此后续主 DNS 返回的3.125.197.172被直接丢弃。阶段2TCP 三次握手连接 52.58.199.22第 5264 行87.7991s客户端发送SYN包Seq0向52.58.199.22:80发起连接带有 MSS1460、窗口缩放等选项。第 5274 行88.0698s服务器回复SYN-ACKSeq0, Ack1确认连接。第 5275 行88.0699s客户端回复ACKSeq1, Ack1。三次握手完成连接建立。阶段 3发送 HTTP 请求第 5276 行88.0701s客户端发送HTTP GET请求请求路径为/LICENSE协议为 HTTP/1.1。数据包长度为 134 字节包含 HTTP 头。阶段4ICMP 异常迟到的 DNS 响应被拒绝第 5279 行88.0828s当主 DNS222.200.115.251将迟到的 DNS 响应包含3.125.197.172发回给客户端时客户端回复了一个ICMP 类型 3目标不可达代码 3端口不可达阶段 5接收 HTTP 响应数据分片与重组第 5301 行88.2836s服务器回复纯ACK确认收到客户端的 GET 请求。第 5302、5303 行88.2836s服务器发送 HTTP 200 OK 响应。由于响应内容较大LICENSE文件TCP 将其分为两个数据包发送第 5302 行发送1360 字节有效载荷标记为[TCP PDU reassembled in 5303]表示待重组。第 5303 行发送剩余数据约 361 字节并在该包中附带 HTTP 状态行HTTP/1.1 200 OK (text/plain)。第 5304 行88.2836s客户端发送ACK确认已收到全部数据Ack1722证明累计收到了 1721 字节数据与 1360361 吻合。阶段 6四次挥手优雅关闭连接第 5305 行88.2838s客户端发送FIN, ACK表示数据已接收完毕主动要求关闭发送方向。第 5306 行88.4981s服务器回复ACK确认收到 FIN。第 5307 行88.4982s服务器发送自己的FIN, ACK本数据包仅显示[ACK]但从序列号Ack82和Seq1723推断这实际是双向关闭的最后一环客户端回复最终 ACK连接彻底关闭。这里有关主备DNS做一个解释你的电脑 (10.33.3.236)同时向两个DNS服务器发起了查询主DNS (222.200.115.251)这个IP很可能是你的网络运营商如电信、联通提供的官方DNS。备DNS (10.1.3.38)这是一个内网IP很可能是你的路由器或局域网内的另一台设备。“竞赛”与切换这一次备DNS (10.1.3.38) 响应速度更快先返回了IP地址 (52.58.199.22)。你的电脑采用了这个结果并忽略了主DNS稍后才返回的另一个IP (3.125.197.172)。ICMP错误当主DNS迟到的响应到达时电脑发现用于询问的“窗口”已经关闭于是它发回一个ICMP错误相当于告诉主DNS“这个问题我已经解决了不用再回复了。”2.3 MTU与IP分片MTUMaximun Transmission Unit是数据链路层一帧能承载的最大数据量。以太网的MTU通常是1500字节。如果IP包大于MTU就需要分片分片的弊端增加开销每个分片都要加 IP 头。放大丢包影响只要一个分片丢失整个原始报文都得重传。安全风险分片重叠攻击等。路径 MTU 发现PMTUD​ 是现代 TCP 避免分片的关键机制发送方在 IP 头设置DF标志如果某个链路 MTU 太小路由器会丢弃并回复 ICMP 错误发送方据此减小包大小。这就是为什么 TCP 的 MSSMaximum Segment Size通常设为 1460 字节1500 - 20 字节 IP 头 - 20 字节 TCP 头。2.4 ARP 协议与地址解析ARP 是根据IP地址获取MAC地址的一种协议。ARP地址解析协议是一种解析协议本来主机是完全不知道这个IP对应的是哪个主机的哪个接口当主机要发送一个IP包的时候会首先查一下自己的ARP高速缓存就是一个IP-MAC地址对应表缓存。如果查询的IPMAC值对不存在那么主机就向网络发送一个ARP协议广播包这个广播包里面就有待查询的IP地址而直接收到这份广播的包的所有主机都会查询自己的IP地址如果收到广播包的某一个主机发现自己符合条件那么就准备好一个包含自己的MAC地址的ARP包传送给发送ARP广播的主机。而广播主机拿到ARP包后会更新自己的ARP缓存就是存放IP-MAC对应表的地方。发送广播的主机就会用新的ARP缓存数据准备好数据链路层的的数据包发送工作。3 TCP核心机制3.1 连接管理三次握手与四次挥手三次握手第一步客户端发送 SYN初始化序列号 x进入SYN_SENT。第二步服务端回复 SYNACK初始化序列号 y确认号 ackx1进入SYN_RCVD。第三步客户端回复 ACK双方进入ESTABLISHED。为什么是三次不是两次答“从表面看三次握手是为了验证双方的收发能力是否正常。第一次握手表明确认客户端的发送能力和服务端的接收能力第二次握手确认服务端的发送能力和客户端的接收能力但此时服务端还不知道客户端是否收到了自己的回应所以需要第三次握手来最终确认。但更深层的核心原因有两点第一为了防止‘已失效的连接请求报文段’突然又传到服务端造成资源浪费。如果没有第三次握手服务端收到一个过期的SYN就会直接建立连接导致服务端白白消耗资源。第二这是为了同步双方的初始序列号ISN。三次握手让双方明确地交换并确认了各自的起始序号这是后续TCP可靠传输和拥塞控制的基础。所以三次握手是保障TCP可靠性的最小且必要步骤两次握手无法规避历史连接的隐患而四次握手则冗余没必要。”四次挥手注意这里的抓包软件Wireshark 为了方便用户阅读自动隐藏了一次挥手过程。Wireshark 的显示逻辑是如果 ACK 包紧接着上一个包发出或者是为了响应上一个包它会尝试将它们合并显示。第一步主动关闭方发送 FIN进入FIN_WAIT_1。第二步被动关闭方回复 ACK进入CLOSE_WAIT主动方进入FIN_WAIT_2。第三步被动方发送 FIN进入LAST_ACK。第四步主动方回复 ACK进入TIME_WAIT等待 2MSL 后关闭。TIME_WAIT 两大作用保证最后一个 ACK 可靠到达。让旧报文在网络中自然消失。TIME_WAIT 过多怎么办开启tcp_tw_reuse需配合tcp_timestamps。使用长连接减少连接次数。服务端改为被动关闭。⚠️tcp_tw_recycle已在 Linux 4.12 之后移除且 NAT 环境下有严重 bug不要再使用。为什么是四次挥手TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式这就意味着当主机1发出FIN报文段时只是表示主机1已经没有数据要发送了主机1告诉主机2它的数据已经全部发送完毕了但是这个时候主机1还是可以接受来自主机2的数据当主机2返回ACK报文段时表示它已经知道主机1没有数据发送了但是主机2还是可以发送数据到主机1的当主机2也发送了FIN报文段时这个时候就表示主机2也没有数据要发送了就会告诉主机1我也没有数据要发送了之后彼此就会愉快的中断这次TCP连接。为什么客户端收到 FIN 后还要等 2MSLTime-Wait这是为了确保服务端收到了最后的 ACK。如果客户端发送完最后一个ACK后直接关闭万一这个ACK在网络中丢失了服务端会因为迟迟收不到ACK而认为自己的FIN没送到于是超时重传FIN。此时客户端如果已经关闭了收到这个重传的FIN就会回复RST复位导致服务端报错。所以客户端必须等待2MSL最大报文生存时间的两倍确保如果服务端重传FIN自己还能收到并重新发送ACK从而确保被动关闭方能正常进入CLOSED状态这是TCP可靠性的最后一道保险。”3.2 可靠传输机制序列号与确认应答每个字节都有序列号接收方回复 ACK 表示期望收到的下一个字节序号累计确认。超时重传RTO动态计算 RTT超时重传指数退避。快速重传收到三个相同 ACK 立即重传不等超时。SACK选择性确认只重传丢失的部分。滑动窗口发送窗口 min(cwnd, rwnd)接收窗口由接收方缓冲区决定。3.3 流量控制 vs 拥塞控制流量控制拥塞控制关注点接收方的处理能力网络的承载能力作用对象发送方与接收方之间全网路径上的路由器/交换机实现方式滑动窗口rwnd拥塞窗口cwnd 算法3.3.1 流量控制如果发送方把数据发送得过快接收方可能会来不及接收这就会造成数据的丢失。所谓 流量控制 就是让发送方的发送速率不要太快要让接收方来得及接收。利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。设A向B发送数据。在连接建立时B告诉了A“我的接收窗口是 rwnd 400 ”(这里的 rwnd 表示 receiver window)。因此发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意TCP的窗口单位是字节不是报文段。假设每一个报文段为100字节长而数据报文段序号的初始值设为1。大写ACK表示首部中的确认位ACK小写ack表示确认字段的值ack。从图中可以看出B进行了三次流量控制。第一次把窗口减少到 rwnd 300 第二次又减到了 rwnd 100 最后减到 rwnd 0即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机B重新发出一个新的窗口值为止。B向A发送的三个报文段都设置了 ACK 1只有在ACK1时确认号字段才有意义。TCP为每一个连接设有一个持续计时器(persistence timer)。只要TCP连接的一方收到对方的零窗口通知就启动持续计时器。若持续计时器设置的时间到期就发送一个零窗口控测报文段携1字节的数据那么收到这个报文段的一方就重新设置持续计时器。3.3.2 拥塞控制流量控制只考虑接收方的处理能力但网络本身也可能拥堵——路由器缓存溢出、链路带宽不足。如果所有发送方都按自己的最大能力发送网络很快就会瘫痪拥塞崩溃拥塞控制分为4个阶段慢启动Slow Start连接刚建立时发送方并不知道网络的容量因此保守地从一个很小的拥塞窗口cwnd开始。初始 cwnd 通常为 10 个 MSS约 14600 字节Linux 3.0 以后。每收到一个 ACKcwnd 增加 1 个 MSS实际上是指数增长每个 RTT 内 cwnd 翻倍。当 cwnd ≥ ssthresh慢启动阈值时转入拥塞避免阶段。抓包特征慢启动阶段发送方发出的数据包序列呈“爆炸式”增长Wireshark 可以看到连续的[ACK]后跟着越来越多的数据段。拥塞避免Congestion Avoidance进入拥塞避免后cwnd 的增长变为线性每经过一个 RTTcwnd 增加 1 个 MSS实际实现中每收到一个 ACKcwnd MSS / cwnd。这个阶段持续到发生丢包超时或收到三个重复 ACK。快速重传Fast Retransmit当发送方收到三个重复的 ACK即同一个 seq 被确认了四次说明很可能发生了丢包不必等待超时立即重传丢失的报文段。同时发送方认为网络可能轻度拥塞进入快速恢复阶段。快速恢复Fast Recovery收到三个重复 ACK 时ssthresh cwnd / 2但不能小于 2 个 MSS。cwnd ssthresh 3加 3 是因为已经收到的三个重复 ACK 表明有三个报文段离开了网络。随后每收到一个重复 ACKcwnd 增加 1 个 MSS允许发送新的数据。当收到新的 ACK确认了新数据时cwnd 设置为 ssthresh退出快速恢复进入拥塞避免。注意如果发生超时RTO则 ssthresh cwnd / 2cwnd 1重新进入慢启动。超时意味着网络可能严重拥塞。4 UDP与TCP的选择4.1 UDP与TCP的区别是否面向连接TCP是面向连接的通讯前需要三次握手断开时需要四次挥手。是端对端的连接支持全双工通讯UDP是面向无连接的不需要三次握手和四次挥手支持一对一、一对多、多对一和多对对的通讯数据传输方式TCP面向字节流。不保留应用层的消息边界数据像水流一样连续传输接收方需要自行解析数据块。存在粘包问题UDP面向数据报。每个udp数据包都是独立的有明确的边界接收方收到的就是发送方发出的完整数据单元是否可靠TCP是可靠的连接。提供确认机制ACK、重传机制、序列号、丢包重发、乱序重组、流量控制和拥塞控制确保数据完整、有序、不丢失地到达。UDP是不可靠传输。不保证数据送达也不重传、不排序。数据包可能丢失、重复或乱序由上层应用自行处理。传输效率TCP由于有连接建立、确认、重传、流量控制等机制开销大延迟较高但是稳定UDP头部小8个字节无连接开销传输速度快、延迟低适合对实时性要求高的场景4.2 应用场景场景推荐协议原因网页浏览HTTP/HTTPS、文件传输FTP、电子邮件SMTPTCP要求数据完整、准确不能丢包视频直播、在线游戏、语音通话VoIP、DNS查询UDP追求低延迟少量丢包可接受实时性优先广播/组播如网络发现、IPTVUDPTCP不支持广播和组播