破解网络奥秘:一图胜千言,TCP重传、滑动窗口、流量与拥塞控制全解析

引言

TCP 是一个非常复杂且伟大的协议,它通过许多机制来确保传输的可靠性。为了实现这一目标,TCP需要处理各种问题,比如数据损坏、丢包、重复数据以及分片顺序混乱等。如果这些问题得不到解决,可靠传输就无从谈起。

众所周知,TCP 是一个可靠传输的协议,那么它是如何实现这种可靠性的呢?

为了实现可靠传输,需要考虑许多方面的问题,例如数据的破坏、丢包、重复传输以及分片顺序的混乱等。如果这些问题得不到解决,可靠传输就无从谈起。

TCP 是通过以下机制来实现可靠传输的:序列号、确认应答、重传控制、连接管理以及窗口控制。

今天,我们将重点介绍 TCP 的重传机制滑动窗口流量控制拥塞控制

在这里插入图片描述

重传机制

TCP 实现可靠传输的一种方式是通过使用序列号和确认应答。

在 TCP 协议中,当发送端的数据到达接收端时,接收端会发送一个确认应答消息,表示已经成功接收到数据。

在这里插入图片描述

然而,在复杂的网络环境中,数据传输并不总是那么顺利。如果数据在传输过程中丢失了怎么办?

为了应对数据包丢失的情况,TCP 引入了重传机制

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

  • 超时重传
  • 快速重传
  • SACK
  • D-SACK

超时重传

重传机制的一种方式是在发送数据时设置一个定时器。如果在指定时间内没有收到对方的 ACK 确认应答消息,发送端就会重新发送该数据包,这种方法被称为超时重传

TCP 会在以下两种情况下触发超时重传:

  1. 数据包丢失
  2. 确认应答丢失

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

我们先来了解一下什么是 RTT(Round-Trip Time 往返时延),从下图我们就可以知道:

在这里插入图片描述
RTT 指的是数据发送时刻到接收到确认的时刻的差值,也就是包的往返时间。

超时重传时间是以 RTO (Retransmission Timeout 超时重传时间)表示。

假设在重传的情况下,超时时间 RTO 「较长或较短」时,会发生什么事情呢?

  • 当超时时间 RTO 较大时,重发速度慢,数据丢失后需要等待很长时间才会重传,效率低,性能差;
  • 当超时时间 RTO 较小时,可能会导致未丢失的数据也被重传,重传过快会增加网络拥塞,进而引发更多的超时,导致更多的重传。

精确测量超时时间 RTO 的值是非常重要的,这样可以使重传机制更加高效。

从上述情况可以看出,超时重传时间 RTO 的值应略大于数据报文的往返时间 RTT

在这里插入图片描述

到这里,可能大家会觉得计算超时重传时间 RTO 的值似乎并不复杂。

似乎只需要在发送端发送数据包时记下时间 t0,然后在接收端收到 ACK 时再记下时间 t1,那么 RTT = t1 - t0。其实没那么简单,这只是一次采样,不能代表普遍情况

实际上,“报文往返时间 RTT 的值” 经常变化,因为网络状况也是不断变化的。由于 “报文往返时间 RTT 的值” 经常波动,所以 “超时重传时间 RTO 的值” 也应该是一个动态变化的值。这里我不做过多讲解,感兴趣的朋友可以自行查阅相关资料。

尽管超时重传机制能确保丢失的数据最终被重传,但它的局限性在于可能会导致较长的延迟,因为发送端需要等待定时器超时后才能重传数据。为了解决这一问题,TCP 引入了快速重传机制

快速重传

TCP 还引入了另一种称为快速重传(Fast Retransmit)的机制。这种机制不是依靠时间,而是依靠数据驱动来触发重传

那么,快速重传机制是如何工作的呢?其实很简单,一图胜千言。

在这里插入图片描述

