当前位置: 首页 > news >正文

WebRTC服务质量(08)- 重传机制(05) RTX机制

WebRTC服务质量(01)- Qos概述
WebRTC服务质量(02)- RTP协议
WebRTC服务质量(03)- RTCP协议
WebRTC服务质量(04)- 重传机制(01) RTX NACK概述
WebRTC服务质量(05)- 重传机制(02) NACK判断丢包
WebRTC服务质量(06)- 重传机制(03) NACK找到真正的丢包
WebRTC服务质量(07)- 重传机制(04) 接收NACK消息
WebRTC服务质量(08)- 重传机制(05) RTX机制
WebRTC服务质量(09)- Pacer机制(01) 流程概述
WebRTC服务质量(10)- Pacer机制(02) RoundRobinPacketQueue
WebRTC服务质量(11)- Pacer机制(03) IntervalBudget
WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

一、前言:

RTX协议(Retransmission,即重传协议)是 WebRTC 中用于处理丢包恢复的一部分。由于网络通信中的丢包不可避免,WebRTC RTP协议栈支持多种丢包恢复机制,其中之一便是通过RTX协议实现的重传机制

RTX协议会根据 RTP 丢包检测机制(具体由 RTCP 的 NACK 包触发),将数据包重传给接收端。RTX 的核心思想是发送端将丢失的数据包重新封装并发送,而接收端根据识别重新封装的数据包进行补偿处理,从而还原完整的流媒体内容。

二、RTX协议的工作原理:

RTX 的工作紧密依赖于 RTP 和 RTCP 协议。以下是 RTX 的通信流程:

  1. 发送端发送数据包: 发送端按照 RTP 协议发送音视频流,每个 RTP 数据包都有唯一的序列号(sequence number),用于接收端检测是否丢包。
  2. 接收端检测丢包: 接收端通过接收 RTP 包的序列号,以及 RTP/RTCP 的统计信息,检测数据是否丢失。一旦丢包,接收端会发送 RTCP NACK(Negative Acknowledgment)反馈包给发送端,并指定丢失数据包的序列号。
  3. 发送端重新发送丢失的数据包: 发送端根据接收到的 RTCP NACK 请求,使用 RTX 流重传对应的丢失数据包。RTX 流不同于主流(primary stream),通常会有独立的 SSRC(Synchronization Source Identifier)用于区分两者。
  4. 接收端合并重传包: 接收端接收到 RTX 包后,根据 RTX 的特殊封装格式提取出原始 Payload(数据载荷)并通过序列号将其合并到主流中。

RTX 的核心点在于:

  • 独立的 SSRC:RTX 使用独立的 SSRC,和主流区分开,这样可以识别重传流。
  • 修改过的 RTP 包头:RTX 包在 RTP 层修改了一些元信息,用于兼容重传流和主流的处理。
  • RTX包有自己的payload type
  • RTX包是按照自己的Sequence Number进行排序的。

三、如何找到RTX包:

  • 找到Offer/Answer这种SDP信息。
  • 从SDP中找到RTX的SSRC。
  • 然后抓包,根据SSRC过滤出RTX包。

四、RTX协议格式:

在这里插入图片描述

  1. RTP Header:和前面介绍协议时候的RTP Header是一样的;
  2. OSN:重传的是哪个原始数据的序列包,注意我们RTX属于RTP协议,这儿的Original指的是RTP包;
  3. 可以看出RTX就是普通的RTP协议加了2字节的OSN,注意不是RTCP;

五、抓取RTX包:

5.1、获取本机的RTX流:

在这里插入图片描述

看看RTX的seq:

在这里插入图片描述

seq独立可以更好的统计出丢包。

5.2、看看原始流哪些包丢失了:

在这里插入图片描述

BLP是0x0001。知道BLP是什么吗?

1)NACK PID和NACK BLP:

1)概念:

