TCP实战一(三握四挥、流量控制)
上一篇博文已经介绍了tcpdump的一些基本操作与命令,今天这篇博文将带你解密如何利用wireshark对tcpdump抓到的数据包进行可视化分析!
参考文献:https://zhuanlan.zhihu.com/p/142665708
目录
1.Wireshark可视化分析ping过程(分析ICMP协议)
2.Wireshark可视化分析TCP三次握手以及四次挥手过程
3.TCP三次握手异常情况实战分析
4.Wireshark可视化分析TCP流量控制
本篇博文的精华:
Q1:TCP 第一次握手 SYN 丢包,客户端的操作是什么?
通过实验一的实验结果,我们可以得知,当客户端发起的 TCP 第一次握手 SYN 包,如果在初始RTO内没收到服务端的 ACK,就会重传 SYN 数据包,通常情况下初始RTO的值都为1,随后,每次的RTO值都是前一次的2倍,直到 SYN 包的重传次数到达 tcp_syn_retries
值后,客户端不再发送 SYN 包。(但是通常情况下你也不能把tcp_syn_retries
的值设置的过大,博主尝试过当此值设置为8以上时,效果都和8一样)
Q2:TCP 第二次握手 SYN、ACK 丢包,服务端与客户端分别进行什么操作?
通过实验二的实验结果,我们可以得知,当第二次握手的 SYN、ACK 丢包时,客户端会超时重发 SYN 包,服务端也会发送两次SYN+ACK 包,第一个 SYN+ACK 数据包是响应客户端超时重传的 SYN 数据包,第二个SYN+ACK 数据包则是超时重传的。
客户端 SYN 包超时重传的最大次数,是由 tcp_syn_retries 决定的,默认值是 5 次;服务端 SYN、ACK 包时重传的最大次数,是由 tcp_synack_retries 决定的,默认值是 5 次。且每次的 RTO 都是上一次的2倍。
参考:RTO的理解以及计算方法
Q3:TCP 第三次握手 ACK 丢包,客户端和服务端分别有什么操作
在建立 TCP 连接时,如果第三次握手的 ACK,服务端无法收到,则服务端就会短暂处于 SYN_RECV
状态,而客户端会处于 ESTABLISHED
状态。
由于服务端一直收不到 TCP 第三次握手的 ACK,则会一直重传 SYN、ACK 包,直到重传次数超过 tcp_synack_retries
值(默认值 5 次)后,服务端就会断开 TCP 连接。
而客户端则会有两种情况:
- 如果客户端没发送数据包,一直处于
ESTABLISHED
状态,然后经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接,于是客户端连接就会断开连接。 - 如果客户端发送了数据包,一直没有收到服务端对该数据包的确认报文,则会一直重传该数据包,直到重传次数超过
tcp_retries2
值(默认值 15 次)后,客户端就会断开 TCP 连接。
1.Wireshark可视化分析ping过程(分析ICMP协议)
Wireshark 除了可以抓包外,还提供了可视化分析网络包的图形页面,同时,还内置了一系列的汇总分析工具。
首先,我们以 ping 例子来说,我们可以使用下面的命令,把抓取的数据包保存到 tcpdata.pcap 文件
tcpdump -nn -v -c icmp -w /home/xx/桌面/tcpdata.pcap
接着把 ping.pcap 文件拖到电脑,再用 Wireshark 打开它。打开后,你就可以看到下面这个界面:
是吧?在 Wireshark 的页面里,可以更加直观的分析数据包,不仅展示各个网络包的头部信息,还会用不同的颜色来区分不同的协议,由于这次抓包只有 ICMP 协议,所以只有紫色的条目。
接着,在网络包列表中选择某一个网络包后,在其下面的网络包详情中,可以更清楚的看到,这个网络包在协议栈各层的详细信息。比如,以编号 1 的网络包为例子:
- 可以在数据链路层,看到 MAC 包头信息,如源 MAC 地址和目标 MAC 地址等字段;
- 可以在 IP 层,看到 IP 包头信息,如源 IP 地址和目标 IP 地址、TTL、IP 包长度、协议等 IP 协议各个字段的数值和含义;
- 可以在 ICMP 层,看到 ICMP 包头信息,比如 Type、Code 等 ICMP 协议各个字段的数值和含义;
以下这张图是IP头部信息与ICMP头部信息,可以与上图对照进行分析
Wireshark 用了分层的方式,展示了各个层的包头信息,把“不可见”的数据包,清清楚楚的展示了给我们,从 ping 的例子中,我们可以看到网络分层就像有序的分工,每一层都有自己的责任范围和信息,上层协议完成工作后就交给下一层,最终形成一个完整的网络包。
2.Wireshark可视化分析TCP三次握手以及四次挥手过程
既然学会了 tcpdump 和 Wireshark 两大网络分析利器,那我们快马加鞭,接下用它俩抓取和分析 HTTP 协议网络包,并理解 TCP 三次握手和四次挥手的工作原理。
本次例子,我们将要访问的依旧是 www.baidu.com 。在终端一用 tcpdump 命令抓取数据包,并利用curl命令访问www.baidu.com网址。
在tcpdump抓取数据包的终端中按下 Ctrl+C 停止 tcpdump,并把得到的 http_info.pcap 取出到电脑。
使用 Wireshark 打开 http_info.pcap 后,你就可以在 Wireshark 中,看到如下的界面:
我们都知道 HTTP 是基于 TCP 协议进行传输的,那么:
- 最开始的 3 个包就是 TCP 三次握手建立连接的包
- 中间是 HTTP 请求和响应的包
- 而最后的 4 个包则是 TCP 断开连接的挥手包
Wireshark 可以用时序图的方式显示数据包交互的过程,从菜单栏中,点击 统计 (Statistics) -> 流量图 (Flow Graph),然后,在弹出的界面中的「流量类型」选择 「TCP Flows」,你可以更清晰的看到,整个过程中 TCP 流的执行过程:
为什么三次握手连接过程的 Seq 是 0 ?
其实是因为Wireshark 工具帮我们做了优化,它默认显示的是序列号 seq 是相对值,而不是真实值。
如果你想看到实际的序列号的值,可以右键菜单, 然后找到「协议首选项」,接着找到「Relative Seq」后,把它给取消。
取消后的流量图如下(也就是真实的seq值):
可见,客户端和服务端的序列号实际上是不同的,并且序列号初始是一个随机值。这其实跟我们书上看到的 TCP 三次握手和四次挥手基本上是一致的。
有关tcp三次握手、四次挥手可以查看我的博客:备战研发岗—计算机网络。
在此以图的形式简单回顾一下整个过程:
为什么有些情况下抓到的 TCP 挥手是三次,而不是书上说的四次?例如:
因为服务器端收到客户端的 FIN
后,服务器端同时也要关闭连接,这样就可以把 ACK
和 FIN
合并到一起发送,节省了一个包,变成了“三次挥手”。
而通常情况下,服务器端收到客户端的 FIN
后,很可能还没发送完数据,所以就会先回复客户端一个 ACK
包,稍等一会儿,完成所有数据包的发送后,才会发送 FIN
包,这也就是四次挥手了。
3.TCP三次握手异常情况实战分析
TCP三次握手的过程也许大伙都背的滚瓜烂熟了,打你有没有想过在三次握手的过程中可能会发生一下几个问题:
- TCP 第一次握手的 SYN 丢包了,会发生了什么?
- TCP 第二次握手的 SYN、ACK 丢包了,会发生什么?
- TCP 第三次握手的 ACK 包丢了,会发生什么?
不就是丢包嘛,重传数据包不就好了嘛,真的是这么简单吗?那你能回答出以下的问题吗
- 那会重传几次?
- 超时重传的时间 RTO 会如何变化?
- 在 Linux 下如何设置重传次数?
- ....
接下来用三个实验案例带你解密整个过程,帮助你全面剖析TCP三次握手。
实验环境
在VMware上部署两个虚拟机,一台当作客户机,一台当作服务器。两台虚拟机关系如下图:
- 客户端和服务端都基于 CentOs 6.5 Linux,Linux 内核版本 2.6.32
- 服务端 192.168.127.150,apache web 服务
- 客户端 192.168.127.151
- curl是一个命令行工具,通过指定的URL来上传或下载数据,并将数据展示出来。curl中的
c
表示client,而URL,就是URL. - Telnet协议是TCP/IP协议家族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。Telnet是常用的远程控制Web服务器的方法。
有些人可能不懂如何在服务端部署apache web服务,详情见链接:CentOS6.5环境下搭建Apache httpd服务器
3.1 实验一:TCP 第一次握手 SYN 丢包
为了模拟 TCP 第一次握手 SYN 丢包的情况,我的步骤如下:
- 客户端运行tcpdump命令,并利用curl指令成功连上服务端。
- 切换到服务端,立马拔掉服务端的网线(可以采用ifdown 网卡名的命令进行操作)
- 最后切换到客户端,再次运行curl指令
从上图可以看出centos6.5(内核版本 2.6.32)参数tcp_syn_retries的默认值为5,为了实验效果我们把该值设置为6。
按照上述的实验步骤,最终得到的实验结果如下图所示:
从上图可以发现, 客户端发起了 SYN 包后,一直没有收到服务端的 ACK ,基于超时重传机制所以客户端根据tcp_syn_retries的值(6)重传了 6 次syn数据包(一共包含7个Flags[S],其中第一个是正常发送SYN数据包,后面6个为超时重传发送的syn数据包),并且每次重传syn数据包的间隔时间是不同的并且是有规律的:1,2,4,8,16,32.centos6.5默认RTO(Retransmission Timeout)的初始值为1,
RTO指的是,发送数据包在一定的时间周期内没有收到相应的ACK,等待一定的时间,超时之后就认为这个数据包丢失,就会重新发送。这个等待时间被称为RTO.
随后,每次的RTO值都是前一次的2倍。当超过最大重传次数后,客户端不再发送 SYN 包。
第六次发送syn数据包,需要等待64s才知道该连接也超时了,此时重发次数已达到tcp_syn_retries的值,所以最后在21:56:14(发送第一个syn数据包的时间) + (1+2+4+8+16+32+64) = 21: 58:21curl命令返回连接失败。
实验一的实验小结
Q:TCP 第一次握手 SYN 丢包,客户端的操作是什么?
通过实验一的实验结果,我们可以得知,当客户端发起的 TCP 第一次握手 SYN 包,如果在初始RTO内没收到服务端的 ACK,就会重传 SYN 数据包,通常情况下初始RTO的值都为1,随后,每次的RTO值都是前一次的2倍,直到 SYN 包的重传次数到达 tcp_syn_retries
值后,客户端不再发送 SYN 包。(但是通常情况下你也不能把tcp_syn_retries
的值设置的过大,博主尝试过当此值设置为8以上时,效果都和8一样)
记录一下:在没有翻墙的情况下,博主利用客户机curl命令访问外网例如谷歌、youtube等等,按理来说在tcp_syn_retries = 6的情况下,tcpdump指令应该抓到7个syn数据包,但是实验结果如下所示:
在tcp_syn_retries = 3的情况下,实验结果如下:
综上所述,当你在客户端利用curl命令链接外网,tcp_syn_retries的有效值最大为4。
- 意思就是如果你设置的tcp_syn_retries的值大于4,tcpdump命令只能抓到客户端重复发送4次syn数据包,并且发送完4次syn数据包后,客户端还会接收到服务端发送的RST+ACK包。
- 如果你设置的tcp_syn_retries的值小于4,则tcpdump命令就只会抓到客户端重复发送 tcp_syn_retries的值 次syn数据包,并且在距离第一次发送syn数据包21秒后,客户端接收到服务端发送的RST+ACK。包。
原因可能在于linux内核版本升级了,所以对此进行了优化(这是我的猜想)。
3.2 实验二:TCP 第二次握手 SYN、ACK 丢包
为了模拟客户端收不到服务端第二次握手 SYN、ACK 包,我的做法是在客户端加上防火墙限制,直接粗暴的把来自服务端的数据都丢弃,防火墙的配置如下:
接着在客户端运行tcpdump命令进行数据包的抓取
最后在客户端执行 curl 指令:
由上图date命令间隔可知:运行curl命令前后花了 63s curl报错退出了。有些人可能立马就会联想到63s不正是实验一客户端5次重发syn数据包的总时间吗(1+2+4+8+16)+32 = 63s.实验二不是探讨TCP 第二次握手 SYN、ACK 丢包吗,这与实验一有关系吗?不急,接下来就带你解密。
我们把客户端抓取的数据包利用Wireshark进行分析:
- 第1行:客户端成功向服务端发送syn数据包
- 第2行:Service成功接收到来自Client端发送syn数据包,所以向Client端发送 syn+ack 数据包
- 由于我们在客户端加上防火墙限制,直接粗暴的把来自服务端的数据都丢弃,所以Client端是收不到来自Service端的ACK+SYN数据包。
- 第3行:Client端又重新向Service端发送一个seq相同的syn数据包。
- 因为距离第一次发送syn数据包已经过去了1s,这1s正好满足RTO的初始值,此时Client端认为第一次发送syn数据包超时了,所以进行第一次超时重传。
- 第4行:在第2行Service端向Client端发送ACK+SYN数据包后,没有收到来自Client端的ACK数据包,所以选择了超时重传ACK+SYN数据包。
- 第5行:Client端超时重传的 SYN 包又抵达了服务端,服务端收到后,重传定时器就重新计时,然后向Client端回了 SYN、ACK 包。
- 所以出现了2次Service端向Client端发送ACK+SYN数据包。
- 第6-17行重复上述过程。
- 最后,Client端重发syn数据包达到了5次 (tcp_syn_retries 默认值 5 次),就不会再发送syn数据包了。
根据以上两张图片,可以发现
- 客户端发起 SYN 后,由于防火墙屏蔽了服务端的所有数据包,所以 curl 是无法收到服务端的 SYN、ACK 包,当发生超时后,就会重传 SYN 包
- 服务端收到客户的 SYN 包后,就会回 SYN、ACK 包,但是客户端一直没有回 ACK,服务端在超时后,重传了 SYN、ACK 包,接着一会,客户端超时重传的 SYN 包又抵达了服务端,服务端收到后,超时定时器就重新计时,然后回了 SYN、ACK 包,所以相当于服务端的超时定时器只触发了一次,又被重置了。
- 最后,客户端 SYN 超时重传次数达到了 5 次(tcp_syn_retries 默认值 5 次),就不再继续发送 SYN 包了。
所以,我们可以发现,当第二次握手的 SYN、ACK 丢包时,客户端会超时重发 SYN 包,服务端也会发送两次SYN+ACK 包,第一个 SYN+ACK 数据包是响应客户端超时重传的 SYN 数据包,第二个SYN+ACK 数据包则是超时重传的。
Q:客户端设置了防火墙,屏蔽了服务端的网络包,为什么 tcpdump 还能抓到服务端的网络包?
添加 iptables 限制后, tcpdump 是否能抓到包 ,这要看添加的 iptables 限制条件:
- 如果添加的是
INPUT
规则,则可以抓得到包 - 如果添加的是
OUTPUT
规则,则抓不到包
网络包进入主机后的顺序如下:
- 进来的顺序 Wire(网线) -> NIC(网卡) -> tcpdump -> netfilter/iptables(防火墙)
- 出去的顺序 iptables -> tcpdump -> NIC -> Wire
Q:tcp_syn_retries 是限制 SYN 重传次数,那第二次握手 SYN、ACK 限制最大重传次数是多少?
如图所示,TCP 第二次握手 SYN、ACK 包的最大重传次数默认值是 5
次。
为了验证 SYN、ACK 包最大重传次数是 5 次,我们继续做下实验,我们先把客户端的 tcp_syn_retries
设置为 1,表示客户端 SYN 最大超时次数是 1 次,目的是为了防止多次重传 SYN,把服务端 SYN、ACK 超时定时器重置。
接着,还是如上面的步骤:
- 客户端配置防火墙屏蔽服务端的数据包
- 客户端 tcpdump 抓取 curl 执行时的数据包
把抓取的数据包,用 Wireshark 打开分析,显示的时序图如下:
从上图,我们可以分析出:
- 客户端的 SYN 只超时重传了 1 次,因为
tcp_syn_retries
值为 1 - 服务端应答了客户端超时重传的 SYN 包后,由于一直收不到客户端的 ACK 包,所以服务端一直在超时重传 SYN、ACK 包,每次的 RTO 也是指数上涨的,一共超时重传了 5 次,因为
tcp_synack_retries
值为 5
接着,我把 tcp_synack_retries 设置为 2,tcp_syn_retries
依然设置为 1:
$ echo 2 > /proc/sys/net/ipv4/tcp_synack_retries
$ echo 1 > /proc/sys/net/ipv4/tcp_syn_retries
依然保持一样的实验步骤进行操作,接着把抓取的数据包,用 Wireshark 打开分析,显示的时序图如下:
可见:
- 客户端的 SYN 包只超时重传了 1 次,符合 tcp_syn_retries 设置的值;
- 服务端的 SYN、ACK 超时重传了 2 次,符合 tcp_synack_retries 设置的值
实验二的实验小结
Q:TCP 第二次握手 SYN、ACK 丢包,服务端与客户端分别进行什么操作?
通过实验二的实验结果,我们可以得知,当第二次握手的 SYN、ACK 丢包时,客户端会超时重发 SYN 包,服务端也会发送两次SYN+ACK 包,第一个 SYN+ACK 数据包是响应客户端超时重传的 SYN 数据包,第二个SYN+ACK 数据包则是超时重传的。
客户端 SYN 包超时重传的最大次数,是由 tcp_syn_retries 决定的,默认值是 5 次;服务端 SYN、ACK 包时重传的最大次数,是由 tcp_synack_retries 决定的,默认值是 5 次。且每次的 RTO 都是上一次的2倍。
参考:RTO的理解以及计算方法
3.3 实验三:TCP 第三次握手 ACK 丢包
为了模拟 TCP 第三次握手 ACK 包丢,我的实验方法是在服务端配置防火墙,屏蔽客户端 TCP 报文中标志位是 ACK 的包,也就是当服务端收到客户端的 TCP ACK 的报文时就会丢弃,iptables 配置命令如下:
接着,在客户端执行如下 tcpdump 命令:
然后,客户端向服务端发起 telnet,因为 telnet 命令是会发起 TCP 连接,所以用此命令做测试:
此时,由于服务端收不到第三次握手的 ACK 包,所以一直处于 SYN_RECV
状态:
而客户端是已完成 TCP 连接建立,处于 ESTABLISHED
状态:
过了 一阵子,观察发现服务端的 TCP 连接不见了:
但是客户端依然还是处于 ESTABLISHED
状态:
接着,在刚才客户端建立的 telnet 会话,输入 123456 字符,进行发送:
持续「好长」一段时间,客户端的 telnet 才断开连接:
以上就是本次的实现三的现象,这里存在两个疑点:
- 为什么服务端原本处于
SYN_RECV
状态的连接,过 一阵子后就消失了? - 为什么客户端 telnet 输入 123456 字符后,过了好长一段时间,telnet 才断开连接?
不着急,我们把刚抓的数据包,用 Wireshark 打开分析,显示的时序图如下:
上图的流程如下:
- 客户端发送 SYN 包给服务端,服务端收到后,回了个 SYN、ACK 包给客户端,此时服务端的 TCP 连接处于
SYN_RECV
状态; - 客户端收到服务端的 SYN、ACK 包后,给服务端回了个 ACK 包,此时客户端的 TCP 连接处于
ESTABLISHED
状态; - 由于服务端配置了防火墙,屏蔽了客户端的 ACK 包,所以服务端一直处于
SYN_RECV
状态,没有进入ESTABLISHED
状态,tcpdump 之所以能抓到客户端的 ACK 包,是因为数据包进入系统的顺序是先进入 tcpudmp,后经过 iptables; - 接着,服务端超时重传了 SYN、ACK 包,重传了 5 次后,也就是超过 tcp_synack_retries 的值(默认值是 5),然后就没有继续重传了,此时服务端的 TCP 连接主动中止了,所以刚才处于 SYN_RECV 状态的 TCP 连接断开了,而客户端依然处于
ESTABLISHED
状态; - 虽然服务端 TCP 断开了,但过了一段时间,发现客户端依然处于
ESTABLISHED
状态,于是就在客户端的 telnet 会话输入了 123456 字符; - 此时由于服务端已经断开连接,客户端发送的数据报文,一直在超时重传,每一次重传,RTO 的值是指数增长的,所以持续了好长一段时间,客户端的 telnet 才报错退出了,此时共重传了 15 次。
通过这一波分析,刚才的两个疑点已经解除了:
- 服务端在重传 SYN、ACK 包时,超过了最大重传次数
tcp_synack_retries
,于是服务端的 TCP 连接主动断开了。 - 客户端向服务端发送数据包时,由于服务端的 TCP 连接已经退出了,所以数据包一直在超时重传,共重传了 15 次, telnet 就 断开了连接。
Q:TCP 第一次握手的 SYN 包超时重传最大次数是由 tcp_syn_retries 指定,TCP 第二次握手的 SYN、ACK 包超时重传最大次数是由 tcp_synack_retries 指定,那 TCP 建立连接后的数据包最大超时重传次数是由什么参数指定呢?
TCP 建立连接后的数据包传输,最大超时重传次数是由 tcp_retries2
指定,默认值是 15 次,如下:
$ cat /proc/sys/net/ipv4/tcp_retries2
15
如果 15 次重传都做完了,TCP 就会告诉应用层说:“搞不定了,包怎么都传不过去!”
Q:那如果客户端不发送数据,什么时候才会断开处于 ESTABLISHED 状态的连接?
这里就需要利用到TCP的保活机制,保活机制的原理如下:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个「探测报文」,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
在Centos6.5(linux内核版本2.6)可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值:
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
- tcp_keepalive_time = 7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制
- tcp_keepalive_intvl = 75:表示每次检测间隔 75 秒;
- tcp_keepalive_probes = 9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。
也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。
这个时间是有点长的,所以如果我抓包足够久,或许能抓到探测报文。
实验三小结:
Q:TCP 第三次握手 ACK 丢包,客户端和服务端分别有什么操作
在建立 TCP 连接时,如果第三次握手的 ACK,服务端无法收到,则服务端就会短暂处于 SYN_RECV
状态,而客户端会处于 ESTABLISHED
状态。
由于服务端一直收不到 TCP 第三次握手的 ACK,则会一直重传 SYN、ACK 包,直到重传次数超过 tcp_synack_retries
值(默认值 5 次)后,服务端就会断开 TCP 连接。
而客户端则会有两种情况:
- 如果客户端没发送数据包,一直处于
ESTABLISHED
状态,然后经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接,于是客户端连接就会断开连接。 - 如果客户端发送了数据包,一直没有收到服务端对该数据包的确认报文,则会一直重传该数据包,直到重传次数超过
tcp_retries2
值(默认值 15 次)后,客户端就会断开 TCP 连接。
4.Wireshark可视化分析TCP流量控制
TCP 为了防止发送方无脑的发送数据,导致接收方缓冲区被填满,所以就有了滑动窗口的机制,它可利用接收方的接收窗口来控制发送方一次性要发送的数据量,也就是流量控制。
接收窗口是由接收方指定的值,存储在 TCP 头部中,它可以告诉发送方自己的 TCP 缓冲空间区大小,这个缓冲区是给应用程序读取数据的空间:
- 如果应用程序读取了缓冲区的数据,那么缓冲空间区的就会把被读取的数据移除
- 如果应用程序没有读取数据,则数据会一直滞留在缓冲区。
假设接收方接收到数据后,应用层能很快的从缓冲区里读取数据,那么窗口大小会一直保持不变,过程如下:
但是现实中服务器会出现繁忙的情况,当应用程序读取速度慢,那么缓存空间会慢慢被占满,于是为了保证发送方发送的数据不会超过缓冲区大小,服务器则会调整窗口大小的值,接着通过 ACK 报文通知给对方,告知现在的接收窗口大小,从而控制发送方发送的数据大小。
零窗口通知与窗口探测
假设接收方处理数据的速度跟不上接收数据的速度,缓存就会被占满,从而导致接收窗口为 0,当发送方接收到零窗口通知时,就会停止发送数据。
如下图,可以接收方的窗口大小在不断的收缩至 0:
接着,发送方会定时发送窗口大小探测报文,以便及时知道接收方窗口大小的变化。
以下图 Wireshark 分析图作为例子说明:
- 发送方发送了数据包 1 给接收方,接收方收到后,由于缓冲区被占满,回了个零窗口通知;
- 发送方收到零窗口通知后,就不再发送数据了,直到过了
3.4
秒后,发送了一个 TCP Keep-Alive 报文,也就是窗口大小探测报文; - 当接收方收到窗口探测报文后,就立马回一个窗口通知,但是窗口大小还是 0;
- 发送方发现窗口还是 0,于是继续等待了
6.8
(翻倍) 秒后,又发送了窗口探测报文,接收方依然还是回了窗口为 0 的通知; - 发送方发现窗口还是 0,于是继续等待了
13.5
(翻倍) 秒后,又发送了窗口探测报文,接收方依然还是回了窗口为 0 的通知;
可以发现,这些窗口探测报文以 3.4s、6.5s、13.5s 的间隔出现,说明超时时间会翻倍递增。
这连接暂停了 25s,想象一下你在打王者的时候,25s 的延迟你还能上王者吗?
发送窗口的分析
(1) 在 Wireshark 看到的 Windows size 也就是 " win = ",这个值表示发送窗口吗?
这不是发送窗口,而是在向对方声明自己的接收窗口。
(2) 如何在包里看出发送窗口的大小?
很遗憾,没有简单的办法,发送窗口虽然是由接收窗口决定,但是它又可以被网络因素影响,也就是拥塞窗口,实际上发送窗口是值是 min(拥塞窗口,接收窗口)。
(3) 发送窗口和 MSS 有什么关系?MSS(Maximum Segment Size)最大报文段长度:表示TCP报文段中的数据字段的最大长度
发送窗口决定了一口气能发多少字节,而 MSS 决定了这些字节要分多少包才能发完。
举个例子,如果发送窗口为 16000 字节的情况下,如果 MSS 是 1000 字节,那就需要发送 1600/1000 = 16 个包。
(4) 发送方在一个窗口发出 n 个包,是不是需要 n 个 ACK 确认报文?
不一定,因为 TCP 有累计确认机制,所以当收到多个数据包时,只需要应答最后一个数据包的 ACK 报文就可以了。
TCP实战一(三握四挥、流量控制)的更多相关文章
- 深入理解TCP三握四挥
面试中被问到不少次TCP的三握四挥,今天特意来做一个总结(一些资料是很久前找的,忘了参考的链接了) 一.三次握手 首先来看一张图 最初,客户机A与服务器B的TCP进程都处于 CLOSED 状态. 然后 ...
- 详解TCP三握四挥
TCP握手协议 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接.第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确 ...
- Android项目实战(三十四):蓝牙4.0 BLE 多设备连接
最近项目有个需求,手机设备连接多个蓝牙4.0 设备 并获取这些设备的数据. 查询了很多资料终于实现,现进行总结. ------------------------------------------- ...
- wireshark数据包分析实战 第三、四章
1,wireshark支持的协议上千种,开源的. 2,wireshark需要winpcap驱动支持.winpcap驱动的作用通过操作系统捕捉原始数据包.应用过滤器.将网卡切换为混杂模式. 3,捕获文件 ...
- 由一次线上故障来理解下 TCP 三握、四挥 & Java 堆栈分析到源码的探秘
本文导读: 生产故障场景介绍 TCP 建连三次握手过程 TCP 断连四次挥手过程 结合 Java 堆栈剖析源码 再从堆栈中找到"罪魁祸首" 问题优化方案总结 1.生产故障场景介绍 ...
- TCP 为什么是三次握手,而不是两次或四次?
记得第一次看TCP握手连接的时候,有同样的疑问,我的疑问是,为何不是两次呢?后来随着对网络的理解深入,明白TCP报文是交由IP网络来负责运输,IP网络并不能保证TCP报文到达目的地,既然IP网络是指望 ...
- TCP为什么做三次握手、四次挥手
TCP 为什么做三次握手.四次挥手? TCP 是为了解决可靠传输出现的.为了实现可靠性,TCP 做了流量控制.拥塞控制,并且在建立.关闭连接前做些机制:三次握手.四次挥手. 三次握手是为了让客户端.服 ...
- TCP/IP协议三次握手与四次握手流程解析
原文链接地址:http://www.2cto.com/net/201310/251896.html TCP/IP协议三次握手与四次握手流程解析 TCP/IP协议的详细信息参看<TCP/IP协议详 ...
- TCP连接的三次握手和四次解散过程
客户端和服务器在使用TCP连接传输数据的过程中,需要经过三次握手建立连接和四次握手断开连接操作. 具体如下图所示 上图描述了TCP连接从建立到断开的详细过程,以下就其中的具体报文细节展开讨论. 在TC ...
随机推荐
- String类的内存解析
package com.aff.equals; public class TestString { public static void main(String[] args) { String st ...
- 2020年,为什么我们应该使用abapGit代替SAPLink
SAPLink是一个帮助人们分享开发内容的工具.通过它,人们可以将ABAP开发对象从一个系统打包下载.再上传到另一个系统中.对于各种类型的开发者,它都可以起到作用: 有的开发者喜欢在不同的项目中复制相 ...
- ASP.NET Core Blazor Webassembly 之 数据绑定
上一次我们学习了Blazor组件相关的知识(Asp.net Core Blazor Webassembly - 组件).这次继续学习Blazor的数据绑定相关的知识.当代前端框架都离不开数据绑定技术. ...
- Rocket - diplomacy - LazyModule的组织方式
https://mp.weixin.qq.com/s/vaDUekxkFkOJLmzg5jCngw 简单介绍LazyModule/LazyModuleImp的组织方式. 1. LazyModule L ...
- Rocket - interrupts - Parameters
https://mp.weixin.qq.com/s/eD1_hG0n8W2Wodk25N5KnA 简单介绍interrupts相关的Parameters. 1. IntRange 定义一个中断号区间 ...
- 数据库之 MySQL --- 数据处理 之 单行函数、组函数 (四)
[1] LOWER : 将字符串中的内容全部转成小写 UPPER : 将字符串中的内容全部转成大写 SELECT LOWER ('abAcD') FROM DUAL SE ...
- Java实现 LeetCode 700 二叉搜索树中的搜索(遍历树)
700. 二叉搜索树中的搜索 给定二叉搜索树(BST)的根节点和一个值. 你需要在BST中找到节点值等于给定值的节点. 返回以该节点为根的子树. 如果节点不存在,则返回 NULL. 例如, 给定二叉搜 ...
- (Java实现) 洛谷 P1691 有重复元素的排列问题
题目描述 设R={r1,r2,--,rn}是要进行排列的n个元素.其中元素r1,r2,--,rn可能相同.使设计一个算法,列出R的所有不同排列. 给定n以及待排列的n个元素.计算出这n个元素的所有不同 ...
- Java实现洛谷 P1072 Hankson 的趣味题
P1072 Hankson 的趣味题 输入输出样例 输入 2 41 1 96 288 95 1 37 1776 输出 6 2 PS: 通过辗转相除法的推导 import java.util.*; cl ...
- Java实现 LeetCode 605 种花问题(边界问题)
605. 种花问题 假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有.可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去. 给定一个花坛(表示为一个数组包含0和1,其中0表示没种 ...