在上图中,发送方依次发出了 1,2,3,4,5 份数据:

  1. 第一份 Seq1 先送到了接收方,因此接收方返回 ACK 2
  2. Seq2 因为某些原因没有到达接收方,而 Seq3 到达了接收方,所以接收方仍然返回 ACK 2
  3. 随后,Seq4Seq5 也成功到达了接收方,但接收方还是返回 ACK 2,因为它还在等待 Seq2
  4. 发送方收到三个 ACK 2 后,意识到 Seq2 丢失了,因此在定时器过期之前重传 Seq2
  5. 最后,接收方收到了重传的 Seq2,由于之前的 Seq3Seq4Seq5 都已经收到了,于是接收方返回 ACK 6

因此,快速重传的工作方式是在接收方收到三个相同的 ACK 报文时,发送方会在定时器过期之前重传丢失的报文段。

快速重传机制虽然解决了超时时间的问题,但仍然面临另一个问题:在重传时,应该重传丢失的一个报文段,还是重传所有报文段

例如,在上述例子中,是只重传 Seq2 还是重传 Seq2Seq3Seq4Seq5?因为发送端无法确定这连续的三个 ACK 2 是哪个报文段传回的。

根据 TCP 的不同实现,这两种情况都是可能的。因此,快速重传机制有时会显得不够精确

为了更好地解决这个问题,即明确知道该重传哪些报文段,TCP 引入了 SACK(Selective Acknowledgment,选择性确认) 方法。

SACK

这种方法需要在 TCP 头部的「选项」字段中增加一个 SACK 字段。SACK 能将接收方的缓存状态发送给发送方,使发送方明确哪些数据已经收到,哪些数据还没有收到。通过这些信息,发送方可以只重传丢失的数据

如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)。

D-SACK

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

下面举两个例子来说明 D-SACK 的作用。

例子一:ACK 丢包

在这里插入图片描述

  • 「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,所以发送方超时后,重传第一个数据包(3000 ~ 3499)。
  • **接收方发现数据重复收到,于是回了一个 SACK = 30003500**,告知「发送方」30003500 的数据早已被接收了。由于 ACK 已经到了 4000,意味着 4000 之前的所有数据都已收到,因此这个 SACK 就代表着 D-SACK
  • 这样「发送方」就知道了,数据并没有丢失,丢失的是「接收方」的 ACK 确认报文。

例子二:网络延迟

在这里插入图片描述

  • 数据包(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 版本之后默认是开启的)。

滑动窗口

我们都知道TCP协议采用了一种基于可靠传输的设计,其中包含了确认应答机制,确保每一个数据包都能得到接收方的确认。这类似于两个人之间的对话,一方发送信息后必须等待另一方的回应,确认信息已被正确接收,然后才能发送下一条信息。

然而,这种一对一的应答机制就像是一场严格交替的对话,其中一个人必须完全专注于对方的回应,才能继续发言。这就好像我们在交流时,你必须暂停直到我对你的话做出反应,你才能继续你的思路,这显然是低效且不符合日常交流习惯的。

在这里插入图片描述

因此,这种传输方式存在一个缺点:数据包的往返时间越长,通信效率就越低

为了解决这个问题,TCP 引入了窗口机制。即使在往返时间较长的情况下,窗口机制也能保持较高的通信效率。

有了窗口机制,可以指定窗口大小,窗口大小指的是无需等待确认应答即可继续发送数据的最大量

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

假设窗口大小为 3 个 TCP 段,那么发送方就可以「连续发送」 3 个 TCP 段,并且中途若有 ACK 丢失,可以通过「下一个确认应答进行确认」。如下图:

在这里插入图片描述

图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答

窗口大小由哪一方决定?

TCP头中有一个字段称为窗口大小(Window)。

这个字段由接收端用来告知发送端自身剩余的缓冲区容量。根据接收端的处理能力,发送端可以调整发送数据的速度,避免接收端因处理不过来而导致数据丢失。

因此,通常情况下,窗口大小由接收端决定。

发送端发送的数据量不能超过接收端的窗口大小,否则接收端将无法正常接收这些数据。

流量控制

发送方不能盲目地发送数据给接收方,需要考虑接收方的处理能力。

如果发送方不停地发送数据而不考虑接收方的处理能力,会导致接收方无法处理,进而触发重传机制,从而造成网络资源的浪费。

为了解决这种问题,TCP 提供了一种机制,使得发送方可以根据接收方的实际接收能力来控制数据发送量,这就是所谓的流量控制。这主要是通过滑动窗口(Sliding Window) 机制实现的。