在 NACK 中,NACK PID 和 NACK BLP 是两个关键的字段,用于标识和管理需要重传的数据包。

  • NACK PID:NACK PID(Packet ID)是 NACK 报文中的一个字段,用于指示需要重传的丢失数据包的序列号。当接收端检测到数据包丢失时,会发送 NACK 报文,其中 NACK PID 指示了具体丢失的数据包的序列号。
  • NACK BLP:NACK BLP(Bitmask of Lost Packets)是 NACK 报文中的另一个字段,用于指示一连串连续丢失的数据包。NACK BLP 是一个比特掩码,每个比特位对应一个数据包序列号,用于表示一段连续的丢失数据包范围。

2)举个例子:

假设在一个 WebRTC 实时通信会话中,发送端发送了一系列 RTP 数据包给接收端,序列号分别为 10、11、12、13、14、15。接收端在接收过程中发现数据包 11 和 13 丢失了。接收端会发送 NACK 报文给发送端,请求重传这两个丢失的数据包。

在这个例子中,NACK 报文中的 NACK PID 和 NACK BLP 可能会被设置如下:

  • NACK PID:11, 13
    • NACK PID 指示需要重传的具体丢失数据包的序列号,即数据包 11 和数据包 13。
  • NACK BLP:010010
    • 在这个二进制掩码中,每个比特位对应一个数据包序列号。接收端标记了丢失的数据包范围,从 10 到 15 中的第 2 和第 4 个数据包丢失。
    • 这表示数据包 11 和数据包 13 丢失,而其他数据包正常接收。

3)本文丢包情况:

NACK 报文中 NACK PID 的值为 6806,而 NACK BLP 的值为 0x0001,可以解释如下:

  • NACK PID:6806
    • NACK PID 表示需要重传的具体丢失数据包的序号,即数据包序号为 6806 的数据包需要进行重传。
  • NACK BLP:0x0001
    • NACK BLP 是一个十六进制数,转换为二进制为 0000 0000 0000 0001。
    • 在这个二进制掩码中,每个比特位对应一个数据包序号。由于只有一个比特位为 1,表示只有一个数据包丢失。
    • 在这种情况下,第一个(最低位)的 1 表示序号为 6806 的数据包丢失,其它数据包接收正常。

5.3、使用RTX进行重传:

如果你在抓包软件中找不到RTX包,记得使用时间戳来找。

在这里插入图片描述

因此发送的RTX一定在这个点之后;

在这里插入图片描述

  1. 可以看出872691小于收到NACK的时间点876269,肯定不是。
  2. 881999大于收到NACK的时间点,可能是。
  3. 通过前面协议我们知道RTX的前2个字节是OSN,表示原始数据的序列号,因此,我们将0x1a96转换为十进制看是多少;

在这里插入图片描述

看到没有,重传的额就是6806这个数据包。

六、发送RTX的过程:

我们得先看下NACK接收流程:

在这里插入图片描述

会来到这里:

void RTPSender::OnReceivedNack(const std::vector<uint16_t>& nack_sequence_numbers,int64_t avg_rtt) {packet_history_->SetRtt(5 + avg_rtt);// 遍历nack中每个需要重传的seq,进行重传for (uint16_t seq_no : nack_sequence_numbers) {// 发送RTXconst int32_t bytes_sent = ReSendPacket(seq_no);if (bytes_sent < 0) {// Failed to send one Sequence number. Give up the rest in this nack.RTC_LOG(LS_WARNING) << "Failed resending RTP packet " << seq_no<< ", Discard rest of packets.";break;}}
}

看看具体怎么重传的:

