OSI模型
wireshark抓包总图下的网络分层
分层数据包
每个分层中,都会对所发送的数据附加一个首部,在这个首部中包含了该层必要的信息,如发送的目标地址以及协议相关信息。通常,为协议提供的信息为包首部,所要发送的内容为数据。在下一层的角度看,从上一层收到的包全部都被认为是本层的数据。
TCP报文结构分析
序列号:
序列号 (Sequence Numbe) 发送数据包中的第一个字节的,32 位。TCP 协议是基于流发送数据的,所有使用序号给发送的所有数据字节都编上号,并按照序号进行顺序发送。
确认号:
确认序列号 (Acknowledgment Number),32 位。TCP 的通信是全双工的(两端同时发送和接受),当连接建立成功后,每一方都能同时发送和接收数据。每一个方向的序号代表这个报文段携带的第一个字节数据的编号。每一方还需要使用确认号对它已经收到的字节进行确认。即:本端把收到的最后一个字节的编号加一,这个值就是确认号,然后发送给对端。例如,B正确收到了A发送过来的一个报文段,其序号字段为501,数据长度为200,则序号为(501~700),这表明B正确接受了A发送到700为止的数据,因此B的期望收到的下一个数据序号为701。
标志位的作用:
URG(紧急指针):该位用于指示紧急数据的存在。当设置了这个标志位时,紧急指针字段中的数值才有效。
ACK(确认):表示确认号字段中包含的序号是有效的,用于表明“确认应答”中的确认号字段值是有效的。这样一来,就可以确保双方都正确地理解对方的发送数据。
PSH(推送):该标志位用于通知接收端立即交付数据给应用层而不是等待填充缓冲区再交付。
RST(复位):当连接出现问题时,可以通过设置这个标志位来强制关闭TCP连接,从而清除连接状态。通常用于拒绝一个非法的报文或者重新建立连接。
SYN(同步):用于在建立连接时进行同步序号的同步。在连接建立时,客户端发送一个带有SYN标志位的数据包给服务器,服务器收到后回传一个带有SYN/ACK标志位的数据包,并等待客户端的确认。
FIN(结束):用于释放一个已经建立的连接。当一方要断开连接时,会向对方发送一个带有FIN标志位的数据包,对方收到后可以回传一个带有ACK标志位的确认包,也可以直接发送一个带有FIN/ACK标志位的数据包。
窗口:
窗口(Window)指的是发送本报文段的一方的接收窗口,窗口值告诉对方:从本报文段的确认号算起,接收方目前允许对方发送的数量。窗口值作为接收方让发送方设置其发送窗口的依据。 例如:A发送一个报文段,其确认号是701,窗口字段是1000,就是告诉对方:“从701开始,我还可以接收1000个字节,即701-1700
校验和:
校验和(Checksum),16 位。每一个报文段都包括有校验和字段,若报文有损伤,则由终点 TCP 将其丢弃,并被认为是丢失。TCP 的校验和是强制的。
紧急指针:
紧急指针(Urgent Pointers),16 位,只有 URG 标志位被设值时该字段才有意义,表示紧急数据相对序列号(Sequence Number字段的值)的偏移。用URG 值 + 序号得到最后一个紧急字节。
选项:
选项(长度可变,最长可以为40个字节),这里重点说说MSS
TCP在建立连接的时候,就可以确定发送数据包的单位MSS(Maximum Segment Size), MSS是每一个TCP报文段中的数据字段的最大长度。 (注意,不是整个TCP报文段的最大长度)
TCP在传输大量数据的时候,是以MSS大小将数据进行分割发送的,重传时也是以MSS为单位。
为什么要设计一个MSS呢? 我们知道,TCP报文端的数据部分,至少还要加上40字节的首部(20字节TCP头+20字节IP头),如果选择了很小的MSS长度,比如只有一个字节的数据,却要封装上40字节的头,那么网络的利用率就很低了。但是如果设置得非常大,在IP层传输时就可能被拆分成多个数据片,在终点还要重新拼接,传输出错时还要重传,这样也会使得开销增大。 理想状态下是MSS正好是IP不会分片的最大数据长度。
MSS是在TCP连接建立时,由两边的主机计算出来的:两端的主机都会将自己能够支持的MSS写入。 如果没有指定,就是默认的536字节长。否则就选用较小的值
TCP三次握手,四次握手:
参考这篇文章:TCP协议-CSDN博客
TCP滑动窗口、流量控制
滑动窗口(Sliding window)是一种流量控制技术。
TCP 中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于 接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。当滑动窗口为 0 时,发送方一般不能再发送数据报。滑动窗口是 TCP 中实现诸如 ACK 确认、流量控制、拥塞控制的承载结构。
1、可以把窗口理解为缓冲区的大小:
tcp报文头部中都会带上window告知对方自己的接收窗口大小
2、滑动窗口的大小会随着发送数据和接收数据而变化
3、通信双方都有发送缓冲区和接收缓冲区
服务器:
发送缓冲区(发送缓冲区窗口)
接收缓冲区(接收缓冲发窗己)
客户端:
发送缓冲区(发送缓冲区窗口)
接收缓冲区(接收缓冲发窗己)
wireshark中的"TCP Window Full"
表示接收方的 TCP 接收窗口已经满了,无法接收更多的数据。TCP 接收窗口是用来控制发送方发送数据的速率,接收方会告诉发送方自己还能够接收多少数据,发送方则根据接收窗口大小来控制发送数据的量,以避免发送过多数据导致接收方无法处理。
可以到到条目1建立连接时,接收方告知了自己的接收窗口大小为7800,然后条目512是发送方连续向接收方发送了1300+1300+1300+448+1300+1300+852 = 7800,此时已经达到接收方的最大接收窗口,需要停下来等待接收方处理,条目1315是接收方在处理数据,所以窗口大小也会随之更新
Wireshark 中,"TCP Window Update"
指示接收方告知发送方其接收窗口大小已被更新,可以继续接收数据。当接收方处理完先前接收的数据,释放了一部分空间以接收更多数据时,它会发送一个 "TCP Window Update" 消息给发送方,通知发送方新的接收窗口大小,以便发送方继续发送数据。这个过程是 TCP 协议中的流量控制机制的一部分,确保发送方和接收方之间的数据传输能够有效地进行。
零窗口通知与窗口探测
假设接收方处理数据的速度跟不上接收数据的速度,缓存就会被占满,从而导致接收窗口为 0,当发送方接收到零窗口通知时,就会停止发送数据。
如下图,可以接收方的窗口大小在不断的收缩至 0:
窗口大小在收缩
接着,发送方会定时发送窗口大小探测报文,以便及时知道接收方窗口大小的变化。
以下图 Wireshark 分析图作为例子说明:
零窗口 与 窗口探测
发送方发送了数据包 1 给接收方,接收方收到后,由于缓冲区被占满,回了个零窗口通知;
发送方收到零窗口通知后,就不再发送数据了,直到过了 3.4 秒后,发送了一个 TCP Keep-Alive 报文,也就是窗口大小探测报文;
当接收方收到窗口探测报文后,就立马回一个窗口通知,但是窗口大小还是 0;
发送方发现窗口还是 0,于是继续等待了 6.8(翻倍) 秒后,又发送了窗口探测报文,接收方依然还是回了窗口为 0 的通知;
发送方发现窗口还是 0,于是继续等待了 13.5(翻倍) 秒后,又发送了窗口探测报文,接收方依然还是回了窗口为 0 的通知;
可以发现,这些窗口探测报文以 3.4s、6.5s、13.5s 的间隔出现,说明超时时间会翻倍递增。
利用窗口控制提高速度
TCP 以1个段为单位,每发送一个段进行一次确认应答的处理。这样的传输方式有一个缺点,就是包的往返时间越长通信性能就越低。
为解决这个问题,TCP 引入了窗口这个概念。确认应答不再是以每个分段,而是以更大的单位进行确认,转发时间将会被大幅地缩短。也就是说,发送端主机,在发送了一个段以后不必要一直等待确认应答,而是继续发送。如下图所示:
窗口控制中的重发控制
确认应答未能返回的情况
在这种情况下,数据已经到达对端,是不需要再进行重发的,如下图:
TCP的累计确认
TCP 使用累积确认机制,这意味着 ACK 号表示接收方期望收到的下一个字节序号。如果接收方收到了连续的数据段,它可能不会为每个数据段单独发送 ACK,而是发送一个 ACK 来确认所有连续数据段。因此,如果接收方收到了 Seq=1 到 Seq=3900 的所有数据,它可以直接发送 Ack=3901 来确认。但如果在此之后,它又收到了 Seq=3901 到 Seq=4349 的数据,那么它可以发送 Ack=4349 来同时确认这些数据。
某个报文段丢失的情况
接收主机如果收到一个自己应该接收的序列号以外的数据时,会针对当前为止收到数据返回确认应答。如下图所示,当某一报文段丢失后,发送端会一直收到序号为1001的确认应答,因此,在窗口比较大,又出现报文段丢失的情况下,同一个序列号的确认应答将会被重复不断地返回。而发送端主机如果连续3次收到同一个确认应答,就会将其对应的数据进行重发。这种机制比之前提到的超时管理更加高效,因此也被称为高速重发控制。
wireshark中TCP dup ack 与TCP Fast Retransmission
TCP dup ack (重复应答)
[TCP dup ack XXX#X] 表示第几次重新请求某一个包,
XXX表示第几个包(不是Seq),
X表示第几次请求。
丢包或者乱序的情况下,会出现该标志。
图例中:第26548 第0 此请求 4468792 的包,重复 4申请了4次 ;
[TCP Fast Retransmission] (快速重传)
一般快速重传算法在收到三次冗余的Ack,即三次[TCP dup ack XXX#X]后,发送端进行快速重传。
为什么是三次呢?因为两次 duplicated ACK 肯定是乱序造成的,丢包肯定会造成三次 duplicated ACK。
wireshark中TCP Retransmission
超时重传,如果一个包的丢了,又没有后续包可以在接收方触发[Dup Ack],或者**[Dup Ack]也丢失**的情况下,TCP会触发超时重传机制。
可以看到条目1313~1320中,接收方一直在重复确认4924267,说明接收方没有收到seq4924267这个包,1321可以看到服务器发送了5217931这个包,1322客户端收到了这个包,但是Ack=4924267,1323中服务器重传了seq4924267这个包,1324中可以看到接收方终于收到了这个包,并回复了Ack=4925555
RTO 是指数(翻倍)上涨
SYN 超时重传五次
从上图可以发现, 客户端发起了 SYN 包后,一直没有收到服务端的 ACK ,所以一直超时重传了 5 次,并且每次 RTO 超时时间是不同的:
第一次是在 1 秒超时重传
第二次是在 3 秒超时重传
第三次是在 7 秒超时重传
第四次是在 15 秒超时重传
第五次是在 31 秒超时重传
可以发现,每次超时时间 RTO 是指数(翻倍)上涨的
wireshark中TCP Out-of-Order
wireshark中出现TCP Out-of-Order时,指的是TCP发送端传输过程中报文乱序了,后一个包的 Seq 号小于前一个包的 Seq+Len 时
可以看到在条目285中发送方的seq=211901, 条目286中发送方是seq=213201,长度为251,此时正常下一包seq应该是213201+251=213452,但是287中是seq确是211901(可能是重传导致),小于213452。
wireshark中TCP Previous segment not captured
TCP Previous segment not captured]报文指的是在TCP发送端传输过程中,该Seq前的报文缺失了。一般在网络拥塞的情况下,造成TCP报文乱序、丢包时,会出现该标志。
服务器39.135.135.81,端口80,发送序号Seq=147154的包,长度Len=1360,那么下一个数据包序号应该为Seq=147154+1360=148514,可以看到客户端请求的也是Ack=148514。而服务器下一个包序号为Seq=149874,中间的包丢失了。
wireshare中TCP acked unseen segment
这个问题,就是这个报文没有抓全,当前报文是个ack的报文,但是,其要进行确认的报文不在
TCP 延迟确认与 Nagle 算法
当我们 TCP 报文的承载的数据非常小的时候,例如几个字节,那么整个网络的效率是很低的,因为每个 TCP 报文中都有会 20 个字节的 TCP 头部,也会有 20 个字节的 IP 头部,而数据只有几个字节,所以在整个报文中有效数据占有的比重就会非常低。
那么就出现了常见的两种策略,来减少小报文的传输,分别是:
- Nagle 算法
- 延迟确认
Nagle 算法是如何避免大量 TCP 小数据报文的传输?
Nagle 算法做了一些策略来避免过多的小数据报文发送,这可提高传输效率。
Nagle 算法的策略:
没有已发送未确认报文时,立刻发送数据。
存在未确认报文时,直到「没有已发送未确认报文」或「数据长度达到 MSS 大小」时,再发送数据。
只要没满足上面条件中的一条,发送方一直在囤积数据,直到满足上面的发送条件。
上图右侧启用了 Nagle 算法,它的发送数据的过程:
一开始由于没有已发送未确认的报文,所以就立刻发了 H 字符;
接着,在还没收到对 H 字符的确认报文时,发送方就一直在囤积数据,直到收到了确认报文后,此时没有已发送未确认的报文,于是就把囤积后的 ELL 字符一起发给了接收方;
待收到对 ELL 字符的确认报文后,于是把最后一个 O 字符发送了出去
可以看出,Nagle 算法一定会有一个小报文,也就是在最开始的时候。
可以在 Socket 设置
TCP_NODELAY
选项来关闭这个算法
那延迟确认又是什么?
事实上当没有携带数据的 ACK,他的网络效率也是很低的,因为它也有 40 个字节的 IP 头 和 TCP 头,但确却没有携带数据报文。
为了解决 ACK 传输效率低问题,所以就衍生出了 TCP 延迟确认。
TCP 延迟确认的策略:
- 当有响应数据要发送时,ACK 会随着响应数据一起立刻发送给对方
- 当没有响应数据要发送时,ACK 将会延迟一段时间,以等待是否有响应数据可以一起发送
- 如果在延迟等待发送 ACK 期间,对方的第二个数据报文又到达了,这时就会立刻发送 ACK
TCP Keep-alive机制
一般的,TCP的客户端与服务器的连接类型可以分为:
短链接:客户端连接到服务器后,即开始与服务器交互,请求资源,上报数据等,交互完毕后即断开与服务器的连接,如HTTP协议等。
长连接:客户端连接到服务器后,不一定会立即进行数据的传递,而是一直保持连接状态,且双方一般不会主动断开连接,如MQTT协议等
需要注意的是,不管是长连接还是短连接都不是TCP协议本身所规定的,TCP只是给应用层提供了建立与断开连接的方法与资源管理。
相关参数
SO_KEEPALIVE:是否开启保活
TCP_KEEPIDLE:Start keeplives after this period
TCP_KEEPINTVL:Interval between keepalives
TCP_KEEPCNT:Number of keepalives before death
SO_KEEPALIVE:Keep-alive可以是双向的,即客户端可以主动给服务器发,或服务器主动给客户端发送。在使能了SO_KEEPALIVE后,即启用了保活机制。
TCP_KEEPIDLE:当客户端与服务器没有交互数据达到TCP_KEEPIDLE的空闲时间后,TCP将会给对方发送探测包。
TCP_KEEPINTVL:如果上一次的探测包没有得到响应,那么将用TCP_KEEPINTVL作为下一次的探测包间隔。
TCP_KEEPCNT:当连续发送了TCP_KEEPCNT次数的探测包都未收到响应后,本地将会释放当前连接资源,并且通知应用层连接断开。
正常探测包
版权归原作者 遇到困难睡大觉- 所有, 如有侵权,请联系我们删除。