想象一下,发送方和接收方之间有一条管道,数据就像水流一样通过这条管道。滑动窗口机制就像是在这条管道的接收端设置了一个可调节的阀门,用来控制水流的速度,确保接收方能够顺利处理所有流进的数据。

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

操作系统缓冲区

操作系统缓冲区是内存中用于暂时存储数据的区域,以便在数据传输过程中平衡生产和消费速率。操作系统在发送和接收数据时,都会用到缓冲区。

  1. 发送缓冲区:存储即将通过网络发送的数据。发送方应用程序将数据写入发送缓冲区,操作系统再根据网络状况和滑动窗口大小将数据发送出去。
  2. 接收缓冲区:存储接收到的数据,等待应用程序读取。接收方从网络接收到的数据先存储在接收缓冲区,然后再由应用程序读取和处理。

TCP滑动窗口

TCP滑动窗口是TCP协议用来控制数据流量和确保可靠传输的机制。它规定了在等待接收方确认(ACK)之前,发送方可以发送的数据量。

  1. 发送窗口:发送方维护一个发送窗口,包含已发送但未确认的数据量。这个窗口限制了发送方在等待ACK之前可以发送的数据量。
  2. 接收窗口:接收方维护一个接收窗口,告诉发送方它可以接收的数据量。接收窗口大小通常由接收缓冲区的可用空间决定。

关系和协作

  1. 同步和协调

    • 发送缓冲区和发送窗口:发送缓冲区存储即将发送的数据,而发送窗口决定了在等待ACK之前可以发送的数据量。如果发送窗口变小(例如,接收方的接收缓冲区满了),发送方将减少发送速率,将更多数据留在发送缓冲区。
    • 接收缓冲区和接收窗口:接收缓冲区存储接收到但尚未被应用程序处理的数据。接收窗口大小通常反映了接收缓冲区的剩余空间。如果接收缓冲区快满了,接收窗口会变小,通知发送方减少发送速率。
  2. 窗口大小调整

    • 接收方根据接收缓冲区的可用空间动态调整接收窗口大小,并通过ACK包通知发送方。发送方则根据接收窗口的大小调整发送数据的速率。
    • 当接收缓冲区有足够空间时,接收方会增大接收窗口,允许发送方发送更多数据;当接收缓冲区空间不足时,接收方会减小接收窗口,限制发送方的数据传输速率。
  3. 数据流管理

    • 操作系统通过缓冲区管理和滑动窗口机制,确保数据流在网络上传输时的平稳性和可靠性。缓冲区用来平衡发送方和接收方的处理速度,而滑动窗口用来控制数据传输的节奏,避免网络拥堵和数据丢失。

窗口关闭

在TCP流量控制中,“窗口关闭”是指接收窗口的大小变为零的情况。这意味着接收方目前没有可用缓冲区空间来接收更多的数据,因此它告诉发送方暂停发送新的数据包,直到缓冲区有空闲空间为止。这种情况可能发生在接收方的应用程序处理数据速度较慢或者系统资源不足时。

  1. 窗口关闭的触发
  • 接收窗口为零:当接收方的缓冲区满了,接收窗口的大小就会变为零,通知发送方不要再发送数据。
  • ACK 包中的窗口大小:接收方在发送ACK包时,会在窗口大小字段中填入0,表示当前无法接收更多的数据。
  1. 发送方的反应
  • 停止发送数据:发送方在接收到窗口大小为零的ACK包后,会停止发送新的数据包。
  • Zero Window Probe:发送方会定期发送零窗口探测包(Zero Window Probe),询问接收方的窗口大小是否恢复。如果接收方的缓冲区有了新的可用空间,会回复一个窗口更新包。
  1. 窗口重新开放
  • 接收方缓冲区释放空间:接收方应用程序处理数据,释放缓冲区空间后,接收窗口会重新开放。
  • 更新窗口大小:接收方在下一次发送ACK包时,会更新窗口大小字段,通知发送方当前可以接收的数据量。
  • 恢复数据传输:发送方收到更新后的窗口大小后,继续发送数据。