int32_t RTPSender::ReSendPacket(uint16_t packet_id) {absl::optional<RtpPacketHistory::PacketState> stored_packet =packet_history_->GetPacketState(packet_id);if (!stored_packet || stored_packet->pending_transmission) {return 0;}const int32_t packet_size = static_cast<int32_t>(stored_packet->packet_size);const bool rtx = (RtxStatus() & kRtxRetransmitted) > 0;std::unique_ptr<RtpPacketToSend> packet =packet_history_->GetPacketAndMarkAsPending(packet_id, [&](const RtpPacketToSend& stored_packet) {std::unique_ptr<RtpPacketToSend> retransmit_packet;if (retransmission_rate_limiter_ &&!retransmission_rate_limiter_->TryUseRate(packet_size)) {return retransmit_packet;}if (rtx) {retransmit_packet = BuildRtxPacket(stored_packet);} else {retransmit_packet =std::make_unique<RtpPacketToSend>(stored_packet);}if (retransmit_packet) {retransmit_packet->set_retransmitted_sequence_number(stored_packet.SequenceNumber());}return retransmit_packet;});if (!packet) {return -1;}packet->set_packet_type(RtpPacketMediaType::kRetransmission);packet->set_fec_protect_packet(false);std::vector<std::unique_ptr<RtpPacketToSend>> packets;packets.emplace_back(std::move(packet));paced_sender_->EnqueuePackets(std::move(packets));return packet_size;
}

我们根据代码逻辑发现,并不一定要使用RTX:

  1. 如果启用了 RTX(Retransmission),则构造并发送独立的 RTX 包。
  2. 如果没有启用 RTX,则直接从包历史记录中取出丢失的原始 RTP 包并重传。

代码比较负责,关键部分展开说明下:

1)构造重传包:

  • 匿名函数:

    std::unique_ptr<RtpPacketToSend> packet =packet_history_->GetPacketAndMarkAsPending(packet_id, [&](const RtpPacketToSend& stored_packet) {std::unique_ptr<RtpPacketToSend> retransmit_packet;// 检查重传速率限制if (retransmission_rate_limiter_ &&!retransmission_rate_limiter_->TryUseRate(packet_size)) {return retransmit_packet;}if (rtx) {// 如果支持 RTX,构造一个 RTX 包retransmit_packet = BuildRtxPacket(stored_packet);} else {// 否则直接使用原始 RTP 包retransmit_packet =std::make_unique<RtpPacketToSend>(stored_packet);}if (retransmit_packet) {retransmit_packet->set_retransmitted_sequence_number(stored_packet.SequenceNumber());}return retransmit_packet;});

    这段代码的核心是获取丢失的历史包并将其打包为重传包:

    1. 调用 packet_history_->GetPacketAndMarkAsPending
      • 从历史记录中取出丢失的 RTP 包,并标记该包已进入待发送状态(防止重复重传)。
    2. 使用匿名函数对包进行加工处理
      • 重传速率限制:
        • 如果当前重传速率超出了允许的带宽,则直接丢弃该重传包,不做进一步处理。
      • RTX 构造:
        • 如果启用了 RTX,通过调用 BuildRtxPacket 方法,将原始包封装为 RTX 包。
        • 关键点:RTX 包的特殊结构包含原始包的 OSN(Original Sequence Number),用于表示其原始 RTP 包的序列号。
      • 普通重传包:
        • 如果没有启用 RTX,则直接复制原始 RTP 包来重发。
      • 设置序列号:
        • 调用 set_retransmitted_sequence_number,设置重传包所对应的原始序列号。

2)重传包的最终处理:

if (!packet) {return -1;
}packet->set_packet_type(RtpPacketMediaType::kRetransmission);
packet->set_fec_protect_packet(false);
std::vector<std::unique_ptr<RtpPacketToSend>> packets;
packets.emplace_back(std::move(packet));// 将重传包加入到 paced sender 的发送队列
paced_sender_->EnqueuePackets(std::move(packets));
  1. 检查是否成功获取重传包:
    • 如果没有生成有效的重传包(可能被带宽限制丢弃),返回 -1
  2. 设置包类型:
    • 通过 set_packet_type 设置为 kRetransmission,标明这是一个重传包。
    • set_fec_protect_packet(false):通知不对重传包应用 FEC(前向纠错)保护,因为 RTX 本身是另一个冗余机制。
  3. 插入发送队列:
    • 将重传包添加到 paced_sender_(节奏发送器)模块中,以有节奏地发送包,避免瞬时大量重传耗尽带宽。

3)小结:

  1. RTX 重传的实现流程
    • RTX 重传包通过函数 BuildRtxPacket 生成。
    • RTX 中包含一个特殊的字段 OSN,用于恢复原始包的序列号。
    • 通过独立的 SSRC 和 Payload Type 区分 RTX 包与普通主流包。
  2. 带宽管理和速率控制
    • retransmission_rate_limiter_ 限制了重传包的发送速率,防止网络因重传过载。
  3. 灵活处理重传策略
    • 支持两种模式:RTX 和普通 RTP 重传。
    • RTX 是更成熟的丢包恢复机制,但需要双方支持;否则退回到简单的 RTP 重传方式。

