计算机网络 —— TCP篇 TCP 重传、滑动窗口、流量控制、拥塞控制

news2024/11/25 9:04:58

计算机网络系列文章目录

TCP篇 TCP 重传、滑动窗口、流量控制、拥塞控制
在这里插入图片描述


文章目录

  • 计算机网络系列文章目录
  • 前言
  • 4.2 TCP 重传、滑动窗口、流量控制、拥塞控制
    • 4.2.1 重传机制
      • 超时重传
        • 什么情况会触发超时重传
        • 超时时间应该设置为多少呢?
      • 快速重传
      • SACK
      • D-SACK
    • 4.2.2 滑动窗口
        • 为什么需要滑动窗口
        • 发送方的窗口移动的逻辑
          • 程序是如何表示发送方的四个部分的
        • 接收方的窗口
    • 4.2.3 流量控制
      • 操作系统缓冲区与滑动窗口的关系
      • 窗口关闭
      • 糊涂窗口综合症
    • 4.2.4 拥塞控制
      • 拥塞窗口
      • 慢启动
      • 拥塞避免
      • 拥塞发生
        • 发生超时重传的拥塞发生算法
        • 发生快速重传的拥塞发生算法
      • 快速恢复


前言

文章来自小林codingTCP重传机制,在学习中总结

4.2 TCP 重传、滑动窗口、流量控制、拥塞控制

4.2.1 重传机制

TCP 实现可靠传输的方式之一,是通过序列号与确认应答

在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。
在这里插入图片描述
但在错综复杂的网络,并不一定能如上图那么顺利能正常的数据传输,万一数据在传输过程中丢失了呢?

所以 TCP 针对数据包丢失的情况,会用重传机制解决。

接下来说说常见的重传机制:

超时重传

什么情况会触发超时重传

重传机制的其中一个方式,就是在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,也就是我们常说的超时重传。

TCP 会在以下两种情况发生超时重传:
数据包丢失
确认应答丢失
在这里插入图片描述

超时时间应该设置为多少呢?

RTT(Round-Trip Time 往返时延)是 数据发送时刻到接收到确认的时刻的差值,也就是包的往返时间

超时重传时间 RTO (Retransmission Timeout 超时重传时间)的值应该略大于报文往返 RTT 的值

当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

估计往返时延RTT:
需要 TCP 通过采样 RTT 的时间,然后进行加权平均,算出一个平滑 RTT 的值,而且这个值还是要不断变化的,因为网络状况不断地变化。

除了采样 RTT,还要采样 RTT 的波动范围,这样就避免如果 RTT 有一个大的波动的话,很难被发现的情况。

RTO根据这两个值去计算

如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍

也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。

超时触发重传存在的问题是,超时周期可能相对较长。那是不是可以有更快的方式呢?

于是就可以用「快速重传」机制来解决超时重发的时间等待。

快速重传

TCP 还有另外一种快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传

快速重传的机制是 当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。

在这里插入图片描述
在上图,发送方发出了 1,2,3,4,5 份数据:

第一份 Seq1 先送到了,于是就 Ack 回 2;
结果 Seq2 因为某些原因没收到,Seq3 到达了,于是还是 Ack 回 2;
后面的 Seq4 和 Seq5 都到了,但还是 Ack 回 2,因为 Seq2 还是没有收到;
发送端收到了三个 Ack = 2 的确认,知道了 Seq2 还没有收到,就会在定时器过期之前,重传丢失的 Seq2。
最后,收到了 Seq2,此时因为 Seq3,Seq4,Seq5 都收到了,于是 Ack 回 6 。

快速重传机制只解决了一个问题,就是超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传一个,还是重传所有的问题

举个例子,假设发送方发了 6 个数据,编号的顺序是 Seq1 ~ Seq6 ,但是 Seq2、Seq3 都丢失了,那么接收方在收到 Seq4、Seq5、Seq6 时,都是回复 ACK2 给发送方,但是发送方并不清楚这连续的 ACK2 是接收方收到哪个报文而回复的, 那是选择重传 Seq2 一个报文,还是重传 Seq2 之后已发送的所有报文呢(Seq2、Seq3、 Seq4、Seq5、 Seq6) 呢?

如果只选择重传 Seq2 一个报文,那么重传的效率很低。因为对于丢失的 Seq3 报文,还得在后续收到三个重复的 ACK3 才能触发重传。

