NS3学习——tcpVegas算法代码详解(2)

NS3学习——tcpVegas算法代码详解(1)-CSDN博客

目录

4.TcpVegas类中成员函数

(5) CongestionStateSet函数

(6) IncreaseWindow函数

1.检查是否启用 Vgas

2.判断是否完成了一个“Vegas 周期”

2.1--if:判断RTT样本数量是否足够

2.2--else:RTT 样本 > 2

2.2.1 if--diff > m_gamma 并且处于慢启动阶段

2.2.2 else if-- diff < m_gamma 并且处于慢启动阶段

2.2.3 else-- 进入拥塞避免阶段

2.2.3.1 --if diff > m_beta

2.2.3.2 --else if diff < m_alpha 

2.2.3.3 --else  m_alpha < diff < m_beta

2.2.4 --更新慢开始阈值

 2.3 --重置RTT计数与最小RTT

3.慢启动阶段判断

(7) GetName函数

(8) GetSsThresh函数


4.TcpVegas类中成员函数

(5) CongestionStateSet函数

void
TcpVegas::CongestionStateSet (Ptr<TcpSocketState> tcb,const TcpSocketState::TcpCongState_t newState)
{NS_LOG_FUNCTION (this << tcb << newState);if (newState == TcpSocketState::CA_OPEN){EnableVegas (tcb);}else{DisableVegas ();}
}

函数作用:根据TCP连接的拥塞状态来启用或者禁用Vegas算法。

函数体:检查传入的newState 参数值是否为:TcpSocketState::CA_OPEN(拥塞避免阶段),若是,则启用Vegas算法,TCP使用该算法来调整拥塞窗口的值;若不是,则停止使用Vegas。

TcpVegas 算法通常在拥塞避免阶段启用,因为此时网络已稳定,Vegas 可以通过动态调整拥塞窗口来更好地利用网络带宽,并避免网络拥塞。

(6) IncreaseWindow函数