七、总结:

RTPSender::ReSendPacket 函数是 WebRTC 中 RTP 协议层实现丢包恢复的重要部分。它利用 RtpPacketHistory 记录和 RTX 协议实现高效的重传机制,同时考虑到带宽管理、速率限制和两种重传策略(RTX 和普通 RTP)的兼容性。在具体代码实现中,它通过灵活的封装和严格的速率控制,保证了无损恢复的同时避免了网络拥塞情况。

相关文章:

WebRTC服务质量(08)- 重传机制(05) RTX机制

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…...

Go的select的运行原理

Go语言中的select语句是一种专门用于处理多个通道&#xff08;channel&#xff09;操作的控制结构。其运行原理可以概括为以下几点&#xff1a; 1. 监听多个通道 select语句能够同时监听多个通道上的操作&#xff0c;这些操作可以是发送操作或接收操作。每个通道操作都对应se…...

操作002:HelloWorld

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

3D坐标下,一点在某一线段上的左右方向的判定

3D坐标下&#xff0c;一点在某一线段上的左右方向的判定 代码 代码 #include <iostream> #include <Eigen/Dense>#define M_PI 3.1415926// 计算三点组成平面的参数和变换到XOY平面的变换矩阵 void computePlaneAndTransform(const Eigen::Vector3d& P1, cons…...

Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码 【AI辅助开发系列】

&#x1f380;&#x1f380;&#x1f380;【AI辅助编程系列】&#x1f380;&#x1f380;&#x1f380; Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码Visual Studio 安装和管理 GitHub CopilotVisual Studio 使用 GitHub Copilot 扩展Visual Studio 使用 GitHu…...

Linux高级--2.4.5 靠协议头保证传输的 MAC/IP/TCP/UDP---协议帧格式

任何网络协议&#xff0c;都必须要用包头里面设置写特殊字段来标识自己&#xff0c;传输越复杂&#xff0c;越稳定&#xff0c;越高性能的协议&#xff0c;包头越复杂。我们理解这些包头中每个字段的作用要站在它们解决什么问题的角度来理解。因为没人愿意让包头那么复杂。 本…...

【UE5 C++课程系列笔记】14——GameInstanceSubsystem与动态多播的简单结合使用

效果 通过在关卡蓝图中触发GameInstanceSubsystem包含的委托&#xff0c;来触发所有绑定到这个委托的事件&#xff0c;从而实现跨蓝图通信。 步骤 1. 新建一个C类 这里命名为“SubsystemAndDelegate” 引入GameInstanceSubsystem.h&#xff0c;让“SubsystemAndDelegate”继承…...

PyQt实战——随机涂格子的特色进度条(十一)

系类往期文章&#xff1a; PyQt5实战——多脚本集合包&#xff0c;前言与环境配置&#xff08;一&#xff09; PyQt5实战——多脚本集合包&#xff0c;UI以及工程布局&#xff08;二&#xff09; PyQt5实战——多脚本集合包&#xff0c;程序入口QMainWindow&#xff08;三&…...

.NET 8.0 项目升级到 .NET 9.0

本文项目从.NETCore3.1开始一直延续到目前&#xff0c;如果您没有升级过&#xff0c;请参考以下文章&#xff1a; .Net Core 2.2 升级到 .Net Core 3.1&#xff1a;https://blog.csdn.net/hefeng_aspnet/article/details/131259537 NetCore3.1或Net6.0项目升级到Net7.0&#x…...

