flv.js的追帧、断流重连及实时更新的直播优化方案

目录
  • 1. 前言
  • 2. 前端直播
    • 2.1 常见直播协议
    • 2.2 flv.js 的原理
    • 2.3 flv.js 的简单使用
  • 3. flv.js 的优化方案
    • 3.1 追帧-解决延迟累积问题
    • 3.2 断流重连
    • 3.3 实时更新
    • 3.4 解决 stuck 问题
  • 4. 封装插件 flvExtend.js
  • 5. 其他问题
  • 参考

1. 前言

最近在处理前端直播的业务,根据业务需要,使用 flv.js 的方案播放实时的flv视频流。不得不承认,flv.js 是一个伟大的库。

在使用flv.js开发的过程中,遇到了一些问题,也无外乎是视频延迟,视频卡顿等问题,经过在github issues里摸爬滚打,加上长时间的试错,将这些问题归纳出了对应的解决方案,也自己封装了一个扩展插件 flvExtend。

于是写这篇文章来对我遇到的一些问题进行总结,我提出的解决方案不一定适合所有场景,如果有更好的解决方案,欢迎讨论,这也是我写这篇文章的目的,也是我写文章的初心。

2. 前端直播

在讲解 flv.js 的优化方案之前,我想先简单的介绍一下前端直播的方案,为什么要使用 flv.js,方便大家理解以及作为一项技术来储备。

2.1 常见直播协议

  • RTMP: 底层基于 TCP,在浏览器端依赖 Flash。
  • HTTP-FLV: 基于 HTTP 流式 IO 传输 FLV,依赖浏览器支持播放 FLV。
  • WebSocket-FLV: 基于 WebSocket 传输 FLV,依赖浏览器支持播放 FLV。WebSocket 建立在 HTTP 之上,建立 WebSocket 连接前还要先建立 HTTP 连接。
  • HLS: Http Live Streaming,苹果提出基于 HTTP 的流媒体传输协议。HTML5 可以直接打开播放。
  • RTP: 基于 UDP,延迟 1 秒,浏览器不支持。

可以看到,在浏览器端,可以考虑的方案有:HTTP-FLVWebSocket-FLV 以及 HLS, 我们可以对比一下这几个直播协议之间的性能:
(以下数据来源于网络,只做对比参考)

传输协议 播放器 延迟 内存 CPU
RTMP Flash 1s 430M 11%
HTTP-FLV Video 1s 310M 4.4%
HLS Video 20s 205M 3%

可以看出在浏览器里做直播,使用 HTTP-FLV 协议是不错的,性能优于 RTMP+Flash,延迟可以做到和 RTMP+Flash 一样甚至更好。

2.2 flv.js 的原理

flv.js 的主要工作就是,在获取到 FLV 格式的音视频数据后通过原生的 JS 去解码 FLV 数据,再通过 Media Source Extensions API 喂给原生 HTML5 Video 标签。(HTML5 原生仅支持播放 mp4/webm 格式,不支持 FLV)

flv.js 为什么要绕一圈,从服务器获取 FLV 再解码转换后再喂给 Video 标签呢?原因如下:

  1. 兼容目前的直播方案:目前大多数直播方案的音视频服务都是采用 FLV 容器格式传输音视频数据。
  2. FLV 容器格式相比于 MP4 格式更加简单,解析起来更快更方便。

2.3 flv.js 的简单使用

<script src="flv.min.js"></script>
<video id="videoElement"></video>
<script>
  if (flvjs.isSupported()) {
    var videoElement = document.getElementById("videoElement");
    var flvPlayer = flvjs.createPlayer({
      type: "flv",
      isLive: true,
      url: "http://example.com/flv/video.flv",
    });
    flvPlayer.attachMediaElement(videoElement);
    flvPlayer.load();
    flvPlayer.play();
  }
</script>

