万字长文 | 23 个问题 TCP 疑难杂症全解析(中)

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
全局流量管理 GTM,标准版 1个月
简介: 万字长文 | 23 个问题 TCP 疑难杂症全解析(中)

挥手一定需要四次吗?


假设 client 已经没有数据发送给 server 了,所以它发送 FIN 给 server 表明自己数据发完了,不再发了,如果这时候 server 还是有数据要发送给 client 那么它就是先回复 ack ,然后继续发送数据。


等 server 数据发送完了之后再向 client 发送 FIN 表明它也发完了,然后等 client 的 ACK 这种情况下就会有四次挥手。


那么假设 client 发送 FIN 给 server 的时候 server 也没数据给 client,那么 server 就可以将 ACK 和它的 FIN 一起发给client ,然后等待 client 的 ACK,这样不就三次挥手了?


为什么要有 TIME_WAIT?


断开连接发起方在接受到接受方的 FIN 并回复 ACK 之后并没有直接进入 CLOSED 状态,而是进行了一波等待,等待时间为 2MSL。


MSL 是 Maximum Segment Lifetime,即报文最长生存时间,RFC 793 定义的 MSL 时间是 2 分钟,Linux 实际实现是 30s,那么 2MSL 是一分钟。


那么为什么要等 2MSL 呢?

  • 就是怕被动关闭方没有收到最后的 ACK,如果被动方由于网络原因没有到,那么它会再次发送 FIN, 此时如果主动关闭方已经 CLOSED 那就傻了,因此等一会儿。
  • 假设立马断开连接,但是又重用了这个连接,就是五元组完全一致,并且序号还在合适的范围内,虽然概率很低但理论上也有可能,那么新的连接会被已关闭连接链路上的一些残留数据干扰,因此给予一定的时间来处理一些残留数据。


等待 2MSL 会产生什么问题?


如果服务器主动关闭大量的连接,那么会出现大量的资源占用,需要等到 2MSL 才会释放资源。


如果是客户端主动关闭大量的连接,那么在 2MSL 里面那些端口都是被占用的,端口只有 65535 个,如果端口耗尽了就无法发起送的连接了,不过我觉得这个概率很低,这么多端口你这是要建立多少个连接?


如何解决 2MSL 产生的问题?


快速回收,即不等 2MSL 就回收, Linux 的参数是 tcp_tw_recycle,还有 tcp_timestamps 不过默认是打开的。


其实上面我们已经分析过为什么需要等 2MSL,所以如果等待时间果断就是出现上面说的那些问题。

所以不建议开启,而且 Linux 4.12 版本后已经咔擦了这个参数了。

前不久刚有位朋友在群里就提到了这玩意。


image.png

一问果然有 NAT 的身影。

现象就是请求端请求服务器的静态资源偶尔会出现 20-60 秒左右才会有响应的情况,从抓包看请求端连续三个 SYN 都没有回应。

比如你在学校,对外可能就一个公网 IP,然后开启了 tcp_tw_recycle(tcp_timestamps 也是打开的情况下),在 60 秒内对于同源 IP 的连接请求中 timestamp 必须是递增的,不然认为其是过期的数据包就会丢弃。

学校这么多机器,你无法保证时间戳是一致的,因此就会出问题。

所以这玩意不推荐使用。


image.png


重用,即开启 tcp_tw_reuse 当然也是需要 tcp_timestamps 的。

这里有个重点,tcp_tw_reuse 是用在连接发起方的,而我们的服务端基本上是连接被动接收方


tcp_tw_reuse 是发起新连接的时候,可以复用超过 1s 的处于 TIME_WAIT 状态的连接,所以它压根没有减少我们服务端的压力。


它重用的是发起方处于 TIME_WAIT 的连接


这里还有一个 SO_REUSEADDR ,这玩意有人会和 tcp_tw_reuse 混为一谈,首先 tcp_tw_reuse 是内核选项而 SO_REUSEADDR 是用户态选项。