用Python写炸金花游戏

文章目录 **代码分解与讲解**1. **扑克牌的生成与洗牌**2. **给玩家发牌**3. **打印玩家的手牌**4. **定义牌的优先级**5. **判断牌型**6. **确定牌型优先级**7. **比较两手牌的大小**8. **打印结果** 完整代码 以下游戏规则&#xff1a; 那么我们要实现的功能&#xff0c;就是…...

深度学习中的并行策略概述:2 Data Parallelism

深度学习中的并行策略概述&#xff1a;2 Data Parallelism 数据并行&#xff08;Data Parallelism&#xff09;的核心在于将模型的数据处理过程并行化。具体来说&#xff0c;面对大规模数据批次时&#xff0c;将其拆分为较小的子批次&#xff0c;并在多个计算设备上同时进行处…...

电商平台能挡住恶意网络爬虫的攻击吗?

爬虫盗取电商数据的步骤 爬虫技术作为一种数据获取工具&#xff0c;正逐渐成为电商平台的一大隐患。网络爬虫不仅能够获取商家关键信息并滋生仿冒网站&#xff0c;还能收集用户敏感信息&#xff0c;对用户的财产安全和隐私造成严重威胁。同时&#xff0c;爬虫攻击还会扰乱正常…...

Jenkins安装方法二

配置环境 和 Jenkins 官方的 yum 源之后进行安装 # 关闭防火墙 $ sudo systemctl stop firewalld $ sudo systemctl disable firewalld# 安装 EPEL 源 $ sudo yum install -y epel-release # 安装 wget $ sudo yum install -y wget# 配置 Jenkins 官方 yum 源 $ sudo wget -O /…...

Nginx性能优化全方案:打造一个高效服务器

提到前面&#xff1a;一个热衷技术&#xff0c;反对八股的资深研发&#xff0c;不卖课不引流&#xff0c;专注分享高质量教学博客。 如果觉得文章还不错的话&#xff0c;可以点赞收藏关注 支持一下&#xff0c;持续分享高质量技术博客。 如果有什么需要改进的地方还请大佬指出❌…...

【每日学点鸿蒙知识】沙箱目录、图片压缩、characteristicsArray、gm-crypto 国密加解密、通知权限

1、HarmonyOS 如何创建应用沙箱目录&#xff1f; 下载文件&#xff0c;想下载到自己新建的应用沙箱目录&#xff0c;有什么方法实现吗&#xff1f; fs.mkdir可以创建目录 参考文档&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis…...

XMLHttpRequest的基础知识

get请求 const xml new XMLHttpRequest(); xml.open("GET", "https://jsonplaceholder.typicode.com/todos/1", true); xml.onreadystatechange function () {if (xml.readyState 4 && xml.status 200) {console.log(xml.responseText);} }…...