主要流程就是:

  1. 创建flvjs.Player对象,可以传递两个参数:MediaDataSource,以及 Config,具体的可以看下官方文档
  2. 挂载元素
  3. 加载视频流
  4. 播放视频流

附:官方 API 文档

3. flv.js 的优化方案

我们根据官方的例子,可以很容易地把 flv 直播流播起来,但是在实际项目中使用时,还会遇到一些问题,我们需要手动对这些问题进行优化处理

3.1 追帧-解决延迟累积问题

flv.js 有一个最大的问题,就是延迟问题,一方面是直播端的延迟,一方面是浏览器的延迟,而且浏览器的延迟如果不做特殊处理,会造成延时累积的问题,对直播的实时性影响很大。

解决方案需要从以下两部分入手:

3.1.1 修改 config 配置

{
  enableWorker: true, // 启用分离的线程进行转换
  enableStashBuffer: false, // 关闭IO隐藏缓冲区
  stashInitialSize: 128, // 减少首帧显示等待时长
}
  • 开启 flv.js 的 Worker,多线程运行 flv.js 提升解析速度可以优化延迟
  • 关闭 buffer 缓存,这个选项可以明显地降低延迟,缺点就是由于关闭了 buffer 缓存,网络不好的时候可能会出现 loading 加载
  • 调低 IO 缓冲区的初始尺寸,减少首帧显示的等待时长

3.1.2 追帧设置

解决延时累加最有效的方式就是进行追帧设置

追帧,就是去判断缓冲区末尾的 buffer 值与当前播放时间的差值,如果大于某个值,就进行追帧设置,具体的思路如下:

  1. 首先,在 progress 事件,或者定时器中进行追帧逻辑
  2. 判断 buffer 的差值 delta
let end = this.player.buffered.end(0); //获取当前buffered值(缓冲区末尾)
let delta = end - this.player.currentTime; //获取buffered与当前播放位置的差值
  1. 如果 delta 值大于某个设定的值,则进行追帧操作
  2. 追帧有两种方式
    1)一种是直接更新当前的时间:this.player.currentTime = this.player.buffered.end(0) - 1,缺点是如果频繁触发会导致跳帧,观感差;
    2)一种是调快播放速度的方式来慢慢追帧: this.videoElement.playbackRate = 1.1,优点是稳定,缺点是如果 delta 值过大,通过这种方式追得太慢
    在实际使用中两种方式可以结合起来。

代码实现:

videoElement.addEventListener("progress", () => {
  let end = player.buffered.end(0); //获取当前buffered值(缓冲区末尾)
  let delta = end - player.currentTime; //获取buffered与当前播放位置的差值

  // 延迟过大,通过跳帧的方式更新视频
  if (delta > 10 || delta < 0) {
    this.player.currentTime = this.player.buffered.end(0) - 1;
    return;
  }

  // 追帧
  if (delta > 1) {
    videoElement.playbackRate = 1.1;
  } else {
    videoElement.playbackRate = 1;
  }
});

3.2 断流重连

断流重连即在flvjs播放失败的回调中,进行重建视频的操作

代码实现:

this.player.on(flvjs.Events.ERROR, (e) => {
  // destroy
  this.player.pause();
  this.player.unload();
  this.player.detachMediaElement();
  this.player.destroy();
  this.player = null;

  // 进行重建的逻辑,这里不再展开
  this.init();
});

3.3 实时更新

直播需要保证视频的实时性,以下两种操作都会导致视频的实时性得不到保证:

  • 用户点击了暂停,过一段时间后再点播放,这时候的直播视频不是最新的
  • 网页切到后台,再重新切换回前台,视频不是最新的

所以需要根据这两种情况来实时更新视频

代码实现:

// 点击播放按钮后,更新视频
videoElement.addEventListener("play", () => {
  let end = player.buffered.end(0) - 1;
  this.player.currentTime = end;
});

// 网页重新激活后,更新视频
window.onfocus = () => {
  let end = player.buffered.end(0) - 1;
  this.player.currentTime = end;
};