窗口关闭潜在的危险

接收方向发送方通告窗口大小时,是通过 ACK 报文来通告的。

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

在这里插入图片描述

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

TCP 是如何解决窗口关闭时,潜在的死锁现象呢?

为了解决这个问题,TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器

如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。

在这里插入图片描述

  • 如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器;
  • 如果接收窗口不是 0,那么死锁的局面就可以被打破了。

窗口探测的次数一般为 3 次,每次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接。

糊涂窗口综合症

如果接收方太忙,没空处理数据,那么发送方的发送窗口就会越来越小。

到最后,接收方终于挤出一点时间,腾出了几个字节的空间,并通知发送方:“好了,现在有几个字节可以用了!” 结果,发送方二话不说就立即发送这几个字节的数据。这就是传说中的糊涂窗口综合症

要知道,TCP + IP 的头部就占了 40 个字节,为了传输这几个字节的数据要耗费这么多资源,真是亏大了。

这就好比一辆能载 50 人的大巴车,每次只来了几个人就发车。除非司机是个土豪,不然这么干迟早要破产。其实,解决这个问题也不难。大巴司机只需要等乘客人数超过 25 人再发车,这样就不会浪费资源啦。

要不,司机还可以挂个牌子:“车不满,不发车!”

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

  • 接收方可以通告一个小的窗口
  • 而发送方可以发送小数据

于是,要解决糊涂窗口综合症,就解决上面两个问题就可以了

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

怎么让接收方不通告小窗口呢?

接收方就像餐厅的后厨,如果后厨的盘子(缓存空间)快放不下了(窗口小于 min( MSS, 缓存空间的一半 )),就会对前台(发送方)说:“别再接单了!” 相当于通告窗口为 0。

然后后厨开始忙着把盘子上的菜端出去(处理数据)。当盘子上有足够的空间(窗口大小 >= MSS 或者缓存空间的一半可以使用),后厨就会对前台说:“可以继续接单了!” 这样前台就可以继续接新的订单(发送数据)。

这样一来,后厨就不会被堆积的订单压垮,也不会因为只处理几个订单就浪费大量资源。所有人都轻松,生意也更好。

怎么让发送方避免发送小数据呢?

Nagle 算法就像一个超级节约的快递公司老板,他有两个发货规则:

  • 如果车(窗口大小)能装满(>= MSS),或者快递包裹够大(>= MSS),就可以发货。
  • 必须等到之前发出的快递收到回执(ACK)才能发下一批。

如果这两个条件都没满足,老板就会一直囤积包裹,等到条件满足后再发货。

然而,对于一些需要快速小包裹来回跑的情况,比如送外卖(telnet 或 ssh),这种策略就不合适了。外卖小哥不能等满车再走,要不然客户早就饿晕了。这时,快递公司就会关闭节约模式(Nagle 算法),直接派人送餐(关闭 TCP_NODELAY 选项),保证外卖快速送达。

所以,不是所有情况下都需要节约模式,有时候迅速响应更重要!

可以在 Socket 设置 TCP_NODELAY 选项来关闭这个算法:

setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int));

拥塞控制

为什么要有拥塞控制呀,不是有流量控制了吗?

流量控制是为了防止发送方的数据量超过接收方的缓存能力,确保接收方不会被过多数据淹没。然而,这种控制机制无法感知网络层的状况。

在计算机网络中,网络带宽是共享资源,其他主机的通信可能导致网络拥堵。网络拥堵时,如果继续发送大量数据包,会增加数据包的延迟和丢包率。TCP 会尝试重传这些丢失的数据,但这会进一步加重网络负担,从而导致更高的延迟和更多的丢包,形成恶性循环

为了避免这种情况,TCP 引入了拥塞控制机制。拥塞控制的目的是防止发送方的数据填满整个网络。为此,TCP 使用了一个名为拥塞窗口的概念,该窗口用于限制发送方可以发送的数据量,从而降低网络拥塞的风险。拥塞窗口根据网络状况动态调整,确保数据传输既不超出接收方处理能力,也不会导致网络拥堵。

什么是拥塞窗口?和发送窗口有什么关系呢?