学习笔记(C#基础书籍)-- C#基础篇

&#xff08;12.24&#xff09; C#介绍&#xff1a;《第一章》 特点&#xff1a;语法简洁&#xff0c;面向对象&#xff0c;支持绝大部分的web标准&#xff0c;强大的安全机制&#xff08;垃圾回收器&#xff09;&#xff0c;兼容性好&#xff08;遵循.NET的公共语言规范【CL…...

现在有什么赛道可以干到退休?

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;点击跳转到网站 &#xff0c;对人工智能感兴趣的小伙伴可以点进去看看。 最近&#xff0c;一则“90后无论男女都得65岁以后退休”的消息在多个网…...

【VScode】第三方GPT编程工具-CodeMoss安装教程

一、CodeMoss是什么&#xff1f; CodeMoss是一款集编程、学习和办公于一体的高效工具。它兼容多种主流平台&#xff0c;包括VSCode、IDER、Chrome插件、Web和APP等&#xff0c;支持插件安装&#xff0c;尤其在VSCode和IDER上的表现尤为出色。无论你是编程新手还是资深开发者&a…...

选择屏幕的用法

**************************定义控件*********************************** SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-002. SELECT-OPTIONS bukrs FOR iloa-bukrs . "公司代码 SELECT-OPTIONS swerk FOR iloa-swerk OBLIGATORY . "工厂 SELECT-O…...

VirtualBox下ubuntu23.04使用主机串口以及使用 minicom 进行串口调试

VirtualBox下ubuntu23.04使用主机串口以及使用 minicom 进行串口调试 一、打开设备管理器看主机&#xff08;Window系统&#xff09;是否识别出串口&#xff0c;我这边显示的串行通信端口是COM3 二、打开VirtualBox&#xff0c;设置串口和USB设备 串口设置&#xff1a; 启用…...

CH340系列芯片驱动电路·CH340系列芯片驱动!!!

目录 CH340基础知识 CH340常见类型 CH340引脚功能讲解 CH340驱动电路 CH340系列芯片数据手册 编写不易&#xff0c;仅供学习&#xff0c;请勿搬运&#xff0c;感谢理解 常见元器件驱动电路文章专栏连接 LM7805系列降压芯片驱动电路降压芯片驱动电路详解-CSDN博客 ME62…...

Unity中使用环形缓冲区平滑抖动值

环形缓冲数据结构&#xff0c;就是如下图一样的一个收尾相接的列表 在index指针指到4时&#xff0c;再往里添加数据&#xff0c;index就会指向0&#xff0c;并覆盖已有数据。 如何绘制Sin函数&#xff0c;请看下面一篇文章 Unity中如何实现绘制Sin函数图像-CSDN博客 接下来要…...

如何通过HTTP API插入或更新Doc

本文介绍如何通过HTTP API向Collection中插入或更新Doc。 说明 若调用本接口时Doc Id已存在&#xff0c;则等同于更新Doc&#xff1b;Doc Id不存在&#xff0c;则等同于插入Doc。 若调用本接口时不指定Doc Id&#xff0c;则等同于插入Doc&#xff0c;DashVector会自动生成Doc …...

RTMW:实时多人2D和3D 全人体姿态估计

单位&#xff1a;上海AI实验室 代码&#xff1a;mmpose/tree/main/projects/rtmpose 系列文章目录 RTMO: 面向高性能单阶段的实时多人姿态估计 目录 系列文章目录摘要一、背景二、相关工作2.1 自上而下的方法。2.2 坐标分类。2.3 3D Pose 3 实验方法3.1.1 任务限制3.1.3训练技…...

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

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

macrodroid通过http请求控制手机运行宏

macrodroid adb命令 adb shell pm grant com.arlosoft.macrodroid android.permission.WRITE_SECURE_SETTINGS例:http请求手机播放指定MP3文件 声音素材_电量过低提醒 新建一个宏 添加触发器-连接-http服务器请求 路径随意填,最好不要有特殊符号,不然浏览器识别链接会出错,…...

Redis分片集群+MQ处理高并发

Redis的三大集群模式&#xff1a;主从复制、哨兵模式和Cluster模式。每种模式都有其特点和应用场景&#xff0c;具体如下&#xff1a; 主从复制模式&#xff1a;适用于数据备份和读写分离场景&#xff0c;配置简单&#xff0c;但在主节点故障时需要手动切换。哨兵模式&#xff…...

Linux内核 -- UIO (User-space I/O) 简介与使用笔记

UIO (User-space I/O) 简介 UIO (User-space I/O) 是 Linux 内核提供的一种机制&#xff0c;用于简化设备驱动的开发。它将设备的硬件资源&#xff08;如内存映射、中断等&#xff09;通过简单的接口暴露给用户空间程序&#xff0c;从而使用户可以在用户空间编写复杂的设备逻辑…...

《计算机组成及汇编语言原理》阅读笔记:p86-p115

《计算机组成及汇编语言原理》学习第 6 天&#xff0c;p86-p115 总结&#xff0c;总计 20 页。 一、技术总结 1.if statement 2.loop 在许多编程语言中&#xff0c;有类种循环&#xff1a;一种是在程序开头检测条件(test the condition),另一种是在程序末尾检测条件。 3.C…...

Python爬虫实战(保姆级登网页信息爬取教程)

此blog为爬虫实战教学&#xff0c;代码已附上&#xff0c;可以复制运行。若要直接看实战代码翻到博客后半部分。 本文使用selenium库进行爬虫&#xff0c;实现爬取数据操作&#xff0c;此库是通过模仿用户的操作进行对页面的处理。了解了这个思维模式&#xff0c;可以对代码进…...

探索CSDN博客数据:使用Python爬虫技术

探索CSDN博客数据&#xff1a;使用Python爬虫技术 在数字化的浪潮中&#xff0c;数据的获取与分析变得日益关键。CSDN作为中国领先的IT社区和服务平台&#xff0c;汇聚了海量的技术博客与文章&#xff0c;成为一座蕴藏丰富的数据宝库。本文将引领您穿梭于Python的requests和py…...

PPT画图——如何设置导致图片为600dpi

winr&#xff0c;输入regedit打开注册表 按路径找&#xff0c;HKEY_CURRENT_USER\Software\Microsoft\Office\XX.0\PowerPoint\Options&#xff08;xx为版本号&#xff0c;16.0 or 15.0或则其他&#xff09;。名称命名&#xff1a;ExportBitmapResolution 保存即可&#xff0c;…...

uniapp——APP读取bin文件,解析文件的数据内容(二)

文章目录 读取bin文件内容&#xff0c;发送给蓝牙设备&#xff1b;上传文件返回数据格式通过URL路径获取文件对象&#xff0c;读取文件的数据内容file对象返回数据格式 读取bin文件内容&#xff0c;发送给蓝牙设备&#xff1b; 上传文件&#xff0c;根据返回路径&#xff0c;解…...

Paddler负载均衡器

Paddler负载均衡器 Paddler本身是用Go语言编写的,没有直接的Python接口,但可以通过以下方式在Python中使用: 执行命令行调用 在Python中可以使用 subprocess 模块来调用Paddler的命令行工具,实现负载均衡功能 。例如: import subprocessdef start_paddler_agent():com…...

`we_chat_union_id IS NOT NULL` 和 `we_chat_union_id != ‘‘` 这两个条件之间的区别

文章目录 1、什么是空字符串&#xff1f;2、两个引号之间加上空格 好的&#xff0c;我们来详细解释一下 we_chat_union_id IS NOT NULL 和 we_chat_union_id ! 这两个条件之间的区别&#xff0c;以及它们在 SQL 查询中的作用&#xff1a; 1. we_chat_union_id IS NOT NULL 含…...

clicbot可立宝编程 易错归纳笔记

1、屏幕播放表情模块和等待时间 易错点1&#xff1a; (1)等待时间&#xff1a; (a)不是等上一个代码执行完&#xff0c;再执行等待时间&#xff0c;这是错误的。 (b)等待时间上面的代码1刚开始执行&#xff0c;上面的代码2也刚开始执行&#xff0c;不是等到代码1执行完&#xf…...

MySQL 数据”丢失”事件之 binlog 解析应用

事件背景 客户反馈在晚间数据跑批后,查询相关表的数据时,发现该表的部分数据在数据库中不存在 从应用跑批的日志来看,跑批未报错,且可查到日志中明确显示当时那批数据已插入到数据库中 需要帮忙分析这批数据丢失的原因。 备注:考虑信息敏感性,以下分析场景测试环境模拟,相关数据…...

基于人工智能时代政务智慧转型的实现前景初探

去年6月&#xff0c;我有幸聆听了由华政公共管理与政治学院精心组织的2019年MPA研究生高端论坛&#xff0c;上午场&#xff1a;由董海军&#xff08;中共上海市委机构编制委员会办公室处长&#xff09;主讲的深化机构改革的探索与实践&#xff0c;以及下午场&#xff1a;由束金…...

【论文笔记】Visual Alignment Pre-training for Sign Language Translation

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Visual Alignment Pre-tra…...

一起学Git【第五节:git版本回退】

git reset 是 Git 版本控制系统中一个非常强大的命令,它可以用来重置当前分支到指定的状态,即执行撤销操作或者回退至之前的某一版本,他可以回退至之前的某一个提交状态。有三种主要的用法:git reset --soft;git reset --hard;git reset --mixed; 对比如图: 1.git res…...

金蝶V10中间件的使用

目录 环境准备搭建过程配置修改应用部署 环境准备 Linux内核服务器JDK1.8安装包&#xff1a;AAS-V10.zip程序包&#xff1a;***.war 搭建过程 将安装包上传至服务器opt目录下&#xff0c;官方给定的默认服务主目录为“/opt/AAS-V10/ApusicAS/aas/”&#xff1b;解压安装包(解…...

hi168大数据离线项目环境搭建

hi168大数据离线项目环境搭建 ## **1. 服务器准备**##### 1.1 创建集群应用节点 集群服务器使用“我的应用“中的Ubuntu22.04集群模版创建三个节点应用&#xff0c;并且进入“我的应用”中去修改一下节点名称&#xff08;node1对应master&#xff0c;node2对应hadoop1&#xf…...

Ubuntu 22.04安装Docker

陈拓 2024/10/19-2024/12/26 0. 概述 docker是容器&#xff08;Container&#xff09;&#xff0c;有点像一个轻量级的虚拟机。 容器是一种轻量级、可移植、并将应用程序进行的打包的技术&#xff0c;使应用程序可以在几乎任何地方以相同的方式运行。Docker将镜像文件运行起…...

穿山甲等广告联盟依据哪些维度给APP、小程序结算广告变现收益

媒体在开展广告变现商业化时&#xff0c;最关心的是变现收益问题&#xff0c;所运营的不同体量的APP、小程序能产生多少广告变现收益。#广告联盟# 广告变现的价格、收益不是一成不变的&#xff0c;广告转化是影响广告收益的重要因素之一。广告平台针对整个变现链路上的各环节&…...

【ES6复习笔记】迭代器(10)

什么是迭代器&#xff1f; 迭代器&#xff08;Iterator&#xff09;是一种对象&#xff0c;它能够遍历并访问一个集合中的元素。在 JavaScript 中&#xff0c;迭代器提供了一种统一的方式来处理各种集合&#xff0c;如数组、字符串、Map、Set 等。通过迭代器&#xff0c;我们可…...

ROS1入门教程6:复杂行为处理

一、新建项目 # 创建工作空间 mkdir -p demo6/src && cd demo6# 创建功能包 catkin_create_pkg demo roscpp rosmsg actionlib_msgs message_generation tf二、创建行为 # 创建行为文件夹 mkdir action && cd action# 创建行为文件 vim Move.action# 定义行为…...

【 Copilot】云开发 Copilot 实战教程:从入门到精通,掌握云开发核心技能

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 引言 云开发 Copilot 作为一款革新性的开发辅助工具&#xff0c;利用先进的人工智能技术&#xff0c;为开发者在云开发的征程中点亮…...

DataCap MongoDB Driver: 全面解析MongoDB在DataCap中的使用指南

在大数据时代&#xff0c;MongoDB作为一款广受欢迎的NoSQL数据库&#xff0c;其灵活的文档存储模型和强大的查询能力使其成为许多现代应用的首选数据存储方案。今天&#xff0c;我们将深入探讨DataCap MongoDB Driver&#xff0c;这是一个强大的工具&#xff0c;它让在DataCap环…...

[x86 ubuntu22.04]双触摸屏的触摸事件都响应在同一个触摸屏上

1 问题描述 CPU&#xff1a;G6900E OS&#xff1a;ubuntu22.04 Kernel&#xff1a;6.8.0-49-generic 系统下有两个一样的 edp 触摸屏&#xff0c;两个触摸屏的触摸事件都响应在同一个 edp 屏幕上。 2 解决过程 使用“xinput”命令查看输入设备&#xff0c;可以看到只有一个 to…...