然后 SO_REUSEADDR 主要用在你启动服务的时候,如果此时的端口被占用了并且这个连接处于 TIME_WAIT 状态,那么你可以重用这个端口,如果不是 TIME_WAIT,那就是给你个 Address already in use。


所以这两个玩意好像都不行,而且 tcp_tw_reuse 和tcp_tw_recycle,其实是违反 TCP 协议的,说好的等我到天荒地老,你却偷偷放了手?


image.png

要么就是调小 MSL 的时间,不过也不太安全,要么调整 tcp_max_tw_buckets 控制 TIME_WAIT 的数量,不过默认值已经很大了 180000,这玩意应该是用来对抗 DDos 攻击的。


所以我给出的建议是服务端不要主动关闭,把主动关闭方放到客户端。毕竟咱们服务器是一对很多很多服务,我们的资源比较宝贵。


自己攻击自己


还有一个很骚的解决方案,我自己瞎想的,就是自己攻击自己。

Socket 有一个选项叫 IP_TRANSPARENT ,可以绑定一个非本地的地址,然后服务端把建连的 ip 和端口都记下来,比如写入本地某个地方。


然后启动一个服务,假如现在服务端资源很紧俏,那么你就定个时间,过了多久之后就将处于 TIME_WAIT 状态的对方 ip 和端口告诉这个服务。



然后这个服务就利用 IP_TRANSPARENT 伪装成之前的那个 client 向服务端发起一个请求,然后服务端收到会给真的 client 一个 ACK, 那 client 都关了已经,说你在搞啥子,于是回了一个 RST,然后服务端就中止了这个连接。


image.png

超时重传机制是为了解决什么问题?


前面我们提到 TCP 要提供可靠的传输,那么网络又是不稳定的如果传输的包对方没收到却又得保证可靠那么就必须重传。


TCP 的可靠性是靠确认号的,比如我发给你1、2、3、4这4个包,你告诉我你现在要 5 那说明前面四个包你都收到了,就是这么回事儿。


不过这里要注意,SeqNum 和 ACK 都是以字节数为单位的,也就是说假设你收到了1、2、4 但是 3 没有收到你不能 ACK 5,如果你回了 5 那么发送方就以为你5之前的都收到了。


所以只能回复确认最大连续收到包,也就是 3。

而发送方不清楚 3、4 这两个包到底是还没到呢还是已经丢了,于是发送方需要等待,这等待的时间就比较讲究了。


如果太心急可能 ACK 已经在路上了,你这重传就是浪费资源了,如果太散漫,那么接收方急死了,这死鬼怎么还不发包来,我等的花儿都谢了。


所以这个等待超时重传的时间很关键,怎么搞?聪明的小伙伴可能一下就想到了,你估摸着正常来回一趟时间是多少不就好了,我就等这么长。


这就来回一趟的时间就叫 RTT,即 Round Trip Time,然后根据这个时间制定超时重传的时间 RTO,即 Retransmission Timeout。


不过这里大概只好了 RTO 要参考下 RTT ,但是具体要怎么算?首先肯定是采样,然后一波加权平均得到 RTO。

RFC793 定义的公式如下:

1、先采样 RTT

2、SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT) 3、RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]]

ALPHA 是一个平滑因子取值在 0.80.9之间,UBOUND 就是超时时间上界-1分钟,LBOUND 是下界-1秒钟,BETA 是一个延迟方差因子,取值在 1.32.0。


但是还有个问题,RTT 采样的时间用一开始发送数据的时间到收到 ACK 的时间作为样本值还是重传的时间到 ACK 的时间作为样本值?


image.png从图中就可以看到,一个时间算长了,一个时间算短了,这有点难,因为你不知道这个  ACK 到底是回复谁的。


所以怎么办?发生重传的来回我不采样不就好了,我不知道这次 ACK 到底是回复谁的,我就不管他,我就采样正常的来回。


这就是 Karn / Partridge 算法,不采样重传的RTT。