3.4 解决 stuck 问题

有的时候,视频在播放的过程中会突然卡住,或者控制台有时会报错 “Playback seems stuck at 0, seek to 1.1”。

我们需要判断视频是否卡住了,然后重建视频实例

思路就是判断 decodedFrames 是否产生变化,如果视频是播放状态并且该值没有产生变化,则可以判断视频卡住了。

代码实现:

function handleStuck() {
  let lastDecodedFrames = 0;
  let stuckTime = 0;

  this.interval && clearInterval(this.interval);
  this.interval = setInterval(() => {
    const decodedFrames = this.player.statisticsInfo.decodedFrames;
    if (!decodedFrames) return;

    if (lastDecodedFrames === decodedFrames && !this.videoElement.paused) {
      // 可能卡住了,重载
      stuckTime++;
      if (stuckTime > 1) {
        console.log(`%c 卡住,重建视频`, "background:red;color:#fff");
        // 先destroy,再重建视频实例
        this.rebuild();
      }
    } else {
      lastDecodedFrames = decodedFrames;
      stuckTime = 0;
    }
  }, 800);
}

4. 封装插件 flvExtend.js

我将这些优化方案封装成了一个插件 flvExtend.js,它相当于是 flv.js 的一个功能扩展

插件地址:http://github.com/shady-xia/flvExtend

使用起来是这个样子:

import FlvExtend from "flv-extend";

// 配置需要的功能
const flv = new FlvExtend({
  element: videoElement, // *必传
  frameTracking: true, // 开启追帧设置
  updateOnStart: true, // 点击播放后更新视频
  updateOnFocus: true, // 获得焦点后更新视频
  reconnect: true, // 开启断流重连
  reconnectInterval: 2000, // 断流重连间隔
});

// 调用 init 方法初始化视频
// init 方法的参数与 flvjs.createPlayer 相同,并返回 flvjs.player 实例
const player = flv.init(
  {
    type: "flv",
    url: "http://192.168.0.11/stream",
    isLive: true,
  },
  {
    enableStashBuffer: false, // 如果您需要实时(最小延迟)来进行实时流播放,则设置为false
    stashInitialSize: 128, // 减少首帧显示等待时长
  }
);

// 直接调用play即可播放
player.play();

5. 其他问题

这里打算长期记录一下遇到的问题以及解决思路,欢迎大家讨论,我会更新补充

1)多路视频同时直播

由于浏览器对 http 1.0 的限制,以Chrome为例,同一个浏览器下,最多只能播6路同源地址下的视频(包括多个标签页也会被合算在内)

目前的解决方案有:

  1. 使用http 2.0,由于http 2.0的多路复用,可以同屏播放多个视频流
  2. 使用 websocket
  3. 通过为流分配不同的服务端地址

参考

  • github issues
  • 使用 flv.js 做直播
本文转载于网络 如有侵权请联系删除