如果选择重传 Seq2 之后已发送的所有报文,虽然能同时重传已丢失的 Seq2 和 Seq3 报文,但是 Seq4、Seq5、Seq6 的报文是已经被接收过了,对于重传 Seq4 ~Seq6 折部分数据相当于做了一次无用功,浪费资源。

可以看到,不管是重传一个报文,还是重传已发送的报文,都存在问题。

为了解决不知道该重传哪些 TCP 报文,于是就有 SACK 方法

SACK

还有一种实现重传机制的方式叫:SACK( Selective Acknowledgment), 选择性确认。

这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西它可以将已收到的数据的信息发送给「发送方」,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。

如下图,发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制,通过 SACK 信息发现只有 200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复。
在这里插入图片描述
如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)。

D-SACK

Duplicate SACK 又称 D-SACK,其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。

例子1 ACK丢包
在这里插入图片描述
例子2 网络延时
在这里插入图片描述
数据包(1000~1499) 被网络延迟了,导致「发送方」没有收到 Ack 1500 的确认报文。
而后面报文到达的三个相同的 ACK 确认报文,就触发了快速重传机制,但是在重传后,被延迟的数据包(1000~1499)又到了「接收方」;
所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 D-SACK,表示收到了重复的包。
这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而是因为网络延迟了。

可见,D-SACK 有这么几个好处:

可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;
可以知道是不是「发送方」的数据包被网络延迟了;
可以知道网络中是不是把「发送方」的数据包给复制了;
在 Linux 下可以通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能(Linux 2.4 后默认打开)。

4.2.2 滑动窗口

为什么需要滑动窗口

我们都知道 TCP 是每发送一个数据,都要进行一次确认应答。当上一个数据包收到了应答了, 再发送下一个。

这个模式就有点像我和你面对面聊天,你一句我一句。但这种方式的缺点是效率比较低的。

如果你说完一句话,我在处理其他事情,没有及时回复你,那你不是要干等着我做完其他事情后,我回复你,你才能说下一句话,很显然这不现实。

所以,这样的传输方式有一个缺点数据包的往返时间越长,通信的效率就越低

为解决这个问题,TCP 引入了窗口这个概念。即使在往返时间较长的情况下,它也不会降低网络通信的效率。

那么有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。

窗口的实现实际上是操作系统开辟的一个缓存空间发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除

假设窗口大小为 3 个 TCP 段,那么发送方就可以「连续发送」 3 个 TCP 段,并且中途若有 ACK 丢失,可以通过「下一个确认应答进行确认」。如下图:
在这里插入图片描述
图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答

TCP 头里有一个字段叫 Window,也就是窗口大小。

这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。

所以,通常窗口的大小是由接收方的窗口大小来决定的。
发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。

发送方的窗口移动的逻辑

我们先来看看发送方的窗口,下图就是发送方缓存的数据,根据处理的情况分成四个部分,其中深蓝色方框是发送窗口,紫色方框是可用窗口:
在这里插入图片描述
#1 是已发送并收到 ACK确认的数据:1~31 字节
#2 是已发送但未收到 ACK确认的数据:32~45 字节
#3 是未发送但总大小在接收方处理范围内(接收方还有空间):46~51字节
#4 是未发送但总大小超过接收方处理范围(接收方没有空间):52字节以后

在下图,当发送方把数据「全部」都一下发送出去后,可用窗口的大小就为 0 了,表明可用窗口耗尽,在没收到 ACK 确认之前是无法继续发送数据了。
在这里插入图片描述
在下图,当收到之前发送的数据 32~36 字节的 ACK 确认应答后,如果发送窗口的大小没有变化,则滑动窗口往右边移动 5 个字节,因为有 5 个字节的数据被应答确认,接下来 52-56 字节又变成了可用窗口,那么后续也就可以发送 52- 56 这 5 个字节的数据了。
在这里插入图片描述

程序是如何表示发送方的四个部分的

TCP 滑动窗口方案使用三个指针来跟踪在四个传输类别中的每一个类别中的字节。其中两个指针是绝对指针(指特定的序列号),一个是相对指针(需要做偏移)。
在这里插入图片描述
SND.WND:表示发送窗口的大小(大小是由接收方指定的);

SND.UNA(Send Unacknoleged):是一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号,也就是 #2 的第一个字节。