拥塞窗口(Congestion Window,CWND) 是 TCP 协议中用于拥塞控制的一个重要概念。它表示发送方在不引发网络拥塞的情况下,能够发送的最大数据量。拥塞窗口的大小动态调整,以反映网络当前的拥塞状况。具体来说,拥塞窗口的大小基于网络的反馈信息(如延迟、丢包等)来增加或减少,从而控制数据的发送速率,避免过度拥塞。

发送窗口(Send Window) 是流量控制的概念,表示发送方能够发送但尚未被接收方确认的数据量。发送窗口的大小由接收方的接收窗口(Receiver Window)和拥塞窗口(CWND)中较小者决定。接收窗口控制着发送方的最大发送数据量,以避免淹没接收方的缓存,而拥塞窗口则控制着网络的总负载,防止网络拥堵。

总的来说,发送窗口和拥塞窗口在数据传输中共同作用:发送窗口确保发送方不会超出接收方的处理能力,而拥塞窗口则防止发送方的数据过多地填满网络造成拥堵。最终,TCP 发送方会根据这两个窗口的最小值来决定实际可以发送的数据量。

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

其实,如果发送方在规定时间内没有收到 ACK 确认报文,也就是说发生了超时重传,发送方就会认为网络出现了拥塞。这是因为超时通常表明网络传输出现了问题,可能是由于网络拥堵或丢包造成的。因此,发送方会把这种超时情况视作网络拥塞的信号,并调整其发送策略以缓解拥塞。

拥塞控制有哪些控制算法?

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

  • 慢启动
  • 拥塞避免
  • 拥塞发生
  • 快速恢复

1. 慢启动(Slow Start):

  • 目的: 快速找出网络的最大容量。
  • 方法: 开始时用很小的数据量发送,接收到确认后,发送量快速增加。就像在探测水深时,从小处开始,然后快速加深。

2. 拥塞避免(Congestion Avoidance):

  • 目的: 平稳增加发送数据量,避免网络拥堵。
  • 方法: 当网络负担较重时,发送数据量的增加变得比较慢。就像在汽车进入拥堵区时,从加速驾驶改为平稳驾驶。

3. 快重传(Fast Retransmit):

  • 目的: 迅速重传丢失的数据包。
  • 方法: 如果发现某个数据包的确认收不到,发送方会快速重传这个数据包,而不是等到超时。就像收到一个丢失的邮件后,立刻重新发送。

4. 快恢复(Fast Recovery):

  • 目的: 在发现丢包后快速恢复数据传输。
  • 方法: 快重传后,拥塞窗口会减少到一半,然后逐渐恢复到正常速度。就像在堵车后,车速会先减慢,再慢慢恢复正常行驶。

拥塞算法示意图

在这里插入图片描述

参考

小林coding——TCP 重传、滑动窗口、流量控制、拥塞控好难?看完图解就不愁了(重制)
在这里插入图片描述

小结

啊哈,让我们以一种轻松愉快的方式总结一下TCP的那些事儿吧!

想象一下,你和你的朋友在玩“扔沙包”游戏,但是你们俩中间隔着一个特别长的隧道。为了让游戏进行得既公平又有趣,你们发明了一套规则:

  1. TCP重传 —— 如果你扔过去的沙包,你的朋友没接到,他会喊一声:“没接到,再扔一次!”于是你就得重新扔过去。这就是TCP重传,确保数据包准确无误地到达目的地。

  2. 滑动窗口 —— 假设你朋友的口袋只能装三个沙包,他告诉你:“一次扔三个,多了我接不住。”这就是滑动窗口,它控制了你一次能扔多少沙包,确保你的朋友能处理得过来。

  3. 流量控制 —— 有时候你的朋友在忙别的事,他就说:“慢点扔,我这忙着呢。”这就是流量控制,它确保你的朋友不会被沙包淹没,也就是防止数据溢出。

  4. 拥塞控制 —— 如果隧道里有很多人在玩这个游戏,大家都扔沙包,那隧道就会堵住。所以你们得聪明点,看看隧道里情况怎么样,适当调整扔沙包的频率,这就是拥塞控制,避免整个网络被数据包堵死。

