流媒体学习之路(WebRTC)——Pacer与GCC(5)
流媒体学习之路(WebRTC)——Pacer与GCC(5)
——
我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标:可以让大家熟悉各类Qos能力、带宽估计能力,提供每个环节关键参数调节接口并实现一个json全配置,提供全面的可视化算法观察能力。欢迎大家使用
——
文章目录
- 流媒体学习之路(WebRTC)——Pacer与GCC(5)
- 一、PacingController
- 1.1 背景介绍
- 1.2 代码
- 二、IntervalBudget
- 2.1 背景
- 2.2 代码
- 三、PacedSender
- 四、总结
在讲具体内容之前插一句嘴,从GCC分析(3)开始,我们将针对GCC的实现细节去分析它设计的原理,让我们理解这些类存在的意义,不再带大家去串具体的流程了。
一、PacingController
1.1 背景介绍
Pacer(Packet Pacing)的作用是在传输数据时能平滑的发送出去,减少对网络冲击和抖动的产生,提高通信质量。在一次数据传输中,如果所有包几乎同时发送,网络就可能会遭遇到冲击,这就可能导致网络拥塞,数据包丢失等问题。为了避免这样的问题,需要通过一个定时器均匀分散发送数据包。
特别是在音视频传输中,PACER更是非常重要的一部分。因为音视频的传输对于网络的稳定性和实时性要求非常高,任何形式的网络抖动或者丢包都会造成音视频的卡顿,延迟等问题。所以在WebRTC中使用Pacer,就是为了使音视频传输更加平滑,减少由于网络抖动造成的影响,从而达到提高实时音视频通信质量的目的。
提到WebRTC的Pacer就需要讲述它码率控制的逻辑:
从GCC输出的码率会设置给编码器以及pacer。pacer并不是完全严格设置多少就发多少,而是留有2.5倍的空间去发送。真正控制发送码率的则是输出给编码器的部分,期望控制编码器的输出码率。同时,pacer还对所有数据设置了优先级,优先级如下:
int GetPriorityForType(RtpPacketToSend::Type type) {// Lower number takes priority over higher.switch (type) {case RtpPacketToSend::Type::kAudio:// Audio is always prioritized over other packet types.return kFirstPriority + 1;case RtpPacketToSend::Type::kRetransmission:// Send retransmissions before new media.return kFirstPriority + 2;case RtpPacketToSend::Type::kVideo:case RtpPacketToSend::Type::kForwardErrorCorrection:// Video has "normal" priority, in the old speak.// Send redundancy concurrently to video. If it is delayed it might have a// lower chance of being useful.return kFirstPriority + 3;case RtpPacketToSend::Type::kPadding:// Packets that are in themselves likely useless, only sent to keep the// BWE high.return kFirstPriority + 4;}
}
Pacer之所设计成这样,是因为我们向编码器设置码率之后想要保证丝滑清晰的画面,不可能完全控制输出码率,有时候画面复杂码率就大一些,画面简单码率就小一些。所以Pacer为了保证延迟预留了2.5倍的发送空间,也就是说真正控制码率的位置其实是编码器的输出。
1.2 代码
接下来我看看看pacer的核心代码——PacingController。这个类包含了优先级设置以及发送的逻辑,前面提到了优先级的内容下面只介绍发送逻辑:
void PacingController::ProcessPackets() {Timestamp now = CurrentTime(); // 当前时间TimeDelta elapsed_time = UpdateTimeAndGetElapsed(now); // 与上次process的间隔// 发送保活,每500ms发送一个padding包,一旦发送的数据大于拥塞窗口则不发送if (ShouldSendKeepalive(now)) {DataSize keepalive_data_sent = DataSize::Zero();// 产生padding包std::vector<std::unique_ptr<RtpPacketToSend>> keepalive_packets =packet_sender_->GeneratePadding(DataSize::bytes(1));for (auto& packet : keepalive_packets) {keepalive_data_sent +=DataSize::bytes(packet->payload_size() + packet->padding_size());packet_sender_->SendRtpPacket(std::move(packet), PacedPacketInfo());}OnPaddingSent(keepalive_data_sent);}// 处于暂停直接返回if (paused_)return;// 进入发送间隔开始计算if (elapsed_time > TimeDelta::Zero()) {DataRate target_rate = pacing_bitrate_;DataSize queue_size_data = packet_queue_.Size();// 队列中有数据才能发送if (queue_size_data > DataSize::Zero()) {// Assuming equal size packets and input/output rate, the average packet// has avg_time_left_ms left to get queue_size_bytes out of the queue, if// time constraint shall be met. Determine bitrate needed for that.// packet_queue_.UpdateQueueTime(CurrentTime());if (drain_large_queues_) {// 平均发送时间 = 最大队列时长(2s)- 平均排队时间TimeDelta avg_time_left =std::max(TimeDelta::ms(1),queue_time_limit - packet_queue_.AverageQueueTime());DataRate min_rate_needed = queue_size_data / avg_time_left;// 最发送码率大于目标码率,则目标码率等于最小需求码率if (min_rate_needed > target_rate) {target_rate = min_rate_needed;RTC_LOG(LS_VERBOSE) << "bwe:large_pacing_queue pacing_rate_kbps="<< target_rate.kbps();}}}// 设置媒体桶media_budget_.set_target_rate_kbps(target_rate.kbps());UpdateBudgetWithElapsedTime(elapsed_time);}bool first_packet_in_probe = false;bool is_probing = prober_.IsProbing();PacedPacketInfo pacing_info;absl::optional<DataSize> recommended_probe_size;// 正在探测则获取探测数据信息if (is_probing) {pacing_info = prober_.CurrentCluster();first_packet_in_probe = pacing_info.probe_cluster_bytes_sent == 0;recommended_probe_size = DataSize::bytes(prober_.RecommendedMinProbeSize());}DataSize data_sent = DataSize::Zero();// The paused state is checked in the loop since it leaves the critical// section allowing the paused state to be changed from other code.// while (!paused_) {if (small_first_probe_packet_ && first_packet_in_probe) {// If first packet in probe, insert a small padding packet so we have a// more reliable start window for the rate estimation.// 产生padding包auto padding = packet_sender_->GeneratePadding(DataSize::bytes(1));// If no RTP modules sending media are registered, we may not get a// padding packet back.if (!padding.empty()) {// Insert with high priority so larger media packets don't preempt it.EnqueuePacketInternal(std::move(padding[0]), kFirstPriority);// We should never get more than one padding packets with a requested// size of 1 byte.RTC_DCHECK_EQ(padding.size(), 1u);}first_packet_in_probe = false;}// 获取待发送包auto* packet = GetPendingPacket(pacing_info);// 一旦产生不了数据,证明队列为空,则放入padding数据if (packet == nullptr) {// No packet available to send, check if we should send padding.DataSize padding_to_add = PaddingToAdd(recommended_probe_size, data_sent);if (padding_to_add > DataSize::Zero()) {std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets =packet_sender_->GeneratePadding(padding_to_add);if (padding_packets.empty()) {// No padding packets were generated, quite send loop.break;}for (auto& packet : padding_packets) {EnqueuePacket(std::move(packet));}// Continue loop to send the padding that was just added.continue;}// Can't fetch new packet and no padding to send, exit send loop.break;}// 发送数据std::unique_ptr<RtpPacketToSend> rtp_packet = packet->ReleasePacket();RTC_DCHECK(rtp_packet);packet_sender_->SendRtpPacket(std::move(rtp_packet), pacing_info);data_sent += packet->size();// Send succeeded, remove it from the queue.OnPacketSent(packet);if (recommended_probe_size && data_sent > *recommended_probe_size)break;}if (is_probing) {probing_send_failure_ = data_sent == DataSize::Zero();if (!probing_send_failure_) {prober_.ProbeSent(CurrentTime().ms(), data_sent.bytes());}}
}RoundRobinPacketQueue::QueuedPacket* PacingController::GetPendingPacket(const PacedPacketInfo& pacing_info) {if (packet_queue_.Empty()) {return nullptr;}// Since we need to release the lock in order to send, we first pop the// element from the priority queue but keep it in storage, so that we can// reinsert it if send fails.// 取出第一个包RoundRobinPacketQueue::QueuedPacket* packet = packet_queue_.BeginPop();bool audio_packet = packet->type() == RtpPacketToSend::Type::kAudio;bool apply_pacing = !audio_packet || pace_audio_;// 如果处于拥塞状态或者剩余数据为0则取消弹出if (apply_pacing && (Congested() || (media_budget_.bytes_remaining() == 0 &&pacing_info.probe_cluster_id ==PacedPacketInfo::kNotAProbe))) {packet_queue_.CancelPop();return nullptr;}return packet;
}
二、IntervalBudget
2.1 背景
PacingController上述用到了IntervalBudget这个类,这个类用于做数据统计和预估。并且它作为一个抽象预估类,并不会真正的存数据,只是做了数据统计,每次排出数据后都按时间更新一次桶的容量,发送时则会把已发送的数据更新到桶数据中。
2.2 代码
头文件:
class IntervalBudget {public:explicit IntervalBudget(int initial_target_rate_kbps);IntervalBudget(int initial_target_rate_kbps, bool can_build_up_underuse);void set_target_rate_kbps(int target_rate_kbps);// TODO(tschumim): Unify IncreaseBudget and UseBudget to one function.void IncreaseBudget(int64_t delta_time_ms);void UseBudget(size_t bytes);size_t bytes_remaining() const;double budget_ratio() const;int target_rate_kbps() const;private:int target_rate_kbps_;int64_t max_bytes_in_budget_;int64_t bytes_remaining_;bool can_build_up_underuse_;
};
CPP文件:
constexpr int64_t kWindowMs = 500;
}IntervalBudget::IntervalBudget(int initial_target_rate_kbps): IntervalBudget(initial_target_rate_kbps, false) {}IntervalBudget::IntervalBudget(int initial_target_rate_kbps,bool can_build_up_underuse): bytes_remaining_(0), can_build_up_underuse_(can_build_up_underuse) {set_target_rate_kbps(initial_target_rate_kbps);
}void IntervalBudget::set_target_rate_kbps(int target_rate_kbps) {target_rate_kbps_ = target_rate_kbps;// 默认按500ms计算最大桶码率max_bytes_in_budget_ = (kWindowMs * target_rate_kbps_) / 8;// 计算剩余码率bytes_remaining_ = std::min(std::max(-max_bytes_in_budget_, bytes_remaining_),max_bytes_in_budget_);
}void IntervalBudget::IncreaseBudget(int64_t delta_time_ms) {// 按时换算桶的码率int64_t bytes = target_rate_kbps_ * delta_time_ms / 8;if (bytes_remaining_ < 0 || can_build_up_underuse_) {// We overused last interval, compensate this interval.// 把当前的码率加上bytes_remaining_ = std::min(bytes_remaining_ + bytes, max_bytes_in_budget_);} else {// If we underused last interval we can't use it this interval.// 一旦剩余码率为负则重新使用新计算的码率bytes_remaining_ = std::min(bytes, max_bytes_in_budget_);}
}void IntervalBudget::UseBudget(size_t bytes) {// 把使用的数据进行统计bytes_remaining_ = std::max(bytes_remaining_ - static_cast<int>(bytes),-max_bytes_in_budget_);
}size_t IntervalBudget::bytes_remaining() const {return rtc::saturated_cast<size_t>(std::max<int64_t>(0, bytes_remaining_));
}double IntervalBudget::budget_ratio() const {if (max_bytes_in_budget_ == 0)return 0.0;return static_cast<double>(bytes_remaining_) / max_bytes_in_budget_;
}int IntervalBudget::target_rate_kbps() const {return target_rate_kbps_;
}
三、PacedSender
上述的PacingController把具体的发送数据进行具体的计算,WebRTC把发送的逻辑和控制逻辑抽离了出来,其实PacingSender在构造时创建了PacingController并传入了this指针。因此对于PacingController来说PacingSender作为控制器在内部进行了回调。
其他的函数我们不做具体的描述,只介绍定时函数:
int64_t PacedSender::TimeUntilNextProcess() {rtc::CritScope cs(&critsect_);// When paused we wake up every 500 ms to send a padding packet to ensure// we won't get stuck in the paused state due to no feedback being received.// 从controller中获取间隔TimeDelta elapsed_time = pacing_controller_.TimeElapsedSinceLastProcess();if (pacing_controller_.IsPaused()) {// 最大间隔为500msreturn std::max(PacingController::kPausedProcessInterval - elapsed_time,TimeDelta::Zero()).ms();}auto next_probe = pacing_controller_.TimeUntilNextProbe();if (next_probe) {return next_probe->ms();}const TimeDelta min_packet_limit = TimeDelta::ms(5);return std::max(min_packet_limit - elapsed_time, TimeDelta::Zero()).ms();
}
四、总结
本文介绍了Pacer相关的内容,但我们的目的是通过Pacer去理解GCC的逻辑,在经过多个版本的迭代,Pacer与GCC的配合已经非常娴熟,同时耦合也是非常严重的:
-
每次Pacer的溢出发送,都需要GCC兜底(GCC的灵敏可以有效地检测到网络的排队,任何一个溢出的数据都能快速的下调码率,在遇到瓶颈带宽的时候出现了明显的锯齿状发送曲线);
-
码率不足与拥塞探测的矛盾(编码器的输出往往会收到一定的限制不可能无线地上涨,在当今环境下很难探测到带宽瓶颈。Pacer的做法是提供Padding的数据作为补充探测,但大部分厂商为了避免流量过度消耗,就把探测的逻辑关闭了。在这方面来看,Pacer真是没有完全听GCC的话);
也正是因为这样,WebRTC的Pacer是GCC的Pacer其他的拥塞算法来了,估计都水土不服,参考BBR被移除可知。
相关文章:
流媒体学习之路(WebRTC)——Pacer与GCC(5)
流媒体学习之路(WebRTC)——Pacer与GCC(5) —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标:可以让大家熟悉各类Qos能力、带宽估计能力,提供每个环节关键参数调节接口并实现一个json全…...
2023版本QT学习记录 -11- 多线程的使用(QT的方式)
———————多线程的使用(QT方式)——————— 🎄效果演示 两个线程都输出一些调试信息 🎄创建多线程的流程 🎄头文件 #include "qthread.h"🎄利用多态重写任务函数 class rlthread1 : public QThread {Q_OBJE…...
iOS苹果和Android安卓测试APP应用程序的差异
Hello大家好呀,我是咕噜铁蛋!我们经常需要关注移动应用程序的测试和优化,以提供更好的用户体验。在移动应用开发领域,iOS和Android是两个主要的操作系统平台。本文铁蛋讲给各位小伙伴们详细介绍在App测试中iOS和Android的差异&…...
每日算法打卡:数的三次方根 day 7
文章目录 原题链接题目描述输入格式输出格式数据范围输入样例:输出样例: 题目分析示例代码 原题链接 790. 数的三次方根 题目难度:简单 题目描述 给定一个浮点数 n,求它的三次方根。 输入格式 共一行,包含一个浮…...
人机交互主板定制_基于MT8735安卓核心板的自助查询机方案
人机交互主板是一种商显智能终端主板,广泛应用于广告机、工控一体机、教学一体机、智能自助终端、考勤机、智能零售终端、O2O智能设备、取号机、计算机视觉、医疗健康设备、机器人设备等领域。 人机交互主板采用联发科MTK8735芯片平台,四核Cortex-A53架构…...
全志F1C100s Linux 系统编译出错:不能连接 github
环境 Ubuntu 20.04 LTS 64 位虚拟机 开发板:Lichee Pi Nano 源代码:GitHub - florpor/licheepi-nano 问题描述 该源码库使用了 git 子模块的概念,一个库中包含了 u-boot、Linux等代码库。不需要分别编译,一个 make 全搞定 编译时提示错误: >>> linux-hea…...
如何保障 MySQL 和 Redis 的数据一致性?
数据一致性问题是如何产生的? 数据一致性问题通常产生于数据在不同的时间点、地点或系统中存在多个副本的情况, 系统只存在一个副本的情况下也完全可能会产生。 设想一下,你在一家连锁咖啡店有一张会员卡这张会员卡可以绑定两个账号&#x…...
Leetcode 2999. Count the Number of Powerful Integers
Leetcode 2999. Count the Number of Powerful Integers 1. 解题思路2. 代码实现 题目链接:10034. Count the Number of Powerful Integers 1. 解题思路 这一题的话其实还是一个典型的求不大于 N N N的特殊数字个数的问题。 这道题本质上进行一下替换还是要求如…...
【Reading Notes】(2)
文章目录 FreestyleHip-hop dance and MusicProgrammerMaster Freestyle 都说人的成长有三个阶段,第一个阶段认为自己独一无二,天之骄子;第二个阶段发现自己原来如此渺小,如此普通,沮丧失望;第三阶段&#…...
【设计模式之美】SOLID 原则之一:怎么才算是单一原则、如何取舍单一原则
文章目录 一. 如何判断类的职责是否足够单一?二. 类的职责是否设计得越单一越好? 开始学习一些经典的设计原则,其中包括,SOLID、KISS、YAGNI、DRY、LOD 等。 本文主要学习单一职责原则的相关内容。 单一职责原则的定义:…...
# [NOIP2015 普及组] 扫雷游戏#洛谷
题目背景 NOIP2015 普及组 T2 题目描述 扫雷游戏是一款十分经典的单机小游戏。在 n n n 行 m m m 列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。玩家翻开一个非地雷格时&#…...
Unity中Shader的_Time精度问题
文章目录 前言一、U方向上优化二、V方向上优化在这里插入图片描述 三、最终代码1、效果2、Shader 前言 在Unity的Shader中,使用了_Time来达到UV的流动效果,普遍会出现一个问题。我们的UV值会随着时间一直增加(uv值增加了,但是因为…...
听GPT 讲Rust源代码--compiler(15)
File: rust/compiler/rustc_arena/src/lib.rs 在Rust源代码中,rustc_arena/src/lib.rs文件定义了TypedArena,ArenaChunk,DroplessArena和Arena结构体,以及一些与内存分配和容器操作相关的函数。 cold_path<F: FnOnce,drop,new,…...
关键字联合体union的定义和使用
联合体的定义 联合体的定义和结构体相同。 联合体成员共用存储空间,联合体占用的空间最大长度的数据成员的长度。 union State {char sleep;char run;int suspend;double error; }state_u;以上例子,State表示联合体的名字,它相当于声明了一…...
基于GA-PSO遗传粒子群混合优化算法的VRPTW问题求解matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 遗传算法(GA)基本原理 4.2 粒子群优化(PSO)基本原理 4.3 算法优化策略 5.完整程序 1.程序功能描述 VRPTW是车辆路径问题(VR…...
【leetcode100-033】【链表】排序链表
【题干】 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 【思路】 递归版归并法链表版~没什么特别好说的(非递归版归并也是可以哒,但是马上要考试了今天懒得写了!打个flag在这里也许哪天想起来…...
[Kubernetes]5. k8s集群StatefulSet详解,以及数据持久化(SC PV PVC)
前面通过deployment结合service来部署无状态的应用,下面来讲解通过satefulSet结合service来部署有状态的应用 一.StatefulSet详解 1.有状态和无状态区别 无状态: 无状态(stateless)、牲畜(cattle)、无名(nameless)、可丢弃(disposable) 有状态: 有状态(stateful)、宠物(pet)…...
数据库系统-甘晴void学习笔记
数据库系统笔记 计科210X 甘晴void 202108010XXX 教材:《数据库系统概论》第6版 (图片来源于网络,侵删) 文章目录 数据库系统<br>笔记第一篇 基础篇1 绪论1.1数据库系统概述1.2数据模型1.3数据库系统的结构(三级模式结构…...
Azure Machine Learning - 人脸识别任务概述与技术实战
Azure AI 人脸服务提供了可检测、识别和分析图像中的人脸的 AI 算法。 人脸识别软件在许多不同情形中都十分重要,例如识别、无接触访问控制和实现隐私的人脸模糊。你可以通过客户端库 SDK,或者直接调用 REST API 使用人脸服务。 目录 一、人脸识别服务场…...
强化学习的数学原理学习笔记 - 蒙特卡洛方法(Monte Carlo)
文章目录 概览:RL方法分类蒙特卡洛方法(Monte Carlo,MC)MC BasicMC Exploring Starts🟦MC ε-Greedy 本系列文章介绍强化学习基础知识与经典算法原理,大部分内容来自西湖大学赵世钰老师的强化学习的数学原理…...
DDIA 第十一章:流处理
本文是《数据密集型应用系统设计》(DDIA)的读书笔记,一共十二章,我已经全部阅读并且整理完毕。 采用一问一答的形式,并且用列表形式整理了原文。 笔记的内容大概是原文的 1/5 ~ 1/3,所以你如果没有很多时间…...
webpack知识点总结(高级应用篇)
除开公共基础配置之外,我们意识到两点: 1. 开发环境(modedevelopment),追求强大的开发功能和效率,配置各种方便开 发的功能;2. 生产环境(modeproduction),追求更小更轻量的bundle(即打包产物); 而所谓高级应用,实际上就是进行 Webpack 优化…...
均匀与准均匀 B样条算法
B 样条曲线的定义 p ( t ) ∑ i 0 n P i F i , k ( t ) p(t) \sum_{i0}{n} P_i F_{i, k}(t) p(t)i0∑nPiFi,k(t) 方程中 n 1 n1 n1 个控制点, P i P_i Pi, i 0 , 1 , ⋯ n i0, 1, \cdots n i0,1,⋯n 要用到 n 1 n1 n1 个 k k k 次 B 样条基函数 …...
2023年12 月电子学会Python等级考试试卷(一级)答案解析
青少年软件编程(Python)等级考试试卷(一级) 分数:100 题数:37 一、单选题(共25题,共50分) 1. 下列程序运行的结果是?( ) print(hello) print(world) A. helloworld...
启发式算法解决TSP、0/1背包和电路板问题
1. Las Vegas 题目 设计一个 Las Vegas 随机算法,求解电路板布线问题。将该算法与分支限界算法结合,观察求解效率。 代码 python代码如下: # -*- coding: utf-8 -*- """ Date : 2024/1/4 Time : 16:21 Author : …...
阿里云新用户的定义与权益
随着云计算的普及,阿里云作为国内领先的云计算服务提供商,吸引了越来越多的用户。对于新用户来说,了解阿里云新用户的定义和相关权益非常重要,因为它关系到用户能否享受到更多的优惠和服务。 一、阿里云新用户的定义 阿里云新用户…...
go语言多线程操作
目录 引言 一、如何实现多线程 1. 线程的创建与管理: 2. 共享资源与同步: 3. 线程间通信: 4. 线程的生命周期管理: 5. 线程安全: 6. 考虑并发问题: 7. 性能与资源利用: 8. 特定语言或框架的工具和库: 二、go语言多线程 Goroutine 1. 轻量级: 2. 动态栈: 3. 调度:…...
GreatSQL社区2023全年技术文章总结
GreatSQL社区自成立以来一直致力于为广大的数据库爱好者提供一个交流与学习的平台。在2023年,我们见证了社区的蓬勃发展,见证了众多技术文章的诞生与分享。 此篇总结呈现GreatSQL社区2023年社区技术文章在CSDN发布的全部。这些文章涵盖了GreatSQL、MGR、…...
【论文阅读笔记】Stable View Synthesis 和 Enhanced Stable View Synthesis
目录 Stable View Synthesis摘要引言 Enhanced Stable View Synthesis 从Mip-NeRF360的对比实验中找到的两篇文献,使用了卷积神经网络进行渲染和新视角合成,特此记录一下 ToDo Stable View Synthesis paper:https://readpaper.com/pdf-ann…...
网络报文分析程序的设计与实现(2024)
1.题目描述 在上一题的基础上,参照教材中各层报文的头部结构,结合使用 wireshark 软件(下载地址 https://www.wireshark.org/download.html#releases)观察网络各层报文捕获,解析和分析的过程(如下 图所示&a…...
企业网站托管哪家好/郑州百度搜索优化
先创建一个表,创建语句如下:create table member(id int(3),name varchar(8),pass varchar(25));1.向表member中增加一个新列email,mysql语句如下:alter table member add email varchar(50) not null;describe member;-----------------------------------------------| field…...
佛山营销网站建设联系方式/友情链接买卖代理
PHP快速导入大量数据到数据库的方法第一种方法:使用insert into 插入,代码如下:$params array(‘value>50′);set_time_limit(0);echo date(“H:i:s”);for($i0;$i<2000000;$i){$connect_mysql->insert($params);};echo date(“H:i…...
怎么看网站是服务器还是虚拟主机/seo方案怎么做
鼠标事件触发条件onclick鼠标点击左键触发onmouseover鼠标经过触发onmouseout鼠标离开触发onfocus获得鼠标焦点触发onblur失去鼠标焦点触发onmousemove鼠标移动触发onmouseup鼠标弹起触发onmousedown鼠标按下触发 1. 禁用鼠标右键菜单 contextmenu 2. 禁止选中文字 selectstart…...
网站商城建设合同范本/cps广告是什么意思
一、AtomicReference介绍 AtomicReference和AtomicInteger非常类似,不同之处就在于AtomicInteger是对整数的封装,而AtomicReference则对应普通的对象引用。也就是它可以保证你在修改对象引用时的线程安全性。 AtomicReference是作用是对”对象”进行原子…...
手机网站制作架构/seo站长工具 论坛
arm linux 通过NTP对时之后,终端命令行Date命令显示的时间是对的,但是web服务器中的时间还是差8H。之前是通过export TZCST-8 修改的系统时区。 最后通过增加: ./etc/localtime 这个文件用来设置系统的时区,将 /usr/share/zonein…...
企业局域网的组建与网站建设论文/营销方案案例范文
今天分享一份来自中国移动的《5G落地:应用融合与创新》。如需下载《5G落地:应用融合与创新》,请扫码前往我们公众号对话框回复“落地”。ps:链接有效期至3月5日,建议尽快下载保存哦!编辑:陈颖思…...