SND.NXT:也是一个绝对指针,它指向未发送但可发送范围的第一个字节的序列号,也就是 #3 的第一个字节。

指向 #4 的第一个字节是个相对指针,它需要 SND.UNA 指针加上 SND.WND 大小的偏移量,就可以指向 #4 的第一个字节了。

那么可用窗口大小的计算就可以是:

可用窗口大小 = SND.WND -(SND.NXT - SND.UNA)

接收方的窗口

接下来我们看看接收方的窗口,接收窗口相对简单一些,根据处理的情况划分成三个部分:

#1 + #2 是已成功接收并确认的数据(等待应用进程读取);
#3 是未收到数据但可以接收的数据;
#4 未收到数据并不可以接收的数据;
在这里插入图片描述其中三个接收部分,使用两个指针进行划分:

RCV.WND:表示接收窗口的大小,它会通告给发送方。
RCV.NXT:是一个指针,它指向期望从发送方发送来的下一个数据字节的序列号,也就是 #3 的第一个字节。
指向 #4 的第一个字节是个相对指针,它需要 RCV.NXT 指针加上 RCV.WND 大小的偏移量,就可以指向 #4 的第一个字节了。

接收窗口的大小是约等于发送窗口的大小的

因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。

4.2.3 流量控制

发送方不能无脑的发数据给接收方,要考虑接收方处理能力。

如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。

为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
在这里插入图片描述
根据上图的流量控制,说明下每个过程:
客户端向服务端发送请求数据报文。这里要说明下,本次例子是把服务端作为发送方,所以没有画出服务端的接收窗口。

服务端收到请求报文后,发送确认报文和 80 字节的数据,于是可用窗口 Usable 减少为 120 字节,同时 SND.NXT 指针也向右偏移 80 字节后,指向 321,这意味着下次发送数据的时候,序列号是 321
客户端收到 80 字节数据后,于是接收窗口往右移动 80 字节,**RCV.NXT 也就指向 321,这意味着客户端期望的下一个报文的序列号是 321,**接着发送确认报文给服务端。
服务端再次发送了 120 字节数据,于是可用窗口耗尽为 0,服务端无法再继续发送数据。
客户端收到 120 字节的数据后,于是接收窗口往右移动 120 字节,RCV.NXT 也就指向 441,接着发送确认报文给服务端。
服务端收到对 80 字节数据的确认报文后,SND.UNA 指针往右偏移后指向 321,于是可用窗口 Usable 增大到 80。
服务端收到对 120 字节数据的确认报文后,SND.UNA 指针往右偏移后指向 441,于是可用窗口 Usable 增大到 200。
服务端可以继续发送了,于是发送了 160 字节的数据后,SND.NXT 指向 601,于是可用窗口 Usable 减少到 40。
客户端收到 160 字节后,接收窗口往右移动了 160 字节,RCV.NXT 也就是指向了 601,接着发送确认报文给服务端。
服务端收到对 160 字节数据的确认报文后,发送窗口往右移动了 160 字节,于是 SND.UNA 指针偏移了 160 后指向 601,可用窗口 Usable 也就增大至了 200。

操作系统缓冲区与滑动窗口的关系

操作系统的缓冲区是会影响发送窗口和接收窗口的

应用程序没有及时读取缓存时,有一部分数据是会一直占着缓冲区,那么接收窗口的大小就会缩小。然后在回ACK的时候,会通告窗口大小给发送方。然后发送方的窗口大小也会减小。

最后窗口都收缩为 0 了,也就是发生了窗口关闭。当发送方可用窗口变为 0 时,发送方实际上会定时发送窗口探测报文,以便知道接收方的窗口是否发生了改变

当服务端系统资源非常紧张的时候,操作系统可能会直接减少了接收缓冲区大小,这时应用程序又无法及时读取缓存数据,那么这时候就有严重的事情发生了,会出现数据包丢失的现象
在这里插入图片描述

为了防止这种情况发生,TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间再减少缓存,这样就可以避免了丢包情况

窗口关闭

如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。
窗口关闭潜在的危险:
接收方向发送方通告窗口大小时,是通过 ACK 报文来通告的。

那么,当发生窗口关闭时,接收方处理完数据后,会向发送方通告一个窗口非 0 的 ACK 报文,如果这个通告窗口的 ACK 报文在网络中丢失了,那麻烦就大了。
在这里插入图片描述