所以,TCP协议就是你们玩“扔沙包”游戏时制定的一系列规则,确保游戏既好玩又不会让任何人受伤,或者让隧道变成沙包海洋。记住,无论游戏多么复杂,只要规则清晰,每个人都能玩得开心!

这就是TCP协议背后的“扔沙包”哲学,你get到了吗?下次当你在网络的海洋中遨游时,不妨想想你其实是在玩一场高级的“扔沙包”游戏哦!每一次点击、每一次加载,都是一次精准投掷和默契接应的展现!

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

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

相关文章

C++系列-list的模拟实现

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 构造函数生成 template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& data T()):_next(nullptr),_prev…

在 Android 上实现语音命令识别:详细指南

在 Android 上实现语音命令识别:详细指南 语音命令识别在现代 Android 应用中变得越来越普遍。它允许用户通过自然语言与设备进行交互,从而提升用户体验。本文将详细介绍如何在 Android 上实现语音命令识别,包括基本实现、带有占位槽位的命令处理,以及相关的配置和调试步骤…

Linux嵌入式学习——数据结构——概念和Seqlist

数据结构 相互之间存在一种或多种特定关系的数据元素的集合。 逻辑结构 集合&#xff0c;所有数据在同一个集合中&#xff0c;关系平等。 线性&#xff0c;数据和数据之间是一对一的关系。数组就是线性表的一种。 树&#xff0c; 一对多 图&#xff0c;多对多 …

压测实操--kafka broker压测方案

作者&#xff1a;九月 环境信息&#xff1a; 操作系统centos7.9&#xff0c;kafka版本为hdp集群中的2.0版本。 kafka broker参数 num.replica.fetchers&#xff1a;副本抓取的相应参数&#xff0c;如果发生ISR频繁进出的情况或follower无法追上leader的情况则适当增加该值&…

Java---String类

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 前言 在C语言中已经涉及到字符串了…

[前端]解决Iframe即使设置高度100%,但还是显示滚动条scrollbar的问题

前言 好烦,你看看这两个重复的滚动条. 一个是来自iframe,另一个来自父级的div(overflow: auto;) 我已经在css中设置了iframe的height: 100%;border: none;,但无论如何还是显示出了父级的scrollbar 解决 将iframe的display: block;即可. 或者vertical-align: bottom;

【启明智显分享】基于国产Model3芯片的7寸触摸屏助力智慧医疗,电子床头屏提升护理交互

未来医院必然是以信息化为基础&#xff0c;以物联网为特征&#xff0c;以医疗为核心的服务型医院。病房作为医院的重要服务场所&#xff0c;成为智慧医院建设的重要一环。 为提高医护人员与患者的互动交流&#xff0c;给医疗注入智慧元素&#xff0c;让患者享受智能服务&#…

java实现OCR图片识别,RapidOcr开源免费

先看一下识别效果&#xff08;自我感觉很牛逼&#xff09;&#xff0c;比Tess4J Tesseract省事&#xff0c;这个还需要训练&#xff0c;安装软件、下载语言包什么的 很费事&#xff0c;关键识别率不高 RapidOcr不管文字的横竖&#xff0c;还是斜的都能识别&#xff08;代码实现…

接口自动化测试框架实战-1-项目环境搭建

上一小节中我们讲解了一下本次接口自动化测试框架的大致架构设计和功能概览&#xff0c;本小节我们讲解一下整个项目开发环境的搭建方法。 1、python基础环境 安装python3版本&#xff1a;建议3.9.6版本及以上即可 新建项目的虚拟环境&#xff1a;virtualenv或者pycharm自带的…

张高兴的 MicroPython 入门指南:(三)使用串口通信

目录 什么是串口使用方法使用板载串口相互通信 硬件需求电路代码使用板载的 USB 串口参考 什么是串口 串口是串行接口的简称&#xff0c;这是一个非常大的概念&#xff0c;在嵌入式中串口通常指 UART(Universal Asynchronous Receiver/Transmitter&#xff0c;通用异步收发器)。…

深入浅出消息队列----【核心之消息的发送】

