尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux网络---传输层协议TCP(三)

Linux网络---传输层协议TCP(三) 1、理解TIME_WAIT状态TCP 协议规定主动关闭连接的一方要处于 TIME_WAIT 状态等待两个 MSL (maximum segment lifetime) 的时间后才能回到 CLOSED 状态. 我们使用 Ctrl-C 终止了 server, 所以 server 是主动关闭连接的一方在 TIME_WAIT 期间仍然不能再次监听同样的 server 端口 MSL 在 RFC1122 中规定为两分钟但是各操作系统的实现不同在 Centos7/Ubuntu 上默认配置的值是 60s; 可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看 msl 的值1. TIME_WAIT 是什么“主动关闭方”的专利TCP 连接终止时主动发起关闭发送第一个 FIN的一方在收到对端最后一次确认ACK后并不会直接进入 CLOSED 状态而是进入TIME_WAIT状态。2. 为什么要等 2MSL核心设计的两个使命TIME_WAIT 的持续时间是2MSLMSLMaximum Segment Lifetime报文最大生存时间。在 Linux 系统中MSL 通常为 30秒因此 TIME_WAIT 通常持续60秒。这个等待期肩负着两大不可替代的使命2MSLMSL等待可能延迟的ACKMSL(等待可能重传的FIN这确保了主动关闭方发送的最后一个ACK能够可靠到达网络中所有属于这个连接的报文都彻底消失等待2MSL的主要原因是1、确保双方4次挥手都尽可能正确完成2、让陈旧报文在网络中尽可能消散使命一确保最后一个 ACK 能被对方收到“兜底”机制主动关闭方发出的最后一个 ACK 可能会丢失。如果被动关闭方没收到 ACK它会超时重传 FIN。如果主动关闭方直接关闭进入 CLOSED就收不到重传的 FIN导致被动方永远停留在 LAST_ACK 状态浪费资源。处于 TIME_WAIT 的机器可以响应这个重传的 FIN并重新发送 ACK从而保证双方都能正常关闭。使命二让网络中“迷路的”旧数据包自然消亡“防止串线”由于网络延迟连接中可能还有迟到的数据包延迟段正在网络中游荡。如果旧连接关闭后马上用相同的 IP 和端口建立新连接这些“幽灵”数据包突然到达就会被新连接误认为是合法数据导致数据错乱。等待 2MSL 的时间足以保证网络中该连接的所有残余报文包括重传的全部消失。这样新连接就不会受到旧数据的干扰。3. 为什么 TIME_WAIT 集中在“服务器”上很多人误以为 TIME_WAIT 是服务器的噩梦其实这取决于谁是“主动关闭方”短连接 高并发如 HTTP/1.0 服务服务器处理完请求后主动关闭连接大量 TIME_WAIT 堆积在服务器上。客户端主动关闭如调用方TIME_WAIT 堆积在客户端。这里有一个反直觉的冷知识TIME_WAIT 占用的不是内存而是端口号五元组中的源端口。由于一个端口在 TIME_WAIT 期间不能被复用如果服务器短时间爆发百万级连接端口就会被耗光导致无法建立新连接报错Cannot assign requested address。4. 如何应对 TIME_WAIT 过多既然无法绕过就需要策略性地处理最佳实践长连接在微服务内部调用中尽量使用HTTP Keep-Alive或长连接池避免频繁重复建立和关闭连接从根源上减少 TIME_WAIT 的产生。开启端口重用tcp_tw_reuseLinux 内核允许在 TIME_WAIT 状态下复用端口前提是新连接的时间戳大于旧连接的时间戳需开启tcp_timestamps。这非常适用于客户端或短连接场景。注意严禁开启tcp_tw_recycleLinux 4.12 后已移除它在 NAT 环境下会导致严重的丢包问题。调整net.ipv4.ip_local_port_range扩大本地端口的可用范围如改为1024 65535增加端口池容量。使用SO_REUSEADDR套接字选项允许服务器在 TIME_WAIT 状态下立即重启并绑定同一个端口常用于开发调试或需要快速重启的服务。2、解决TIME_WAIT状态引起的bind失败的方法在 server 的 TCP 连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的 服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是每秒都有很大数量的客户端来请求). 这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃, 就需要被服务器端主动清理掉),就会产生大量TIME_WAIT连接. 由于我们的请求量很大, 就可能导致TIME_WAIT的连接数很多, 每个连接都会占用一个通信五元组(源ip, 源端口, 目的ip, 目的端口, 协议). 其中服务器的ip和端口和协议是固定的. 如果新来的客户端连接的ip和端口号和TIME_WAIT占用的链接重复了, 就会出现问题. 使用 setsockopt ()设置 socket 描述符的 选项 SO_REUSEADDR 为 1 , 表示允许创建端口号相同但IP地址不同的多个 socket 描述符四次挥手之后主动断开连接的一方进入time_wait状态被动断开立即释放链接。3、滑动窗口这种一次发一个太低效了我们可以如上图一样一次性发送多组数据。可以大大的提高性能。窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是 4000 个字节 (四个段). ・发送前四个段的时候不需要等待任何 ACK, 直接发送 ・收到第一个 ACK 后滑动窗口向后移动继续发送第五个段的数据依次类推 ・操作系统内核为了维护这个滑动窗口需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答只有确认应答过的数据才能从缓冲区删掉 ・窗口越大则网络的吞吐率就越高情况1数据包已经到达但是ACK丢失了可以通过后续的ACK进行确认。情况2数据报直接丢失了当某一段报文段丢失之后发送端会一直收到 1001 这样的 ACK, 就像是在提醒发送端 我想要的是 1001 一样 如果发送端主机连续三次收到了同样一个 1001 这样的应答就会将对应的数据 1001 - 2000 重新发送 这个时候接收端收到了 1001 之后再次返回的 ACK 就是 7001 了 (因为 2001‑7000) 接收端其实之前就已经收到了被放到了接收端操作系统内核的接收缓冲区中 这种机制被称为 高速重发控制(也叫 快重传).以下这段为AI生成辅助记忆快重传一步一步完整拆解先铺垫最基础规则接收方的 ACK 含义ACK X→X 之前所有字节我收到了我现在最想要 X 号字节举例子 字节1‑10001001‑20002001‑30003001‑40004001‑5000场景开始丢包发生发送方一口气发出 5 段数据第一段1‑1000 ✅顺利到达接收方第二段1001‑2000 ❌在路上丢了没送到接收方第三段2001‑3000 ✅送到了第四段3001‑4000 ✅送到了第五段4001‑5000 ✅送到了重点第三、四、五段虽然到了但是顺序乱了失序。接收方想要的 1001 还没来后面的数据就算收到了也不能向上交给应用。接收方怎么做最关键TCP 规定每当收到一个失序报文立刻重复发送一次 ACK告诉发送方我想要 1001收到 1‑1000 → 返回ACK1001正常的第一次1001‑2000 丢了收不到收到 2001‑3000失序→ 再次发ACK1001重复 ACK #1收到 3001‑4000失序→ 再次发ACK1001重复 ACK #2收到 4001‑5000失序→ 再次发ACK1001重复 ACK #3 发送方现在收到了3 个一模一样的重复 ACK1001发送方触发快重传规则收到 3 次重复 ACK不等超时定时器到期立刻重传丢失的报文 1001‑2000这一步就是快重传。对比老方案【超时重传】发送方只能干等着超时计时器走完漫长的时间才能重传。速度很慢。 快重传不需要等靠重复 ACK 快速发现丢包。重传之后发生什么发送方重新发出1001‑2000 这一次成功到达接收方。 现在接收方手里1‑5000 全部集齐顺序完整 于是接收方返回一个新的、大的 ACKACK5001意思1‑5000 字节全部收到我接下来想要 5001数字流程一览表格事件ACK 返回值收到 1‑1000ACK1001原始收到 2001‑3000ACK1001重复 1收到 3001‑4000ACK1001重复 2收到 4001‑5000ACK1001重复 3发送方收到 3 次重复 ACK →重传 1001‑2000收到重传的 1001‑2000ACK5001全新确认高频坑点90% 同学这里出错3 次重复 ACK不算最开始正常的那一个 ACK总共收到4 个 ACK1001其中后面 3 个是重复冗余 ACK只重传丢失的那一段不会把窗口里所有数据全部重发本例只重传 1001‑2000不是重发后面 2001‑5000。快重传 ≠ 快恢复快重传负责快速把丢了的数据重新发一遍解决丢包快恢复紧跟着快重传调整拥塞窗口 cwnd 大小拥塞控制超时重传 VS 快重传对比超时重传丢包→没人发重复 ACK→定时器倒计时结束→重传。等待时间很长并且cwnd1窗口暴跌。快重传丢包→收到 3 次重复 ACK→立刻重传不等超时。配套快恢复窗口不会降到 1ssthresh减半大白话口诀背诵一段报文路上丢后面报文抢先到。 接收不停发旧 ACK一连三遍把信捎。 发送收到三个复不等超时立刻抛。4、流量控制接 收 端 处 理 数据 的 速 度 是 有 限 的. 如 果 发 送 端 发 的 太 快 , 导 致 接 收 端 的 缓 冲 区 被 打 满 , 这 个 时 候 如 果 发 送 端 继 续 发 送 , 就 会 造 成 丢 包 , 继 而 引 起 丢 包 重 传 等 等 一 系 列 连 锁 反 应 . 因 此 T C P 支 持 根 据 接 收 端 的 处 理 能 力 , 来 决 定 发 送 端 的 发 送 速 度 . 这 个 机 制 就 叫 做 流 量 控 制 ( F l o w C o n t r o l ) ; • 接 收 端 将 自 己 可 以 接 收 的 缓 冲 区 剩 余 空 间 大 小 放 入 T C P 首 部 中 的 窗 口 大 小 字 段 , 通 过 A C K 端 通 知 发 送 端 ; • 窗 口 大 小 字 段 越 大 , 说 明 网 络 的 吞 吐 量 越 高 ; • 接 收 端 一 旦 发 现 自 己 的 缓 冲 区 快 满 了 , 就 会 将 窗 口 大 小 设 置 成 一 个 更 小 的 值 通 知 给 发 送 端 ; • 发 送 端 接 受 到 这 个窗 口 之 后 , 就 会 减 慢 自 己 的 发 送 速 度 ;如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端.接收端如何把窗口大小告诉发送端呢回忆我们的 TCP 首部中有一个 16 位窗口字段就是存放了窗口大小信息 那么问题来了16 位数字最大表示 65535, 那么 TCP 窗口最大就是 65535 字节么 实际上TCP 首部 40 字节选项中还包含了一个窗口扩大因子 M, 实际窗口大小是 窗口字段的值左移 M 位
返回列表