这会导致发送方一直等待接收方的非 0 窗口通知,接收方也一直等待发送方的数据,如不采取措施,这种相互等待的过程,会造成了死锁的现象。

TCP为每个连接设有一个持续定时器,**只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。**如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。
窗口探测的次数一般为 3 次,每次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接。
在这里插入图片描述

糊涂窗口综合症

如果接收方太忙了,来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小。

到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症。

要知道,我们的 TCP + IP 头有 40 个字节,为了传输那几个字节的数据,要搭上这么大的开销,这太不经济了。

糊涂窗口综合症的现象是可以发生在发送方和接收方:

接收方可以通告一个小的窗口
而发送方可以发送小数据
于是,要解决糊涂窗口综合症,就要同时解决上面两个问题就可以了:

让接收方不通告小窗口给发送方
让发送方避免发送小数据

怎么让接收方不通告小窗口呢?
接收方通常的策略如下:

当「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。

等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。

怎么让发送方避免发送小数据呢?
发送方通常的策略如下:

使用 Nagle 算法,该算法的思路是延时处理,只有满足下面两个条件中的任意一个条件,才可以发送数据:

条件一:要等到窗口大小 >= MSS 并且 数据大小 >= MSS;
条件二:收到之前发送数据的 ack 回包;
只要上面两个条件都不满足,发送方一直在囤积数据,直到满足上面的发送条件。

4.2.4 拥塞控制

为什么需要拥塞控制,

流量控制是避免「发送方」的数据填满「接收方」的缓存

计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵

网络出现拥堵时如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大…

当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量

于是,就有了拥塞控制控制的目的就是避免「发送方」的数据填满整个网络

为了在「发送方」调节所要发送数据的量,定义了一个叫做「拥塞窗口」的概念。

拥塞窗口

拥塞窗口 cwnd是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。

我们在前面提到过发送窗口 swnd 和接收窗口 rwnd 是约等于的关系,那么由于加入了拥塞窗口的概念后,此时发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值

拥塞窗口 cwnd 变化的规则:

只要网络中没有出现拥塞,cwnd 就会增大;
但网络中出现了拥塞,cwnd 就减少;

那么怎么知道当前网络是否出现了拥塞呢?

其实只要**「发送方」**没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了拥塞。

拥塞控制主要是四个算法:

慢启动

TCP 在刚建立连接完成后,首先是有个慢启动的过程,这个慢启动的意思就是一点一点的提高发送数据包的数量,如果一上来就发大量的数据,这不是给网络添堵吗?

慢启动的算法记住一个规则就行:当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1

慢启动算法的变化过程如下图:
在这里插入图片描述
可以看出慢启动算法,发包的个数是指数性的增长。

那慢启动涨到什么时候是个头呢?
有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。

当 cwnd < ssthresh 时,使用慢启动算法。
当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」。

拥塞避免

当拥塞窗口 cwnd 「超过」慢启动门限 ssthresh 就会进入拥塞避免算法。一般来说 ssthresh 的大小是 65535 字节。

那么进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd

接上前面的慢启动的栗子,现假定 ssthresh 为 8:

当 8 个 ACK 应答确认到来时,每个确认增加 1/8,8 个 ACK 确认 cwnd 一共增加 1,于是这一次能够发送 9 个 MSS 大小的数据,变成了线性增长

拥塞避免算法的变化过程如下图:
在这里插入图片描述
拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,但是增长速度缓慢了一些。

就这么一直增长着后,网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。

当触发了重传机制,也就进入了「拥塞发生算法」

拥塞发生

当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:

超时重传
快速重传

这两种使用的拥塞发送算法是不同的,接下来分别来说说。

发生超时重传的拥塞发生算法

当发生了「超时重传」,则就会使用拥塞发生算法。

这个时候,ssthresh慢启动阈值 和 cwnd拥塞窗口 的值会发生变化:

ssthresh 慢启动阈值设为 cwnd/2
cwnd 拥塞窗口重置为 初始值

拥塞发生算法的变化如下图,以下图示例子假设这个初始值是1:
在这里插入图片描述
Linux 针对每一个 TCP 连接的 cwnd 初始化值是 10,也就是 10 个 MSS,我们可以用 ss -nli 命令查看每一个 TCP 连接的 cwnd 初始化值,如下图
在这里插入图片描述

发生快速重传的拥塞发生算法

还有更好的方式,前面我们讲过「快速重传算法」。当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传

TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和 cwnd 变化如下:

cwnd = cwnd/2 ,也就是 拥塞窗口设置为原来的一半;
ssthresh = cwnd 慢启动阈值设置为当前的拥塞窗口,也就是曾经的一半

快速恢复

快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈

正如前面所说,进入快速恢复之前,cwnd 和 ssthresh 已被更新了:

cwnd = cwnd/2 ,也就是设置为原来的一半;
ssthresh = cwnd;

然后,进入快速恢复算法如下:

拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了);
重传丢失的数据包;
如果再收到重复的 ACK,那么 cwnd 增加 1;
如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;

快速恢复算法的变化过程如下图:
在这里插入图片描述
也就是没有像「超时重传」一夜回到解放前,而是还在比较高的值,后续呈线性增长。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/177936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构之二叉树系列】万字深剖数据结构---堆

目录前言一、堆二、堆的实现(重点)1. 数据类型重定义2. 堆结构的定义3. 堆结构的重定义三、堆中常见的基本操作&#xff08;重点&#xff09;1. 声明2. 定义&#xff08;1&#xff09;初始化&#xff08;2&#xff09;销毁&#xff08;3&#xff09;插入数据&#xff08;4&…

macOS Monterey 12.6.3 (21G419) 正式版 ISO、IPSW、PKG 下载

macOS Monterey 12.6&#xff0c;皆为安全更新&#xff0c;不再赘述。 macOS Monterey 12.6&#xff0c;发布于 2022 年 9 月 12 日&#xff08;北京时间今日凌晨&#xff09;&#xff0c;本次为安全更新。 今日&#xff08;2022-07-21&#xff09;凌晨&#xff0c;Apple 终于…

ATAC-seq分析:Peak Calling(8)

1. 寻找开发区域 ATACseq 的一个共同目标是识别转录因子结合和/或转录机制活跃的无核小体区域。该核小体游离信号对应于小于一个核小体的片段&#xff08;如 Greenleaf 论文中定义 < 100bp&#xff09;。 然而&#xff0c;为了识别开放的染色质&#xff0c;我们可以简单地使…

意想不到的结果:Foo(m)可能是在定义名为m的对象

文章目录例一&#xff1a;Foo(m); 是定义名为 m 的对象例二&#xff1a;Foo(m).i; 传入实参 m例三&#xff1a;func(Foo(m)); 传入实参 m例四&#xff1a;S(cout)(1) 定义名为 cout 的对象例五&#xff1a;S(std::cout)(1) 传入实参 std::cout你知道吗&#xff0c;如果 Foo 是…

vue3 watch 监听响应式数据变化

主要是用来监听ref 或者reactive 所包裹的数据才可以被监听到 <template><input type"text" v-model"message"> </template> <script setup lang"ts">import {ref, watch} from "vue";let message ref<s…

powerdesigner画UML组件图初步

组件图 组件图是用来描述组件与组件之间关系的一种UML图&#xff0c;组件图在宏观层面上显示了构成系统某一特定方面的实现结构。 组件图可以用来显示组件之间的依赖关系&#xff0c;以及组件的接口和调用关系。 组件图由组件&#xff0c;接口&#xff0c;组件图中的关系&…

20230124英语学习

Why Do We Still Procrastinate Despite It Causing So Much Stress? 明知道拖延不好&#xff0c;为何还会拖延&#xff1f; Are you procrastinating?I am.I have been delaying writing this article for the last few days even though I knew I had a deadline. I have …

从零到一:复现 DIR-815 栈溢出漏洞

从零到一&#xff1a;复现 DIR-815 栈溢出漏洞 实验环境 执行命令uname -a可以查看到当前系统版本 我这边采用桥接模式进行实验。 环境搭建 文章命令操作均在root下操作&#xff0c;且git clone xxxx.git下载所用到工具都均下载保存到/opt/tools/文件夹下&#xff0c;方便统…

OKC和802.11R的知识小科普

欢迎来到东用知识小课堂&#xff01;1.什么是漫游简单来说&#xff0c;就是设备从一个AP&#xff0c;连接到另一个AP。IP地址不需要重新申请。整个过程需要尽可能快的进行&#xff0c;否则对于用户而言&#xff0c;就会发现网络出现卡顿。而为了安全&#xff0c;网络的认证过程…

【Python】使用pyinstaller打包py程序为exe应用程序时,出现“Tcl报错闪退”的解决办法