相关文章

  • 如何设计三极管控制继电器电路[通俗易懂]

    大家好,又见面了,我是你们的朋友全栈君。在用三极管驱动继电器的时候,不管是NPN还是PNP,都要把继电器接在三极管的集电极,而不会接在发射极上。一般初学者都会容易碰到这个问题,下面和大家分析一下这个问题。首先先看一下三极管驱动继电器的正确接法,NPN、PNP三极管驱动继电器的典型电原理图如下图所示。上图中,分别是NPN和PNP三极管驱动继电器的电路原理图,这两个电路中都把继电器接在了集电极上。因为三极管驱动继电器时需要工作在截止和饱和状态,如果把继电器接在发射极可能会导致三极管不能完全饱和继电器线圈压降太多导致电压不足以驱动继电器线圈。以PNP三极管驱动继电器为例,以下为工业场景中设计的带隔离继电器电路:上图中,把继电器接在了发射极上,三极管在正常工作时,基极和发射极之间存在大约0.7V的电压差,而且继电器的线圈在工作时也会产生电压降。如果GPIO处是3.3,基极电阻的压降如果忽略不计的话,那么发射极的电压约是2.6V,对于5V的继电器而言,有可能导致不吸合,或者处于临界状态,导致频率误触发,更何况基极电阻也会产生电压降的。如果发射极接地,而把继电器接集电极的话,基极在不考虑基极电阻压

  • mac全选文字的快捷键_MACBOOK最全快捷键指南

    大家好,又见面了,我是你们的朋友全栈君。官方最新出炉的快捷键大全:剪切、拷贝、粘贴和其他常用快捷键Command-X:剪切所选项并拷贝到剪贴板。Command-C:将所选项拷贝到剪贴板。Command-V:将剪贴板的內容粘贴到当前文稿或应用中。Command-Z:撤销前一个命令。随后您可以按Command-Shift-z来重做,从而反向执行撤销命令。Command-A:全选各项。Command-F:查找文稿中的项目或打开“查找”窗口。Command-G再次查找:查找之前所找到项目出现的下一个位置。要査找出现的上一个位置,请按Command-Shift-G。Command-H:隐藏最前面的应用的窗口。要查看最前面的应用但隐藏所有其他应用,请按Command-OptionCommand-M:将最前面的窗口最小化至“程序坞”。要最小化最前面的应用的所有窗口,请按CommandOption-M。Command-N新建:打开一个新文稿或窗口。Command-O打开所选项,或打开一个对话框以选择要打开的文件。Command-P:打印当前文稿。Command-S:存储当前文稿。Command-W:关

  • elasticsearch安装ik中文分词器

    一、概述elasticsearch官方默认的分词插件,对中文分词效果不理想。中文的分词器现在大家比较推荐的就是IK分词器,当然也有些其它的比如smartCN、HanLP。这里只讲如何使用IK做为中文分词。二、安装elasticsearch环境说明操作系统:centos7.6docker版本:19.03.12ip地址:192.168.31.165安装这里安装7.10.1版本下载镜像docker pull elasticsearch:7.5.1复制修改系统参数vi /etc/sysctl.conf复制调整参数vm.max_map_count=262144复制刷新参数sysctl -p复制注意,系统参数一定要修改,否则参数过低,会造成elasticsearch启动失败。配置文件mkdir -p /data/elk7/elasticsearch/{data,logs,config,plugins} vi /data/elk7/elasticsearch/config/elasticsearch.yml复制内容如下:cluster.name: "docker-cluster"

  • linux 环境下安装使用 git

    linux安装git安装命令 $sudoapt-getinstallgit配置用户和邮箱$gitconfig--globaluser.name\"HanXiaoTong\"$gitconfig--globaluser.email\"hanxiaotongtong@163.com\"hanxt@hanxt-Satellite-L700~$gitconfig--listuser.name=HanXiaotonguser.email=hanxiaotongtong@163.com初始化git仓库用git管理哪个项目就到该项目下面执行以下命令初始化:gitinitgitaddREADMEclone仓库克隆仓库的命令格式为gitclone[url]在当前目录下克隆项目,目录为grit $gitclonegit://github.com/schacon/grit.git在当前目录下克隆项目,目录为mygrit $gitclonegit://github.com/schacon/grit.gitmygrit将文件纳入git管理查看仓库内文档的状态,显示跟踪文件

  • OpenCV常用图像拼接方法(一) :直接拼接

    OpenCV常用图像拼接方法将分为四部分与大家分享,这里是第一种方法,欢迎关注后续。OpenCV常用图像拼接方法(一):直接拼接,俗称硬拼,就是简单的将两张图片合并成一张大图。方法比较简单,这里直接上代码://01_Combine_Two_Images.cpp //环境VS2017+OpenCV4.4.0 //功能介绍:用于将两张图片拼接成一张大图(以左右拼接为例),俗称的硬拼方法 //特点:简单粗暴,实际应用对拍摄条件要求苛刻,适用性差 #include"pch.h" #include<iostream> #include<math.h> #include<opencv2/opencv.hpp> usingnamespacestd; usingnamespacecv; intmain() { Matimg1=imread("01.jpg"); Matimg2=imread("02.jpg"); if(img1.empty()||img2.empty()) { cout<<&

  • 碾压 Python!为什么 Julia 速度这么快?

    【新智元导读】短短几年,由MITCSAIL实验室开发的编程语言Julia已然成为编程界的新宠,尤其在科学计算领域炙手可热。很大部分是因为这门语言结合了C语言的速度、Ruby的灵活、Python的通用性,以及其他各种语言的优势于一身。那么你知道为什么Julia的速度能做到那么快吗?这并不是因为更好的编译器,而是一种更新的设计理念,Julia在开发之初就将这种理念纳入其中,而这也是关注“人生苦短”的Python所欠缺的。为什么要选择Julia?因为它比其他脚本语言更快,它在具备Python、MATLAB、R语言开发速度的同时,又能生成与C语言和Fortran一样快的代码。 但Julia新手对这种说法可能会有点怀疑。为什么其他脚本语言不也提升一下速度?Julia可以做到的,为什么其他脚本语言做不到?你能提供基准测试来证明它的速度吗?这似乎有违“天底下没有免费的午餐”的道理。它真的有那么完美吗?很多人认为Julia运行速度很快,因为它是即时编译(JIT)型的(也就是说,每条语句都使用编译的函数来运行,这些函数要么在使用之前进行即时编译,要么在之前已经编译过并放在缓存中)。这就引出了一个问题:J

  • Spring Boot 2.x基础教程:快速入门

    简介在您第1次接触和学习Spring框架的时候,是否因为其繁杂的配置而退却了?在你第n次使用Spring框架的时候,是否觉得一堆反复黏贴的配置有一些厌烦?那么您就不妨来试试使用SpringBoot来让你更易上手,更简单快捷地构建Spring应用!SpringBoot让我们的Spring应用变的更轻量化。我们不必像以前那样繁琐的构建项目、打包应用、部署到Tomcat等应用服务器中来运行我们的业务服务。通过SpringBoot实现的服务,只需要依靠一个Java类,把它打包成jar,并通过java-jar命令就可以运行起来。这一切相较于传统Spring应用来说,已经变得非常的轻便、简单。如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。总结一下SpringBoot的主要优点:为所有Spring开发者更快的入门开箱即用,提供各种默认配置来简化项目配置内嵌式容器简化Web项目没有冗余代码生成和XML配置的要求快

  • 区块链代码不开源=假项目?

    不管是互联网还是区块链,作为一项技术进入大众的视野,开源与否都饱含争议,曾经Java的非完全开源产生了很多非议,而今区块链的出现,让开源与否这个话题再次进入公众视野。什么是开源?大家经常听到的项目开源,实际上是指项目遵循开源协议,将源代码公开,任何人都可以进行查看。计算机发展早期阶段,软件几乎都是开放的,任何人都可以查看软件的源代码,但是微软的出现打破了这种局面,他们在分发软件的时候也不再附带源代码。从此,专有软件的时代到来了。但是随着区块链技术的出现,以代码开源为特点,凭借在极客圈获得的巨大认同,开始重登历史舞台,现在大部分的项目选择在Github上面开源。Github是谁?gitHub是一个面向开源及私有软件项目的托管平台,因为只支持git作为唯一的版本库格式进行托管,故名gitHub。gitHub于2008年4月10日正式上线,除了git代码仓库托管及基本的Web管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。目前全球拥有数千万的开发者用户,仅在中国就有接近百万的开发者使用Github。区块链项目为何要开源?简单来说

  • 云存储定价:顶级供应商的价格比较

    大多数供应商提供各种不同的云存储服务,并且每种服务的价格可能会受到许多不同因素的影响。例如,数据中心规模、弹性级别、存储数据量、免费资格、数据访问频率、数据传输费用、数据访问费用,以及支持订阅等都可能影响云存储的定价。对于企业来说,估测云存储的定价可能非常复杂。并且,对行业领先的云计算供应商提供的价格进行比较,以确定价格最低的云存储更为复杂。大多数供应商提供各种不同的云存储服务,并且每种服务的价格可能会受到许多不同因素的影响。例如,数据中心规模、弹性级别、存储数据量、免费资格、数据访问频率、数据传输费用、数据访问费用,以及支持订阅等都可能影响云存储的定价。推出的定价指南比较了主要供应商的云存储定价,即基础设施即服务(IaaS)的对象存储。在调查报告中,评估了全球最受欢迎的五家商业云存储提供商:Amazon、MicrosoftAzure、GoogleCloud、IBMCloud和OracleCloud。(1)亚马逊简单存储服务(S3)AWSS3(亚马逊网络服务的简单存储服务)是在云计算行业处于领先地位的亚马逊公司的旗舰对象存储解决方案。它具有可扩展性、99.999999999%的耐用性、

  • 报名人数已超2000,第二届全球虚拟现实大会为何热度爆棚?

    近期,全球虚拟现实行业被一件大事撩动着神经,那就是第二届全球虚拟现实大会(GVRC)即将于6月27日-28日在上海金桥的中欧国际商学院开幕。从开始报名到现在,仅仅一周时间,大会组委会已经收到超过2000份的报名申请。为何这届GVRC受到如此热情的追捧? 据VR行业消息人士介绍,本次GVRC有太多理由让业内人士及周边传统行业、资本方们趋之若鹜:这里汇聚了全球最高规格VR/AR路演!数十位顶级投资人现场点评,现场可以领教最“干货”的行业趋势解析;世界级大咖演讲,近十场圆桌论坛碰撞,前沿开发者分享,顶级投资路演,最多样VR/AR技术展览……单拎出其中任何一项,就足以让人兴奋很久了。所以,瞬间收获超2000份的报名申请,其中包括众多国有大型企业、行业领袖企业、上市公司等超百家,投资机构超过200家,其他相关技术公司超过500家,这还仅仅只是开始。随着开幕日期的到来,热度更会“爆表”。 据了解,本届GVRC核心关键词在于:“融合、共振”。大会力求通过VR技术+传统行业应用+实力资本的深度融合,探讨VR跨界合作、全球协同共享的方式,碰撞出全新的VR行业发展思想、行进路线图,通过汇聚全球顶尖思

  • HDUOJ---What Are You Talking About

    WhatAreYouTalkingAboutTimeLimit:10000/5000MS(Java/Others)    MemoryLimit:102400/204800K(Java/Others)TotalSubmission(s):10963    AcceptedSubmission(s):3521 ProblemDescriptionIgnatiusissoluckythathemetaMartianyesterday.Buthedidn'tknowthelanguagetheMartiansuse.TheMartiangiveshimahistorybookofMarsandadictionarywhenitleaves.NowIgnatiuswanttotranslatethehistorybookintoEnglish.Canyouhelphim?InputTheproblemhasonlyonetestcase,thetestcaseconsistsoftwoparts,thedictionarypartandthebookpart.Thedictiona

  • 【报告下载】35张精彩PPT谈智能制造的三驾马车

  • Qt:基于QTcpSocket,QTcpServer编写的多人网络聊天室(TCP/IP协议) —— 服务器源码

    编译环境:Ubuntu18.04,Qt5.11.2QTcpSockServer.proTEMPLATE=app CONFIG+=consolec++11 CONFIG+=consoleprecompile_header CONFIG-=app_bundle CONFIG+=qt QT+=coreguinetworksql greaterThan(QT_MAJOR_VERSION,4):QT+=widgets HEADERS+=\ CommonInc.h\ Database.h\ Structure.h\ TcpServer.h\ ServerSocket.h SOURCES+=\ TcpServer.cpp\ Database.cpp\ main.cpp\ ServerSocket.cpp复制main.cpp#include<QCoreApplication> #include"CommonInc.h" #include"TcpServer.h" intmain(intargc,char*argv[]) { QCoreAppl

  • MyBatis入门知识汇总

     为什么要使用MyBatis?   我们都知道,在学习mybatis之前,要在Java中操作数据库,需要用到JDBC,但是在使用JDBC时会有许多缺陷。 比如:   1、使用时需要先进行数据库连接,不用后要立即释放连接,这样对数据库进行频繁连接和关闭,会造成数据库资源浪费,同时并发量较大时,会影响数据库性能。   解决方案:为了达到连接复用,使用数据库连接池管理数据库连接。   2、将sql语句硬编码到java代码中,使得代码耦合度高,如果sql 语句修改,就需要重新编译java代码,不利于系统维护。   解决方案:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译,只要在配置文件中修改。   3、向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。   解决方案:将sql语句及占位符号和参数全部配置在xml中。   4、从resutSet中遍历结果集获取数据时,必须保证属性名正确,否则无法取出数据,因此将获取表的字段进行硬编码,不利于系统维护。   解决方案:将查询的

  • RabbitMQ实现即时通讯居然如此简单!

    有时候我们的项目中会用到即时通讯功能,比如电商系统中的客服聊天功能,还有在支付过程中,当用户支付成功后,第三方支付服务会回调我们的回调接口,此时我们需要通知前端支付成功。最近发现RabbitMQ可以很方便的实现即时通讯功能,如果你没有特殊的业务需求,甚至可以不写后端代码,今天给大家讲讲如何使用RabbitMQ来实现即时通讯! MQTT协议 MQTT(MessageQueuingTelemetryTransport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的轻量级通讯协议,该协议构建于TCP/IP协议上。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。 MQTT相关概念 Publisher(发布者):消息的发出者,负责发送消息。 Subscriber(订阅者):消息的订阅者,负责接收并处理消息。 Broker(代理):消息代理,位于消息发布者和订阅者之间,各类支持MQTT协议的消息中间件都可以充当。 Topic(主题):可以理解为消息队列中的路由,订阅者订阅了主题之后,就可以收到发送到该主题的

  • anaconda环境搭建

     anaconda环境搭建  下载anaconda直接去官网下载就可了,地址:https://www.anaconda.com/distribution/,选择适合自己电脑的版本 配置环境变量 1,找到python.exe所在的文件,我的是E:\anaconda\anaconda3 2,然后找到一个scripts文件,点击后将路径复制 3,找到环境变量(怎么找具体的就不说,mysql里怎么找环境变量),然后把路径E:\anaconda\anaconda3\Scripts放到系统变量path里,在路径前加上英文的;分号  conda的一些功能  安装包 #安装matplotlip condainstallmatplotlip 复制 卸载包 condaremovematplotlib复制 更新包 condaupdatematplotlib复制 查询已安装的包 condalist 复制      一念成佛,一念成魔

  • 2018.8.8提高B组模拟考试

    教训:考场上宁打暴力,不打高精。 (不过还好A了一道题保底嘿嘿嘿)   T1题意简述:jzoj5771   Description     MWH寒假外出旅游,来到了S国。S国划分为N个省,第i个省有Ti座城市,编号分别为Ci1,Ci2,……CiTi(各省城市编号不会重复)。所有城市间有M条双向的道路连接,从任意一个城市出发,可到达一切城市,每条道路均须收费。   此时恰逢春运期间,S国交通运输局采取了优惠措施。当一条路的路费在[L..R]区间时,可免去。同时,每个省也有优惠措施,第i个省内的每条道路路费收其Xi%,连接第i个省和第j个省的每条道路路费收其(Xi%+Xj%)/2。MWH想从城市s走到城市t,请求出一对L,R,确保: 1.MWH能免费到达目的地; 2.L≤R; 3.L、R均为整数; 4.L尽可能地大,R在满足L最大的前提下最小。 注意:因每条道路由各省的交通运输局直接管辖,所以每条道路的路费必须先得到省级优惠,再得到国家级优惠。 I

  • 彻底清除挖矿程序

    转自:https://blog.csdn.net/zzf1510711060/article/details/83015700   一:杀死挖矿程序进程在服务器上使用top指令查看cpu的使用情况,发现有一个叫java的程序占用cpu高达99.9% PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND5778root2003735762720404S99.90.11252:00java1root2004320835442420S0.00.20:04.52systemd2root200000S0.00.00:00.00kthreadd3root200000S0.00.00:00.08ksoftirqd/05root0-20000S0.00.00:00.00kworker/0:0H7rootrt0000S0.00.00:00.00migration/0这个是挖矿程序伪装成java程序,用jps查看java进程的时候并没有这个程序。 通过pkill5778杀死进程 二:删除挖矿程序我们通过cd/proc/5778(这个java挖矿程序的pid)进入到

  • Android控件系列之RadioButton&amp;RadioGroup

    学习目的: 1、掌握在Android中如何建立RadioGroup和RadioButton 2、掌握RadioGroup的常用属性 3、理解RadioButton和CheckBox的区别 4、掌握RadioGroup选中状态变换的事件(监听器) RadioButton和CheckBox的区别: 1、单个RadioButton在选中后,通过点击无法变为未选中    单个CheckBox在选中后,通过点击可以变为未选中 2、一组RadioButton,只能同时选中一个     一组CheckBox,能同时选中多个 3、RadioButton在大部分UI框架中默认都以圆形表示     CheckBox在大部分UI框架中默认都以矩形表示 RadioButton和RadioGroup的关系: 1、RadioButton表示单个圆形单选框,而RadioGroup是可以容纳多个RadioButton的容器 2、每个RadioGroup中的RadioButton同时只能有一个被选中 3、不同

  • antd Grid

    import{Row,Col}from'antd'; <Row type="flex"//内容布局(左靠齐,右靠齐,居中) justify="start"//左靠齐(start,center,end,space-between,space-around) align="top"//flex布局下垂直对齐方式:top(默认),moddle,bottom gutter=number/object//栅格间隔,可以写成像素值或支持响应式的对象写法{xs:8,sm:16,md:24} > <Col span={8}//每行24列,这里占8列 offset={8}//向右偏移8列 push={6}//向右偏移6列 pull={2}//向左偏移2列 xs={2}//类似boostrop,小屏幕占两列 order=1//栅格顺序,flex布局模式下有效 >col-12</Col> </Row>复制 1.    xs:<576px   sm :>=576px   md:>=768px   lg:>=992p

  • 雷林鹏分享:JDBC环境设置

      本教程提供了如何创建一个简单的JDBC应用程序的示例。演示如何打开一个数据库连接,执行SQL查询,并显示结果。   所有在此模板的例子中提到的步骤,将在本教程的后续章节说明。   创建JDBC应用程序:   有下列涉及构建JDBC应用程序的六个步骤:   导入数据包.需要包括含有需要进行数据库编程的JDBC类的包。大多数情况下,使用importjava.sql.*就可以了.   注册JDBC驱动程序.需要初始化驱动程序,可以与数据库打开一个通信通道。   打开连接.需要使用DriverManager.getConnection()方法创建一个Connection对象,它代表与数据库的物理连接。   执行查询.需要使用类型声明的对象建立并提交一个SQL语句到数据库。   从结果集中提取数据.要求使用适当的关于ResultSet.getXXX()方法来检索结果集的数据。   清理环境.需要明确地关闭所有的数据库资源相对依靠JVM的垃圾收集。   示例代码:   这个范例的例子可以作为一个模板,在需要建立JDBC应用程序。   基于对环境和数据库安装在前面的章节中做此示例代码已写入。  

相关推荐

推荐阅读