void
TcpVegas::IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
{NS_LOG_FUNCTION (this << tcb << segmentsAcked);if (!m_doingVegasNow){NS_LOG_LOGIC ("Vegas is not turned on, we follow NewReno algorithm.");TcpNewReno::IncreaseWindow (tcb, segmentsAcked);return;}if (tcb->m_lastAckedSeq >= m_begSndNxt){ // A Vegas cycle has finished, we do Vegas cwnd adjustment every RTT.NS_LOG_LOGIC ("A Vegas cycle has finished, we adjust cwnd once per RTT.");m_begSndNxt = tcb->m_nextTxSequence;if (m_cntRtt <= 2){  // We do not have enough RTT samples, so we should behave like RenoNS_LOG_LOGIC ("We do not have enough RTT samples to do Vegas, so we behave like NewReno.");TcpNewReno::IncreaseWindow (tcb, segmentsAcked);}else //m_cntRtt > 2{NS_LOG_LOGIC ("We have enough RTT samples to perform Vegas calculations");uint32_t diff;uint32_t targetCwnd;uint32_t segCwnd = tcb->GetCwndInSegments ();double tmp = m_baseRtt.GetSeconds () / m_minRtt.GetSeconds ();targetCwnd = static_cast<uint32_t> (segCwnd * tmp);NS_LOG_DEBUG ("Calculated targetCwnd = " << targetCwnd);NS_ASSERT (segCwnd >= targetCwnd); // implies baseRtt <= minRttdiff = segCwnd - targetCwnd;NS_LOG_DEBUG ("Calculated diff = " << diff);if (diff > m_gamma && (tcb->m_cWnd < tcb->m_ssThresh)){NS_LOG_LOGIC ("We are going too fast. We need to slow down and ""change to linear increase/decrease mode.");segCwnd = std::min (segCwnd, targetCwnd + 1);tcb->m_cWnd = segCwnd * tcb->m_segmentSize;tcb->m_ssThresh = GetSsThresh (tcb, 0);NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd <<" ssthresh=" << tcb->m_ssThresh);}else if (tcb->m_cWnd < tcb->m_ssThresh){     // Slow start modeNS_LOG_LOGIC ("We are in slow start and diff < m_gamma, so we ""follow NewReno slow start");TcpNewReno::SlowStart (tcb, segmentsAcked);}else //tcb m_cWnd > m_ssThresh{     // Linear increase/decrease modeNS_LOG_LOGIC ("We are in linear increase/decrease mode");if (diff > m_beta){NS_LOG_LOGIC ("We are going too fast, so we slow down by decrementing cwnd");segCwnd--;tcb->m_cWnd = segCwnd * tcb->m_segmentSize;tcb->m_ssThresh = GetSsThresh (tcb, 0);NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd <<" ssthresh=" << tcb->m_ssThresh);}else if (diff < m_alpha){NS_LOG_LOGIC ("We are going too slow, so we speed up by incrementing cwnd");segCwnd++;tcb->m_cWnd = segCwnd * tcb->m_segmentSize;NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd <<" ssthresh=" << tcb->m_ssThresh);}else //   m_alpha < diff < m_beta{NS_LOG_LOGIC ("We are sending at the right speed");}} //else  tcb m_cWnd > m_ssThreshtcb->m_ssThresh = std::max (tcb->m_ssThresh, 3 * tcb->m_cWnd / 4);NS_LOG_DEBUG ("Updated ssThresh = " << tcb->m_ssThresh);} // else m_cntRtt > 2m_cntRtt = 0;m_minRtt = Time::Max ();} //if tcb->m_lastAckedSeq >= m_begSndNxtelse if (tcb->m_cWnd < tcb->m_ssThresh)  //tcb->m_lastAckedSeq < m_begSndNxt{TcpNewReno::SlowStart (tcb, segmentsAcked);}
} //IncreaseWindow

函数体逻辑:

1.检查是否启用 Vgas

if (!m_doingVegasNow)
{// If Vegas is not on, we follow NewReno algorithmNS_LOG_LOGIC ("Vegas is not turned on, we follow NewReno algorithm.");TcpNewReno::IncreaseWindow (tcb, segmentsAcked);return;
}

如果 m_doingVegasNow 为 false,即 Vegas 算法没有启用,那么执行 NewReno 拥塞控制算法。如果 Vegas 启用,那么执行以下代码:

2.判断是否完成了一个“Vegas 周期”

if (tcb->m_lastAckedSeq >= m_begSndNxt)
{// A Vegas cycle has finished, we do Vegas cwnd adjustment every RTT.NS_LOG_LOGIC ("A Vegas cycle has finished, we adjust cwnd once per RTT.");m_begSndNxt = tcb->m_nextTxSequence;

如果 tcb->m_lastAckedSeq发送方成功接收到的最后一个已确认包的序列号)大于等于 m_begSndNxt( Vegas 周期开始时的发送序列号),则表示当前已经完成了一个 Vegas 周期,并且将 m_begSndNxt 更新为当前的 tcb->m_nextTxSequence,以便下次周期开始时使用新的序列号。执行以下代码:

补:在 Vegas 算法中,一个周期是指发送方根据当前 RTT(往返时延)计算并调整其拥塞窗口(cwnd)的过程。这个周期通常与 RTT 周期同步。

m_lastAckedSeq 的变化非常重要,它帮助判断一个周期是否已经完成。每当接收方成功确认一个数据包,m_lastAckedSeq 会更新,以便发送方能知道哪些数据包已经被接收并得到确认。

在每个周期开始时,m_begSndNxt 会被更新为 当前发送序列号,而这个序列号代表的是 下一个将要发送的数据包的起始字节序列号。
当接收到的 ACK 包的序列号大于等于 m_begSndNxt 时,说明当前周期的所有数据包已经被确认,当前周期结束。

每个周期(每个 RTT)执行一次 IncreaseWindow。

tcb->m_nextTxSequence 是当前即将发送的下一个数据包的序列号。将 m_begSndNxt 更新为 tcb->m_nextTxSequence 是为了确保下一个周期从正确的地方开始。

2.1--if:判断RTT样本数量是否足够
if (m_cntRtt <= 2)
{// We do not have enough RTT samples, so we should behave like RenoNS_LOG_LOGIC ("We do not have enough RTT samples to do Vegas, so we behave like NewReno.");TcpNewReno::IncreaseWindow (tcb, segmentsAcked);
}

Vegas 需要足够的 RTT 样本才能做出可靠的拥塞窗口调整。

如果 RTT 样本数少于 2(即 m_cntRtt <= 2),它会退回到 NewReno 行为,这时会使用一个简单的慢启动和拥塞避免机制。

如果 RTT 样本 > 2,算法就会根据 Vegas 的逻辑调整cwnd值,同时执行else中的代码:

2.2--else:RTT 样本 > 2
else
{NS_LOG_LOGIC ("We have enough RTT samples to perform Vegas calculations");

计算目标拥塞窗口:

uint32_t diff;
uint32_t targetCwnd;
uint32_t segCwnd = tcb->GetCwndInSegments ();double tmp = m_baseRtt.GetSeconds () / m_minRtt.GetSeconds ();
targetCwnd = static_cast<uint32_t> (segCwnd * tmp);
NS_LOG_DEBUG ("Calculated targetCwnd = " << targetCwnd);
NS_ASSERT (segCwnd >= targetCwnd); // implies baseRtt <= minRtt

Vegas 计算目标拥塞窗口(targetCwnd),首先获取当前拥塞窗口大小 segCwnd,然后根据 baseRtt(最小 RTT)和 minRtt(当前窗口内最小 RTT)来计算目标拥塞窗口。

如果 baseRtt 小于等于 minRtt,就可以安全计算目标窗口。

NS_ASSERT (segCwnd >= targetCwnd);

计算公式如下:  

targetCwnd=segCwnd*\frac{baseRtt}{minRtt}

计算实际拥塞窗口与目标窗口的差值:

diff = segCwnd - targetCwnd;
NS_LOG_DEBUG ("Calculated diff = " << diff);

 计算当前拥塞窗口与目标拥塞窗口之间的差值 diff。这个差值会决定是否需要调整拥塞窗口的大小。

2.2.1 if--diff > m_gamma 并且处于慢启动阶段

当前窗口的差值 diff 大于阈值 m_gamma,并且当前处于慢启动阶段(cwnd 小于 m_ssThresh)

if (diff > m_gamma && (tcb->m_cWnd < tcb->m_ssThresh))
{// We are going too fast. We need to slow down and change from// slow-start to linear increase/decrease mode by setting cwnd// to target cwnd. We add 1 because of the integer truncation.NS_LOG_LOGIC ("We are going too fast. We need to slow down and ""change to linear increase/decrease mode.");segCwnd = std::min (segCwnd, targetCwnd + 1);tcb->m_cWnd = segCwnd * tcb->m_segmentSize;tcb->m_ssThresh = GetSsThresh (tcb, 0);NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd << " ssthresh=" << tcb->m_ssThresh);
}

m_alpha 和 m_beta 用于在正常的增速和减速中控制窗口的变化。m_gamma 是一个更大的阈值,通常用于判断网络是否发生了拥塞。

如果 diff > m_gamma,说明当前发送速率比目标速率快得多,且当前处于慢启动阶段。在慢启动阶段,cwnd 会急剧增长。如果在慢启动阶段的拥塞窗口已大于目标值,说明网络可能出现了拥塞的风险。

segCwnd = std::min(segCwnd, targetCwnd + 1):

调整当前cwnd,防止 segCwnd 超过目标窗口 targetCwnd,即避免发送方继续过快地发送数据。

由于 segCwnd 是以“段”为单位的(tcb->GetCwndInSegments()),加1的操作是为了避免整数截断。因为在计算过程中,通常会有一个小数部分,而加 1 可以保证计算结果向上取整,避免由于整数取整带来的问题。

比如,如果目标拥塞窗口是 targetCwnd = 5.4,由于取整的原因,segCwnd 可能被调整为 5,而加 1 后调整为 6。这样可以确保窗口不会太小,从而避免过早减速。

tcb->m_cWnd = segCwnd * tcb->m_segmentSize:

segCwnd 是拥塞窗口的大小(以段为单位)。
tcb->m_segmentSize 是每个 TCP 数据段的大小(字节数)。
segCwnd * tcb->m_segmentSize 得到的是字节级别的拥塞窗口大小,即实际可发送的数据量(以字节为单位)。通过这个公式,可以将段数(segCwnd)转换为字节数(tcb->m_cWnd),并调整发送窗口。

tcb->m_ssThresh = GetSsThresh(tcb, 0):

重新计算并设置新的慢启动阈值,用于控制从慢启动到拥塞避免阶段的过渡。

2.2.2 else if-- diff < m_gamma 并且处于慢启动阶段

当前的拥塞窗口小于慢启动阈值 m_ssThresh 并且 diff 小于 m_gamma

else if (tcb->m_cWnd < tcb->m_ssThresh)
{// Slow start modeNS_LOG_LOGIC ("We are in slow start and diff < m_gamma, so we ""follow NewReno slow start");TcpNewReno::SlowStart (tcb, segmentsAcked);
}

如果当前的拥塞窗口小于慢启动阈值 m_ssThresh,说明此时处于慢启动阶段。此时 diff 小于 m_gamma,表明网络没有拥塞,拥塞窗口仍然可以增长。

此时退回使用 NewReno 算法中的慢启动阶段,通过调用 TcpNewReno::SlowStart 来增加拥塞窗口。

2.2.3 else-- 进入拥塞避免阶段

当前的拥塞窗口大于慢启动阈值,tcb->m_cWnd 大于或等于 tcb->m_ssThresh

进入拥塞避免阶段,通过与目标 targetCwnd 的差值 diff 大小来选择是 增加窗口、减小窗口,还是 保持当前窗口。

else
{// Linear increase/decrease modeNS_LOG_LOGIC ("We are in linear increase/decrease mode");
2.2.3.1 --if diff > m_beta
if (diff > m_beta){// We are going too fast, so we slow downNS_LOG_LOGIC ("We are going too fast, so we slow down by decrementing cwnd");segCwnd--;tcb->m_cWnd = segCwnd * tcb->m_segmentSize;tcb->m_ssThresh = GetSsThresh (tcb, 0);NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd << " ssthresh=" << tcb->m_ssThresh);}
  • 这表示当前发送速率太快,实际的发送速率(segCwnd)已经超过了目标速率 targetCwnd。
  • 为了避免拥塞,减小 segCwnd,即减小拥塞窗口,从而减慢发送速率。
  • 减小后的 segCwnd 通过 tcb->m_cWnd = segCwnd * tcb->m_segmentSize; 更新。
  • 同时, 通过 GetSsThresh(tcb, 0) 更新慢启动阈值 tcb->m_ssThresh。
2.2.3.2 --else if diff < m_alpha 
 else if (diff < m_alpha){// We are going too slow, so we speed upNS_LOG_LOGIC ("We are going too slow, so we speed up by incrementing cwnd");segCwnd++;tcb->m_cWnd = segCwnd * tcb->m_segmentSize;NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd << " ssthresh=" << tcb->m_ssThresh);}
  • 这表示 当前发送速率太慢,实际的发送速率(segCwnd)低于目标速率 targetCwnd。
  • 为了加速数据传输,增加 segCwnd,即增大 拥塞窗口。
  • 增大的 segCwnd 同样通过 tcb->m_cWnd = segCwnd * tcb->m_segmentSize; 更新。
  • 在这种情况下,不需要调整慢启动阈值 tcb->m_ssThresh,因为它不会影响这一阶段的行为。
2.2.3.3 --else  m_alpha < diff < m_beta
else 
{NS_LOG_LOGIC ("We are sending at the right speed");
}
  • 这表示 当前发送速率合适,既不太快也不太慢,数据流量保持在理想状态。
  • 不需要对拥塞窗口做出调整,继续维持当前的速率。
2.2.4 --更新慢开始阈值
tcb->m_ssThresh = std::max (tcb->m_ssThresh, 3 * tcb->m_cWnd / 4);
NS_LOG_DEBUG ("Updated ssThresh = " << tcb->m_ssThresh);
  • 在(根据拥塞窗口和慢启动阈值大小比较)进行窗口调整之后,根据当前的拥塞窗口 tcb->m_cWnd 更新慢启动阈值(ssthresh)。
  • 计算公式 3 * tcb->m_cWnd / 4 是根据 Vegas 算法的设定,确保慢启动阈值不会过小。
  • 最终tcb->m_ssThresh 会被设置为 tcb->m_ssThresh 和 3 * tcb->m_cWnd / 4 中的较大值。这是为了确保慢启动阈值有足够的大小,避免在后续过程中频繁进入慢启动阶段。
 2.3 --重置RTT计数与最小RTT
m_cntRtt = 0;
m_minRtt = Time::Max ();
  • 由于每个周期结束都会重新进行 RTT 测量和窗口调整,所以需要重置 RTT计数(m_cntRtt)和最小RTT(m_minRtt)值。
  • m_cntRtt = 0:重置 RTT 样本计数器。
  • m_minRtt = Time::Max():将最小 RTT 重置为一个很大的值,确保下一周期开始时能够重新计算最小 RTT。

3.慢启动阶段判断

else if (tcb->m_cWnd < tcb->m_ssThresh)
{TcpNewReno::SlowStart(tcb, segmentsAcked);
}

在周期结束后,检查是否进入了慢启动阶段:

如果当前拥塞窗口 cwnd 小于慢启动阈值 ssThresh,则执行 NewReno 的慢启动算法,快速增长窗口。

注:如果tcb->m_lastAckedSeq < m_begSndNxt,表示当前 Vegas 周期没有结束,那么会进入 else if 判断,如果满足慢启动条件(tcb->m_cWnd < tcb->m_ssThresh),则会执行 NewReno 的慢启动算法。

为什么最后还要判断是否进入慢开始阶段?

如果 cwnd 小于 ssthresh,本应处于慢启动阶段,但由于没有判断 cwnd < ssthresh,程序会直接进入其他模式(如线性增加阶段)。这意味着即使 cwnd 还处于慢启动阶段,程序也会让它变得增长更慢。由于此时 cwnd 还较小,采用线性增长的方式会导致窗口增长太慢,无法迅速利用带宽,从而导致 网络利用率低,吞吐量上升的速度很慢,甚至不能充分利用网络的带宽。也就是说,可能会在不该进入线性增长阶段时就进入该阶段,从而导致 窗口增长速度过慢,降低网络利用率。

这个判断确保了在每个阶段执行适当的窗口调整策略,并帮助算法正确地处理不同网络状态下的拥塞控制。

(7) GetName函数

std::string
TcpVegas::GetName () const
{return "TcpVegas";
}

此函数主要用于标识 TCP 拥塞控制算法的类型,通过调用 GetName(),程序可以知道当前正在使用的是 TCP Vegas 算法。返回一个字符串,"TcpVegas"。

(8) GetSsThresh函数

uint32_t
TcpVegas::GetSsThresh (Ptr<const TcpSocketState> tcb,uint32_t bytesInFlight)
{NS_LOG_FUNCTION (this << tcb << bytesInFlight);return std::max (std::min (tcb->m_ssThresh.Get (), tcb->m_cWnd.Get () - tcb->m_segmentSize), 2 * tcb->m_segmentSize);
}} // namespace ns3

该函数的作用是计算和返回慢启动阈值(ssthresh)。

Ptr<const TcpSocketState>,指向当前连接的 TCP 套接字状态。TcpSocketState 中存储了关于当前 TCP 连接的许多信息,如拥塞窗口(m_cWnd)、慢启动阈值(m_ssThresh)等。
bytesInFlight:这通常表示当前已发送但尚未确认的数据量。这个参数在此函数中没有被直接使用。

tcb->m_ssThresh.Get():当前连接的慢启动阈值。
tcb->m_cWnd.Get() - tcb->m_segmentSize:计算拥塞窗口(cwnd)减去一个数据段的大小,表示如果当前 cwnd 足够大时,应该将 ssthresh 设置为接近这个值。
2 * tcb->m_segmentSize:这是 ssthresh 的最小值,表示即使拥塞窗口较小时,慢启动阈值也不会低于 2 * m_segmentSize。这个值是一个合理的下限,避免在拥塞窗口很小的时候,ssthresh 过小导致性能问题。

返回值:该函数通过 std::max() 和 std::min() 保证返回的 ssthresh 在合理的范围内:

std::min():确保 ssthresh 不会大于 cwnd - segmentSize,即不能超过当前拥塞窗口减去一个数据段的大小。
std::max():确保 ssthresh 不会小于 2 * segmentSize,即在任何情况下 ssthresh 至少为两个数据段大小。
最终返回值就是经过限制的 ssthresh,这是拥塞控制中切换模式的关键值。

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

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

相关文章

GitLab 将停止为中国区用户提供服务,60天迁移期如何应对? | LeetTalk Daily

“LeetTalk Daily”&#xff0c;每日科技前沿&#xff0c;由LeetTools AI精心筛选&#xff0c;为您带来最新鲜、最具洞察力的科技新闻。 GitLab作为一个广受欢迎的开源代码托管平台&#xff0c;近期宣布将停止服务中国大陆、澳门和香港地区的用户提供服务。根据官方通知&#x…

华为实训课笔记 2024 1223-1224

华为实训 12/2312/24 12/23 [Huawei]stp enable --开启STP display stp brief --查询STP MSTID Port Role STP State Protection 实例ID 端口 端口角色 端口状态 是否开启保护[Huawei]display stp vlan xxxx --查询制定vlan的生成树计算结…

《Java源力物语》-3.空值猎手

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” \quad 夜色渐深&#xff0c;在一处偏僻小径上&#xff0c;月光透过浓密的源力云层&#xff0c;在地面上投下斑驳的光影。String正独自练习着刚从…

科技云报到:人工智能时代“三大件”:生成式AI、数据、云服务

科技云报到原创。 就像自行车、手表和缝纫机是工业时代的“三大件”。生成式AI、数据、云服务正在成为智能时代的“新三大件”。加之全球人工智能新基建加速建设&#xff0c;成为了人类社会数字化迁徙的助推剂&#xff0c;让新三大件之间的耦合越来越紧密。从物理世界到数字世…

hiprint结合vue2项目实现静默打印详细使用步骤

代码地址是&#xff1a;vue-plugin-hiprint: hiprint for Vue2/Vue3 ⚡打印、打印设计、可视化设计器、报表设计、元素编辑、可视化打印编辑 本地安装包地址&#xff1a;electron-hiprint 发行版 - Gitee.com 1、先安装hipint安装包在本地 2、项目运行npm&#xff08;socket.…

CUDA各种内存和使用方法

文章目录 1、全局内存2、局部内存3、共享内存3.1 静态共享内存3.2 动态共享内存 4、纹理内存5、常量内存6、寄存器内存7、用CUDA运行时API函数查询设备CUDA 错误检测 1、全局内存 特点&#xff1a;容量最大&#xff0c;访问延时最大&#xff0c;所有线程都可以访问。 线性内存…

Chapter 03 复合数据类型-1

1.列表 Python内置的一种有序、可变的序列数据类型&#xff1b; 列表的定义&#xff1a; [ ]括起来的逗号分隔的多个元素组成的序列 列表对象的创建&#xff1a; &#xff08;1&#xff09;直接赋值 >>> list1 []#创建一个空列表赋值给list1 >>> list…

【后端】LNMP环境搭建

长期更新各种好文&#xff0c;建议关注收藏&#xff01; 本文近期更新完毕。 LNMPlinuxnginxmysqlphp 需要的资源 linux服务器 web服务软件nginx 对应的语言编译器代码文件 数据库mysql安装 tar.gz包或者命令行安装 进入root&#xff1a; sodu 或su mkdir path/{server,soft}…

基于PyQt5的UI界面开发——多界面切换

介绍 最初&#xff0c;因为课设的缘故&#xff0c;我只是想做一个通过按键进行切面切换而已&#xff0c;但是我看网上资料里面仅是语焉不详&#xff0c;让我困惑的很&#xff0c;但后面我通过摸索才发现这件事实在是太简单了&#xff0c;因此我想要记录下来。 本博客将介绍如…

操作002:HelloWorld

文章目录 操作002&#xff1a;HelloWorld一、目标二、具体操作1、创建Java工程①消息发送端&#xff08;生产者&#xff09;②消息接收端&#xff08;消费者&#xff09;③添加依赖 2、发送消息①Java代码②查看效果 3、接收消息①Java代码②控制台打印③查看后台管理界面 操作…

机器视觉检测相机基础知识 | 颜色 | 光源 | 镜头 | 分辨率 / 精度 / 公差

注&#xff1a;本文为 “keyence 视觉沙龙中机器视觉检测基础知识” 文章合辑。 机器视觉检测基础知识&#xff08;一&#xff09;颜色篇 视觉检测硬件构成的基本部分包括&#xff1a;处理器、相机、镜头、光源。 其中&#xff0c;和光源相关的最重要的两个参数就是光源颜色和…

【体验官招募】SoFlu - JavaAI 开发助手:开启智能开发新时代

你是否有过这样的经历&#xff1f;在深夜的办公室里&#xff0c;面对紧急的 Java 项目&#xff0c;看着厚厚的需求文档&#xff0c;你是否感到无从下手&#xff1f; 当你尝试理解客户那些复杂又模糊的需求时&#xff0c;是否会因为要和产品经理反复沟通确认每一个细节而感到厌…

自学记录HarmonyOS Next DRM API 13:构建安全的数字内容保护系统

在完成了HarmonyOS Camera API的开发之后&#xff0c;我开始关注更复杂的系统级功能。在浏览HarmonyOS Next文档时&#xff0c;我发现了一个非常有趣的领域&#xff1a;数字版权管理&#xff08;DRM&#xff09;。最新的DRM API 13提供了强大的工具&#xff0c;用于保护数字内容…

【HENU】河南大学计院2024 操作系统 简答题复习

和光同尘_我的个人主页 一直游到海水变蓝。 单项选择 15x2 30 判断 10x1 10 简答 3x10 30 综合 3x10 30 简答题 简述操作系统的四个基本特征。 并发性 共享性 虚拟性 异步性 并发性是最重要特性&#xff0c;其它三种特性以此为前提。 并发 并发(Concurrence)&#…

GEE错误——PCA系数变换的时候出现的错误

目录 错误提示1 错误提示2 原始的教程链接&#xff1a; 错误代码 修正后的代码 结果 错误提示1 这个是因为原始GEE教程中给的让我们填入需要进行计算的波段名称&#xff0c;而且是以list的形式传入。 错误提示2 这里我们虽然传入了正确的波段名称&#xff0c;但是发现要…

C#代码实现把中文录音文件(.mp3 .wav)转为文本文字内容

我们有一个中文录音文件.mp3格式或者是.wav格式&#xff0c;如果我们想要提取录音文件中的文字内容&#xff0c;我们可以采用以下方法&#xff0c;不需要使用Azure Speech API 密钥注册通过离线的方式实现。 1.首先我们先在NuGet中下载两个包 NAudio 2.2.1、Whisper.net 1.7.3…

计算机操作系统与安全复习笔记

1 绪论 操作系统目标: 方便性; 有效性; 可扩充性; 开放性. 作用: 用户与计算机硬件系统之间的接口; 计算机资源的管理者; 实现了对计算机资源的抽象; 计算机工作流程的组织者. 多道程序设计: 内存中同时存放若干个作业, 使其共享系统资源且同时运行; 单处理机环境下宏观上并行…

qt5.12.11+msvc编译器编译qoci驱动

1.之前编译过minGW编译器编译qoci驱动,很顺利就完成了,文章地址:minGW编译qoci驱动详解,今天按照之前的步骤使用msvc编译器进行编译,直接就报错了: 查了些资料,发现两个编译器在编译时,pro文件中引用的库不一样,下面是msvc编译器引用的库,其中编译引用的库我这里安装…

Java爬虫实战:深度解析VIP商品详情获取技术

在数字化时代&#xff0c;数据的价值不言而喻。对于电商平台而言&#xff0c;掌握VIP商品的详细信息是提升服务质量、优化用户体验的关键。然而&#xff0c;这些信息往往被复杂的网页结构和反爬虫策略所保护。本文将带你深入了解如何使用Java编写爬虫&#xff0c;以安全、高效地…

校史馆云展厅适合远程教学吗?

随着信息技术的飞速发展&#xff0c;远程教学已经成为教育领域的一个重要趋势。 校史馆作为学校文化传承的重要场所&#xff0c;承载着丰富的历史信息和教育资源。 那么&#xff0c;将校史馆搬到云端&#xff0c;构建云展厅&#xff0c;是否适合远程教学呢&#xff1f; 下面…