问题概述 使用pyinstaller -F的命令进行py程序转为exe程序时&#xff0c;打包后的exe程序会出现闪退报错的情况&#xff1a; 解决办法 1. 检查“环境变量”tcl和tk是否配置好&#xff1a; 查看系统高级设置>>>环境变量>>>系统变量 博主使用anaconda进行p…

河道污染物识别系统 python

河道污染物识别系统通过pythonyolo深度学习技术&#xff0c;对现场画面中河道污染物以及漂浮物进行全天候实时监测&#xff0c;当监测到出现污染物漂浮物时&#xff0c;立即抓拍存档触发告警。与C / C等语言相比&#xff0c;Python速度较慢。也就是说&#xff0c;Python可以使用…

计算机组成原理 | 第一章:概论

文章目录&#x1f4da;冯诺依曼计算机的特点&#x1f4da;计算机硬件组成框图&#x1f4da;计算机硬件的主要技术指标&#x1f407;非时间指标&#x1f407;时间指标&#x1f511;计算技巧归纳&#x1f4da;小结&#x1f511;本章掌握要点&#x1f407;补充思考题&#x1f4da;…

Froala Editor内容中删除内联样式

Froala Editor内容中删除内联样式 易于集成-编辑器可以在任何时间内集成到任何类型的项目中。它只需要基本的JavaScript和HTML编码知识。 流行-HTML编辑器在开发人员中很流行&#xff0c;它有最流行的开发框架的插件。 易于升级-将所有自定义内容与编辑器文件夹分开&#xff0c…

3.1存储系统基本概念

文章目录一、引子二、存储器的层次化结构1.层次化结构&#xff08;1&#xff09;金字塔&#xff08;2&#xff09;案例&#xff08;3&#xff09;Cache&#xff08;4&#xff09;寄存器&#xff08;5&#xff09;辅存和外存2.速度与价格举例&#xff08;1&#xff09;主存和Cac…

智障税品牌种草收割流

1.量化量化这一块我后续应该不更新了&#xff0c;因为目前我接触的都是赚钱层次的了发出去都是砸自己的饭碗目前我在8个交易所都是市商费率有需要费率的可以合作我在跑的策略为&#xff1a;套利、高频、预测《赚麻》当你有了顶级费率和速度&#xff0c;什么策略都可以赚钱2.引流…

88.【员工管理系统-springBoot】

SpringBoot(十三)、员工管理系统SpringBoot1.准备工作(1).导入我们所需要的环境依赖(2).首页的Controller与View (静态资源Thymeleaf接管)2.国际化(1).设置字符编码为UTF-8(2).添加文件资源目录 i18n(3).注册国际化实现(4).国际化的实现 index.html(5).英文与汉文的交互(6).在s…

[QMT]08-从本地行情数据解析历史K线信息

用python解析QMT本地数据获取本地行情数据get_local_data(field_list[], stock_code[], period1d, start_time, end_time, count-1,dividend_typenone, fill_dataTrue, data_dirdata_dir)释义从本地数据文件获取行情数据&#xff0c;用于快速批量获取历史部分的行情数据参数fie…

谈谈线程安全问题及其解决方法

本文讲述一下线程的安全问题及解决方法。 一 线程安全问题举例说明 在电影院售票的场景&#xff0c;假使有100张票待售&#xff0c;有3个窗口在售票&#xff0c;如图所示&#xff1a; 三个窗口都卖出票1&#xff0c;一个票被卖了3次&#xff0c;多线程访问共享数据“票”&am…

【代码阅读】MSC-VO

MSC-VO是ICRA2022的一篇点线视觉SLAM论文&#xff0c;本身是在ORBSLAM2的基础上改进的&#xff0c;改进的部分在于为SLAM系统引入了线段&#xff0c;并且使用了曼哈顿坐标系与结构化约束进行优化&#xff0c;之前看过的论文记录可以参考链接&#xff0c;年前把线段匹配和均匀化…

CMake的介绍

1.示例代码其实都非常简单&#xff0c;直接使用 GCC 编译器编译即可&#xff0c;连 Makefile 都不需要。在实际的项目中&#xff0c; 一个工程中可能包含几十、成百甚至上千个源文件&#xff0c; 这些源文件按照其类型、功能、模块分别放置在不同的目录中&#xff1b;面对这样的…