深入浅出消息队列----【核心之消息的发送】 普通消息同步消息异步消息 单向消息顺序消息延迟消息批量消息 本文仅是文章笔记&#xff0c;整理了原文章中重要的知识点、记录了个人的看法 文章来源&#xff1a;编程导航-鱼皮【yes哥深入浅出消息队列专栏】 根据 RocketMQ 官方&am…

工程视角:数据结构驱动的应用开发--字典(dictionary),列表(list)与实体

这里写目录标题 业务业务场景流程分析 实现数据访问层&#xff08;DAL&#xff09;业务逻辑层&#xff08;BLL&#xff09;用户界面层&#xff08;UI&#xff09;工具类 设计思路为什么抽出工具类关于U层使用字典的好处工程视角 业务 业务场景 在一个金融应用系统中&#xff0c…

【JavaScript】虚拟 DOM

虚拟 DOM 是⼀层对真实DOM的抽象&#xff0c;以JavaScript 对象 (VNode 节点) 作为基础的树&#xff0c;⽤对象的属性来描述节点&#xff0c;最终可以通过⼀系列操作使这棵树映射到真实环境上。 虚拟DOM 表现为⼀个 Object对象。并且最少包含标签名 (tag)、属性 (attrs) 和⼦元…

多路复用IO、TCP并发模型

时分复用 CPU单核在同一时刻只能做一件事情&#xff0c;一种解决办法是对CPU进行时分复用(多个事件流将CPU切割成多个时间片&#xff0c;不同事件流的时间片交替进行)。在计算机系统中&#xff0c;我们用线程或者进程来表示一条执行流&#xff0c;通过不同的线程或进程在操作系…

HarmonyOS 本地真机运行

目录 官网地址 1.开发工具设置签名 2.手机开启开发者模式 3.使用USB连接方式 4.使用无线调试连接方式 5.常见的问题 官网地址 使用真机运行应用 使用本地真机运行应用/服务 1.开发工具设置签名 官网应用/服务签名 1.左上角文件--项目结构-勾选自动生成签名-Sign in登录 2…

WEB前端08-综合案例(动态表格)

使用 HTML、CSS 和 JavaScript 创建动态表格 在本教程中&#xff0c;我们将创建一个动态表格&#xff0c;允许用户添加行、选择项目&#xff0c;并执行批量操作&#xff0c;如全选或删除选中的行。我们将通过 HTML、CSS 和 JavaScript 来实现这一功能。让我们逐步了解每个部分…

Vue前端工程化 安装Vue-Cli与node.js 最详细步骤(带图展示)

一、安装NodeJS 1.官网下载 https://nodejs.org/zh-cn 2.直接从百度网盘中提取安装 链接&#xff1a;https://pan.baidu.com/s/1OKhHZUwPCLamvd_08Vxx0g 提取码&#xff1a;61rw 3.开始安装 二、验证NodeJS环境变量 1.Win R 输入cmd打开控制面板 2.输入 node -v 如果出…

NVIDIA 全面转向开源 GPU 内核模块

NVIDIA 全面转向开源 GPU 内核模块 文章目录 NVIDIA 全面转向开源 GPU 内核模块支持的 GPU安装程序更改使用带有 CUDA 元包的包管理器 使用运行文件使用安装帮助脚本包管理器详细信息dnf&#xff1a;Red Hat Enterprise Linux、Fedora、Kylin、Amazon Linux 或 Rocky Linuxzypp…

程序员信息差,这个工具你必须知道

身为程序员&#xff0c;你是否也曾遇到过这样的情况&#xff1a;费尽心思搭建好服务器&#xff0c;开发好接口API&#xff0c;结果上线后却发现用户体验并不好&#xff0c;甚至还因为各种BUG忙得焦头烂额&#xff1f;别担心&#xff0c;你不是一个人。事实上&#xff0c;很多开…

用神经网络求解微分方程

微分方程是物理科学的主角之一&#xff0c;在工程、生物、经济甚至社会科学中都有广泛的应用。粗略地说&#xff0c;它们告诉我们一个量如何随时间变化&#xff08;或其他参数&#xff0c;但通常我们对时间变化感兴趣&#xff09;。我们可以了解人口、股票价格&#xff0c;甚至…