但是不采样重传会有问题,比如某一时刻网络突然就是很差,你要是不管重传,那么还是按照正常的 RTT 来算 RTO, 那么超时的时间就过短了,于是在网络很差的情况下还疯狂重传加重了网络的负载。


因此 Karn 算法就很粗暴的搞了个发生重传我就将现在的 RTO 翻倍,哼!就是这么简单粗暴。


image.png

但是这种平均的计算很容易把一个突然间的大波动,平滑掉,所以又搞了个算法,叫 Jacobson / Karels Algorithm。


它把最新的 RTT 和平滑过的 SRTT 做了波计算得到合适的 RTO,公式我就不贴了,反正我不懂,不懂就不哔哔了。




相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
6月前
|
Web App开发 网络协议 关系型数据库
深度解析TCP协议:特点、应用场景及市面上常见软件案例
深度解析TCP协议:特点、应用场景及市面上常见软件案例
631 1
深度解析TCP协议:特点、应用场景及市面上常见软件案例
|
6月前
|
网络协议 算法 数据可视化
TCP/IP与ISO/OSI模型的差异解析
TCP/IP与ISO/OSI模型的差异解析
162 0
|
6月前
|
网络协议 算法 安全
TCP/UDP 数据传输的链路解析
TCP/UDP 数据传输的链路解析
123 0
|
6月前
|
运维 监控 网络协议
QT实现TCP通信:从基础到高级的全面解析
QT实现TCP通信:从基础到高级的全面解析
803 0
|
6月前
|
域名解析 网络协议
IP协议, TCP协议 和DNS 服务分别是干什么的?
IP协议, TCP协议 和DNS 服务分别是干什么的?
271 0
|
21天前
|
域名解析 缓存 网络协议
浏览器中输入URL返回页面过程(超级详细)、DNS域名解析服务,TCP三次握手、四次挥手
浏览器中输入URL返回页面过程(超级详细)、DNS域名解析服务,TCP三次握手、四次挥手
|
2月前
|
运维 网络协议
深入解析TCP三次握手与四次挥手:建立与断开连接的关键过程
深入解析TCP三次握手与四次挥手:建立与断开连接的关键过程
202 0
|
4月前
|
网络协议 算法 程序员
提高网络稳定性的关键:TCP滑动窗口与拥塞控制解析
**TCP可靠传输与拥塞控制概要:** 小米讲解TCP如何确保数据可靠性。TCP通过分割数据、编号段、校验和、流量控制(滑动窗口)和拥塞控制(慢开始、拥塞避免、快重传、快恢复)保证数据安全传输。拥塞控制动态调整窗口大小,防止网络过载,提升效率。当连续收到3个相同ACK时执行快重传,快恢复避免剧烈波动。关注“软件求生”获取更多技术内容。
139 4
提高网络稳定性的关键:TCP滑动窗口与拥塞控制解析
|
3月前
|
域名解析 网络协议 Linux
在Linux中,我们都知道,dns采用了tcp协议,又采用了udp协议,什么时候采用tcp协议?什么 时候采用udp协议?为什么要这么设计?
在Linux中,我们都知道,dns采用了tcp协议,又采用了udp协议,什么时候采用tcp协议?什么 时候采用udp协议?为什么要这么设计?
|
4月前
|
网络协议 程序员
TCP报文格式全解析:网络小白变高手的必读指南
**TCP报文格式详解摘要** 探索TCP,传输层的关键协议,提供可靠数据传输。报文含源/目的端口(标识应用),32位序号(跟踪字节顺序),确认序号(确认接收),4位首部长度,6位标志(URG, ACK, PSH, RST, SYN, FIN),窗口大小(流量控制),检验和(数据完整性),紧急指针(优先数据)及可变长选项(如MSS, 时间戳)。了解这些字段,能更好地理解TCP连接的建立、管理和数据交换。
334 3

推荐镜像

更多
下一篇
无影云桌面