最新文章
  • MacBook上ubuntu虚拟机共享

    MacBook上ubuntu虚拟机共享

    步骤1: 安装paralles tools 步骤2:执行install安装 报错处理 一通操作还是不行,原因是paralles tools没有匹配当前的内核版本,只能升级parallel 版本了。或者升级一个parallel desktop版本获取到parallel tools安装包放到低版本上。
  • emqx通过REST API获取设备是否在线

    emqx通过REST API获取设备是否在线

    本文通过emqx REST API的方式来进行配置 创建一个API密钥 记录密钥的名称 API key是用户名称,Secret KEY是密码 浏览器登录验证 用户名就是 API Key,密码就是Secret KEY. 登录上之后就可以获取到设备的信息了。 可以参考: http://localhost:18083/api-docs/index.html 支持哪些API 官网参考: https://docs.emqx.com/zh/emqx/latest/admin/api.html
  • 基于nodejs搭建jsonp环境

    基于nodejs搭建jsonp环境

    安装 安装 Node.js 和 npm:执行以下命令安装 Node.js 和 npm: yum install -y nodejs 创建工程 步骤1: 创建一个新的Node.js项目目录,并进入该目录: mkdir jsonp-server cd jsonp-server 步骤2:初始化Node.js项目,并安装Express框架 npm init -y npm install express --save 步骤3:创建一个名为server.js的文件,并添加以下代码: const express = require(\'express\'); const app = express(); const port = 3000; app.get(\'/jsonp\', function(req, res) { const callback = req.query.callback; const data = { name: \'John Doe\', age: 30 }; res.send(`${callback}(${JSON.stringify(data)})`); }); app.listen(port, () => { console.log(`JSONP server listening at http://localhost:${port}`); }); 步骤4:运行你的JSONP服务器: node server.js 步骤5:验证 浏览器中访问https://www.xxx.xxx:3000/jsonp?callback=handleResponse
  • webrtc网页代码分析二

    webrtc网页代码分析二

    交互流程 上图是完整的处理流程。 获取URL参数 const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); const deviceId = urlParams.get('deviceId'); 这段代码先获取当前页面 URL 中的查询字符串部分(window.location.search),然后使用 URLSearchParams 对象解析查询字符串,尝试获取名为 deviceId 的参数值,后续很多操作会根据这个 deviceId 的有无来进行不同的逻辑处理。 页面加载时的初始化操作 window.onload = function () { if (deviceId == undefined) { document.getElementById('video').style.display = 'none'; document.getElementById('getting-started').style.display = 'block'; } const canvas = document.createElement('canvas') canvas.width = 640 canvas.height = 480 const ctx = canvas.getContext('2d') ctx.fillStyle = 'rgba(200, 200, 200, 100)' ctx.fillRect(0, 0, 640, 480) const img = new Image(640, 480) img.src = canvas.toDataURL() document.getElementById('imgStream').src = img.src; } window.onload是页面加载完成后的回调函数,上面使用的是匿名函数,即当页面所有内容加载完成后执行的逻辑。具体逻辑如下 这里判断如果deviceId未定义(也就是可能页面直接访问,没有带上对应的参数),则隐藏视频相关区域(id为video的div),显示初始引导内容区域(id 为 getting-started 的 div)。在函数内创建了一个 canvas 元素,绘制了一个半透明的矩形,将其转换为图片数据后设置给img id=\\\\"imgStream\\\\"元素作为初始显示内容。 MQTT操作 发起连接 var options = { keepalive: 600, username: 'xxx', password: 'xxxx', }; const client = mqtt.connect('wss://xxx.xxx.com:8084/mqtt', options); 定义了 MQTT连接的相关选项(如保活时间、用户名、密码等),然后尝试连接到指定的MQTT服务器地址(wss://xxx.xxx.xxx:8084/mqtt),后续通过client对象来处理 MQTT 的各种消息收发以及相关操作。 事件处理 connect事件 client.on('connect', function () { console.log("connected") client.subscribe('webrtc/' + deviceId + '/jsonrpc-reply', function (err) { if (!err) { } }); console.log('publish to webrtc/' + deviceId + '/jsonrpc'); client.publish('webrtc/' + deviceId + '/jsonrpc', JSON.stringify({ jsonrpc: '2.0', method: 'offer', id: offerId, }), { qos: 2 }); }) 结构为client.on(\\\\'connect\\\\', function () {... }); client.on这里是mqtt对象的事件回调,这里将function(){...}作为回调函数注册进去,funciton实现是匿名函数的方式。当前收到connect事件时,就会执行function的内容。具体逻辑如下。 当成功连接到MQTT服务器后,先在控制台输出 connected 表示连接成功,然后订阅一个特定主题(webrtc/\\\\' + deviceId + \\\\'/jsonrpc-reply)用于接收回复消息,接着向另一个特定主题(webrtc/\\\\' + deviceId + \\\\'/jsonrpc)发布一条包含offer信息的 JSON 格式消息(设置了消息质量等级 qos 为 2),用于发起某种交互(可能与 WebRTC 相关的会话建立等操作)。 (1)订阅函数subscribe client.subscribe(topic, options, callback); topic: 需要订阅的主题,可以是一个字符串或者一个主题数组。主题是 MQTT 中用于分类消息的方式。主题的命名规则通常是层级的,使用斜杠(/)分隔每一层级,例如 \\\\'home/livingroom/temperature\\\\'。 options: 可选参数,配置订阅的一些选项,如 QoS(服务质量)级别。常见的选项包括: -- qos: 服务质量(Quality of Service)级别,取值范围是: -- 0 - 至多一次:消息最多发送一次,不做确认。 -- 1 - 至少一次:消息至少发送一次,可能会重复。 -- 2 - 只有一次:消息只能传递一次,不会丢失,也不会重复。 callback: 可选的回调函数,当订阅成功时会被调用。回调函数接受一个错误对象作为参数,如果订阅成功,err 为 null;如果订阅失败,则返回相应的错误信息。 (2)发布函数publish client.publish(topic, message, options, callback); topic: 需要发布消息的主题,可以是一个字符串,表示主题名称。 message: 需要发布的消息内容,可以是字符串、Buffer、对象等。一般情况下,消息会被自动转换为字符串或 Buffer(如果传入对象,会自动通过 JSON.stringify() 转换为字符串)。 options: 可选参数,配置发布消息的选项,常见的选项包括: -- qos: 服务质量(Quality of Service)级别(如前述),默认是 0。 -- retain: 是否保留消息,若为 true,代理服务器会保存消息并在新订阅者连接时发送该消息。 callback: 可选的回调函数,发布消息成功后会被调用。回调函数接受一个错误对象作为参数,如果发布成功,err 为 null;如果发布失败,则返回相应的错误信息。 messange事件 MQTT消息接收处理(client.on(\\\\'message\\\\', function (topic, message) {... })),语法与上面类似,逻辑如下。 client.on('message', function (topic, message) { let msg = JSON.parse(message.toString()); if (msg.id == offerId) { let sdp = msg.result; let offer = { type: 'offer', sdp: sdp }; log(offer); pc.setRemoteDescription(offer); pc.createAnswer().then(d => pc.setLocalDescription(d)).catch(log); } else if (msg.id == answerId) { log('receive answer ok'); } }) 当接收到MQTT消息时,先将消息内容解析为 JSON 对象,然后根据消息中的 id 判断消息类型,如果 id 与之前定义的 offerId 相等,说明是对之前 offer 的回复,会提取其中的 sdp 信息设置为远程描述(setRemoteDescription),并创建一个本地回答(createAnswer)然后设置本地描述(setLocalDescription);如果 id 等于 answerId,则只是在控制台输出表示接收到了回答消息。 WebRTC操作 创建对象 var pc = new RTCPeerConnection({ iceServers: [ { urls: "turn:rtpeer.allwinnertech.com:3478", username: "pctest", credential: "Aa123456", }, ], }); 创建了一个 RTCPeerConnection 对象用于建立 WebRTC 连接,配置了 iceServers,这里指定了一个 TURN 服务器的相关信息(地址、用户名、密码),有助于在复杂网络环境(如存在 NAT 等情况)下建立稳定的点对点连接,确保音视频数据能顺利传输。 获取PC录音 navigator.mediaDevices.getUserMedia({ video: false, audio: true }) .then(stream => { stream.getTracks().forEach(track => pc.addTrack(track, stream)); }).catch(log); 通过 navigator.mediaDevices.getUserMedia 方法请求获取音频媒体流(这里明确只要音频,video: false, audio: true),如果获取成功,将音频轨道添加到 RTCPeerConnection 对象(pc)中,以便后续传输音频数据,如果出现错误则通过 log 函数(也就是在控制台输出错误信息)进行处理。 连接状态 pc.oniceconnectionstatechange = e => { log(pc.iceConnectionState); document.getElementById('status').innerHTML = pc.iceConnectionState; if (pc.iceConnectionState == 'connected') { // default to muted if (!isMuted) onMuted(); } } 当 WebRTC 连接的 ICE 连接状态发生变化时,先在控制台输出当前连接状态(通过 log 函数),然后更新页面上 id 为 status 的元素文本内容为当前连接状态,并且当连接状态变为 connected(已连接)时,如果当前不是静音状态(!isMuted),则调用 onMuted 函数将音频静音。 ICE候选者 pc.onicecandidate = event => { if (event.candidate === null) { console.log(pc.localDescription.sdp) let json = { jsonrpc: \\'2.0\\', method: \\'answer\\', params: pc.localDescription.sdp, id: answerId, } console.log(json) client.publish(\\'webrtc/\\' + deviceId + \\'/jsonrpc\\', JSON.stringify(json)) setInterval(() => {....}, 1000); } } 当有 ICE 候选者信息产生并且当候选者为 null 时(意味着 ICE 收集过程结束),执行一系列操作,包括打印 RTCPeerConnection 的本地描述中的 sdp 信息,向 MQTT 特定主题发布包含本地描述 sdp 信息的 JSON 消息,并且每隔 1 秒获取连接的统计信息(通过 pc.getStats 方法),整理后更新到页面中 id 为 stats-box 的元素内展示出来。 当收到对端的offer候选信息后,本地会进行与stun/turn服务交互,交互获取到ICE信息后,就会触发回调这个函数,将自己SDP信息组装成json通过mqtt publish发送给对端。 接收音视频 pc.ontrack = function (event) { if (event.track.kind == \\'video\\') { var el = document.getElementById(\\'videoStream\\'); var newStream = new MediaStream(); newStream.addTrack(event.track); el.srcObject = newStream; el.autoplay = true el.controls = false el.muted = true document.getElementById(\\'imgStream\\').style.display = \\'none\\'; document.getElementById(\\'videoStream\\').style.display = \\'block\\'; // 当视频流添加成功后,获取视频分辨率和码率信息并更新显示 el.addEventListener(\\'loadedmetadata\\', function () { updateVideoInfo(event.track); }); } else if (event.track.kind == \\'audio\\') { var el = document.getElementById(\\'audioStream\\'); var newStream = new MediaStream(); newStream.addTrack(event.track); el.srcObject = newStream; el.controls = false el.muted = false } } 当通过 RTCPeerConnection 接收到远端传来的媒体轨道(视频或音频轨道)时,根据轨道类型进行不同的处理。对于视频轨道,获取页面中的视频元素(id 为 videoStream),创建新的 MediaStream 对象添加轨道后设置为视频元素的播放源,同时设置自动播放、隐藏控制条、静音等属性,并在视频元数据加载完成后(通过 loadedmetadata 事件)调用 updateVideoInfo 函数更新视频分辨率和码率信息显示;对于音频轨道,类似地获取音频元素(id 为 audioStream),添加轨道到新的 MediaStream 对象并设置为音频元素播放源,设置不显示控制条且不静音。 数据通道 const datachannel = pc.createDataChannel(\\'pear\\') datachannel.onclose = () => console.log(\\'datachannel has closed\\'); datachannel.onopen = () => { console.log(\\'datachannel has opened\\'); console.log(\\'sending ping\\'); setInterval(() => { console.log(\\'sending ping\\'); datachannel.send(\\'ping\\'); }, 1000); } datachannel.onmessage = e => { if (e.data.byteLength === undefined) { console.log(e.data); } else { // is binary data. mjpeg stream // console.log(e.data.byteLength); var arrayBufferView = new Uint8Array(e.data); var blob = new Blob([arrayBufferView], { type: \\"image/jpeg\\" }); var urlCreator = window.URL || window.webkitURL; var imageUrl = urlCreator.createObjectURL(blob); var imageElement = document.getElementById(\\'imgStream\\'); imageElement.src = imageUrl; } } 创建了一个名为pear的数据通道(const datachannel = pc.createDataChannel(\\'pear\\')),并分别对数据通道的打开、关闭、接收消息等事件绑定了相应的回调函数。例如在数据通道打开时(datachannel.onopen),会定期(每隔 1 秒)发送 ping 消息用于保持连接或检测连接状态等;在接收到消息时(datachannel.onmessage),根据消息数据类型判断,如果是文本消息直接在控制台输出,如果是二进制数据(判断为 MJPEG 流),则将其转换为图片的 URL 并设置给页面中的图片元素(id 为 imgStream)用于显示。 所以这里支持mjpeg的方式,但是是通过数据通道来实现的。
  • webrtc网页代码分析一

    webrtc网页代码分析一

    文档结构 <!doctype html> <html lang="en"> <head> ... </head> <body> ... </body> </html> 文档类型声明():声明文档使用HTML5标准。 html标签:设置文档的语言为英语(lang=\"en\")。 head标签:包含元数据、样式、外部链接等,定义网页的头部信息。 body标签:页面的主体部分,包含所有网页内容。 head标签 字符集和窗口设置 <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> meta标签:用于定义页面的字符集、视口设置、作者信息等。 charset=\\"utf-8\\":指定字符编码为UTF-8,确保网页能够正确显示多种语言的字符。 viewport 设置:用于响应式设计,确保页面在不同设备上显示良好。width=device-width 表示页面宽度等于设备的屏幕宽度,initial-scale=1 表示初始缩放比例为 1。 外部资源引入 <script src="https://unpkg.com/mqtt@4.1.0/dist/mqtt.min.js"></script> <script src="https://kit.fontawesome.com/8c8bbe3334.js" crossorigin="anonymous"></script> MQTT库:引入了 MQTT JavaScript 客户端库(版本 4.1.0)。MQTT 是一种轻量级的消息发布/订阅协议,通常用于 IoT(物联网)应用。这表明页面可能涉及到实时消息传递或设备间通信。 FontAwesome图标库:引入了 FontAwesome 的 JavaScript 库,用于提供图标支持。crossorigin=\"anonymous\" 允许浏览器在不发送凭据的情况下访问该资源。 CSS样式 style标签包含了许多样式规则,以下是主要部分的分析。 全局设置 html { font-family:"Calibri", sans-serif; box-sizing: border-box; } *, *:before, *:after { box-sizing: inherit; } font-family: \"Calibri\", sans-serif;:设置了页面字体为 \"Calibri\",如果不支持则使用 sans-serif。 box-sizing: border-box;:使得所有元素的 padding 和 border 都包含在 width 和 height 的计算中,避免了布局问题。 响应式设计 @media screen and (max-width: 650px) { .column { width: 100%; display: block; } } 使用 @media 媒体查询来调整页面布局。在屏幕宽度小于或等于 650px 时,将 .column 类的元素的宽度设置为 100%,并显示为块级元素。这样有助于在小屏幕设备(如手机)上优化布局。 卡片样式和容器 .card { box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); } .container { position: relative; text-align: center; color: white; } .card:为卡片元素设置了一个阴影效果,使其在页面上看起来更立体。 .container:设置容器为相对定位,并使文本居中。文本颜色为白色。 按钮样式 .title { color: grey; } .bottom-left, .top-left, .top-right, .bottom-right { position: absolute; padding: 10px; } .button { border: none; outline: 0; display: inline-block; padding: 8px; color: white; background-color: #000; text-align: center; cursor: pointer; } .button:hover { background-color: #555; } .title:为标题设置灰色字体颜色。 .bottom-left, .top-left, .top-right, .bottom-right:为定位在四个角的元素提供了绝对定位样式。通过定位,它们可以被放置在容器的四个角。 .button:定义了按钮的基本样式,包括无边框、白色文字、黑色背景以及光标指针。hover 样式将按钮背景色更改为灰色(#555)。 导航栏样式 .topnav { overflow: hidden; background-color: #333; } .topnav a { float: left; display: block; color: #f2f2f2; text-align: center; padding: 14px 16px; text-decoration: none; font-size: 17px; } .topnav a:hover { background-color: #ddd; color: black; } .topnav a.active { background-color: #04AA6D; color: white; } .topnav:定义了一个水平的导航栏,背景色为深灰色,且隐藏溢出的内容。 .topnav a:定义了导航栏中每个链接的样式,包括文本颜色、对齐方式、内边距等。 .topnav a:hover:设置链接在悬停时背景色变为浅灰色,文字颜色变为黑色。 .topnav a.active:当链接处于 \"活动\" 状态时,背景色变为绿色(#04AA6D),文字变为白色。 按钮样式 .btn-group button { margin-top: -4px; background-color: white; border: 1px solid white; color: black; padding: 10px 24px; cursor: pointer; } .btn-group button:hover { background-color: #f2f2f2; } .btn-group button:定义了按钮组中的按钮样式,包括背景色为白色,边框为白色,文本为黑色,且在悬停时背景色变为浅灰色。 普通按钮样式 .btn { background-color: DodgerBlue; border: none; color: white; padding: 12px 16px; font-size: 16px; cursor: pointer; } .btn:hover { background-color: RoyalBlue; } .btn:定义了一个蓝色的按钮,包含内边距、字体颜色等样式。在悬停时,按钮的背景色会变成更深的蓝色(RoyalBlue)。 body标签 body包含页面部分显示部分和script部分。 页面内容 导航栏区域 <div class="topnav" id="myTopnav"> <a href="#">Peer Project</a> <a href="https://ko-fi.com/sepfy95" style="float: right">Sponsor</a> <a href="https://github.com/sepfy/pear" style="float: right">Github</a> </div> 这是一个顶部导航栏(topnav)元素,包含三个链接: Peer Project:这是主页面的链接,指向当前页面(#)。可以是应用的标题或者主页面链接。 ko-fi.com:链接到 ko-fi.com,可能是用于捐赠或支持项目。 github: GitHub 项目的链接,用户可以访问该链接查看源代码或参与项目。 这些链接使用了 float: right 样式,使得“Sponsor”和“Github”链接都被浮动到右侧,而“Peer Project”链接则停留在左侧。 音视频展示区域 //这是包含视频内容的容器 <div class="column" id="video"> //用来包裹所有显示视频、音频及空间的内容,创建一个卡片式布局 <div class="card"> //容器内放置了视频和音频元素,包含一些展示信息的<p>标签 <div class="container"> // 媒体元素信息 //图片元素,在没有视频流可用时显示,他的id是imgStream <img id="imgStream" style="width: 100%"> //video元素,用于显示视频流,display样式为none,默认视频流不可见。通过js控制 <video id="videoStream" playsinline style="width:100%; display: none"></video> //audio元素,用于播放音频流的,默认被隐藏,通过js控制。 <audio id="audioStream" style="display: none"></audio> //用于设置设备信息 //段落元素用于显示设备信息,这里是device id <p class="top-left" id="device-id"></p> //段落元素,用于显示设备当的状态。 <p class="top-right" id="status">waiting</p> </div> //控制按钮,包括音量、录音、旋转、暂停 <div class="btn-group" style="width:100%"> //控制音量的按钮,调用onVolume函数,图标为fa-solid fa-volume-xmark <button style="width:25%" class="btn" onclick="onVolume()"><i id="volume-icon" class="fa-solid fa-volume-xmark"></i></button> //控制是否静音,调用onMuted函数,图标为fa-solid fa-microphone-slash <button style="width:25%" class="btn" onclick="onMuted()"><i id="mute-icon" class="fa-solid fa-microphone-slash"></i></button> //用于旋转视频,点击调用onRotate函数,图标为solid fa-arrows-rotate <button style="width:25%" class="btn" onclick="onRotate()"><i class="fa-solid fa-arrows-rotate"></i></button> //用于停止视频或音频流,点击调用onStop函数,图标为fa-solid fa-circle-stop <button style="width:25%" class="btn" onclick="onStop()"><i id="stop-icon" class="fa-solid fa-circle-stop"></i></button> </div> </div> </div>
  • javascript之dom

    javascript之dom

    什么是dom DOM(Document Object Model)是一个编程接口,它将 HTML 或 XML 文档呈现为一个由节点和对象(这些节点和对象其实是文档的各种元素、属性和文本内容等)组成的树形结构。这个树形结构允许开发者使用编程语言(如 JavaScript)来访问、修改和操作文档的内容、结构和样式。对于以下简单的 HTML 文档: <!DOCTYPE html> <html> <head> <title>My Page</title> </head> <body> <h1>Hello, World!</h1> <p id=\\\"myParagraph\\\">This is a paragraph.</p> </body> </html> 它的 DOM 树结构大致如下: - html 节点是根节点,它包含两个子节点:head 和 body。 - head 节点包含一个子节点 title,title 节点的文本内容是 \\\\\\\"My Page\\\\\\\"。 - body 节点包含两个子节点:h1 和 p。h1 节点的文本内容是 \\\\\\\"Hello, World!\\\\\\\",p 节点有一个属性 id 为 \\\\\\\"myParagraph\\\\\\\",其文本内容是 \\\\\\\"This is a paragraph.\\\\\\\"。 获取dom元素 getElementById 这是最常用的方法之一,用于通过元素的id属性获取单个元素。id在HTML文档中应该是唯一的,语法如下。 document.getElementById(\\\"elementId\\\"); 在上面的 HTML 文档中,如果要获取p元素,可以使用以下 JavaScript 代码: var paragraph = document.getElementById(\\\"myParagraph\\\"); console.log(paragraph.textContent); // 输出:This is a paragraph. getElementsByTagName 根据标签名获取元素集合。它返回一个类似数组的对象(HTMLCollection),包含所有匹配标签名的元素。语法如下: document.getElementsByTagName(\\\"tagName\\\"); 要获取文档中的所有 h1 元素: var h1Elements = document.getElementsByTagName(\\\"h1\\\"); for (var i = 0; i < h1Elements.length; i++) { console.log(h1Elements[i].textContent); // 输出:Hello, World! } getElementsByClassName 通过类名获取元素集合。同样返回一个 HTMLCollection,包含所有具有指定类名的元素。 document.getElementsByClassName(\\\"className\\\"); 假设HTML中有多个元素有highlight类,如下: <p class=\\\"highlight\\\">This is a highlighted paragraph.</p> <span class=\\\"highlight\\\">This is a highlighted span.</span> 可以使用以下代码获取这些元素: var highlightedElements = document.getElementsByClassName(\\\"highlight\\\"); for (var i = 0; i < highlightedElements.length; i++) { console.log(highlightedElements[i].textContent); // 输出两个元素的文本内容 } 修改元素 修改元素内容 textContent属性 用于获取或设置元素的文本内容,如改变前面获取的p元素的内容 var paragraph = document.getElementById(\\\"myParagraph\\\"); paragraph.textContent = \\\"This is a new paragraph content.\\\"; innerHTML属性 用于获取或设置元素内部的HTML内容。可以用于添加或修改元素内部的标签和文本。如在前面p元素内部添加一个strong标签: var paragraph = document.getElementById(\\\"myParagraph\\\"); paragraph.innerHTML = \\\"This is a <strong>modified</strong> paragraph.\\\"; 修改元素属性 获取和设置属性 可以通过元素对象的属性来获取和设置大多数 HTML 属性。例如,对于一个 元素的 src 属性。假设 HTML 中有一个img元素,可以这样修改其src属性: <img id=\\\"myImage\\\" src=\\\"original.jpg\\\"> var image = document.getElementById(\\\"myImage\\\"); image.src = \\\"new.jpg\\\"; 使用setAttribute和getAttribute setAttribute 用于设置元素的属性,getAttribute 用于获取元素的属性。如对于上面的img元素,也可以这样操作: var image = document.getElementById(\\\"myImage\\\"); image.setAttribute(\\\"src\\\", \\\"new.jpg\\\"); var currentSrc = image.getAttribute(\\\"src\\\"); console.log(currentSrc); // 输出:new.jpg 创建与添加DOM元素 创建元素 使用createElement方法。具体为先使用 document.createElement(\\\\\\\"tagName\\\\\\\") 创建一个新的 HTML元素。如创建一个新的div元素并设置其 id 和内容,如下。 var newDiv = document.createElement(\\\"div\\\"); newDiv.id = \\\"newDiv\\\"; newDiv.textContent = \\\"This is a new div.\\\"; 添加元素 appendChild 使用appendChild方法,将一个元素添加为另一个元素的子元素。例如,将新创建的div元素添加到 body元素中。 var body = document.getElementsByTagName(\\\"body\\\")[0]; var newDiv = document.createElement(\\\"div\\\"); newDiv.id = \\\"newDiv\\\"; newDiv.textContent = \\\"This is a new div.\\\"; body.appendChild(newDiv); insertBefore 在指定的子元素之前插入一个新元素。它需要两个参数:要插入的新元素和参考元素(新元素将插入在参考元素之前)。如假设有两个 p 元素在 body 中,在第一个 p 元素之前插入新的 div 元素。 <body> <p>First Paragraph</p> <p>Second Paragraph</p> </body> var body = document.getElementsByTagName(\\\"body\\\")[0]; var newDiv = document.createElement(\\\"div\\\"); newDiv.id = \\\"newDiv\\\"; newDiv.textContent = \\\"This is a new div.\\\"; var firstParagraph = document.getElementsByTagName(\\\"p\\\")[0]; body.insertBefore(newDiv, firstParagraph); 删除DOM元素 用于从父元素中删除指定的子元素,语法如下。 parentElement.removeChild(childElement); 如假设要删除前面添加的div元素(假设它有 id 为 \\\\\\\"newDiv\\\\\\\")。 var divToRemove = document.getElementById(\\\"newDiv\\\"); var body = document.getElementsByTagName(\\\"body\\\")[0]; body.removeChild(divToRemove);
  • 快速搭建一个可访问的网页

    快速搭建一个可访问的网页

    简介 搭建网站需要以下几个组件, Web服务器(如Apache、Nginx):它是网页能够被访问的核心组件。当用户在浏览器中输入网站的域名或 IP 地址并请求访问网页时,Web 服务器软件会接收这些 HTTP 请求。负责从服务器的存储设备(如硬盘)中找到对应的网页文件(如 HTML、CSS、JavaScript 文件等),并将这些文件发送回用户的浏览器。 编程语言支持(如PHP、Python、Node.js):实现网页的动态生成和业务逻辑,如果只是静态网页或不需要服务器端脚本处理PHP也不需要安装。 SSL证书(可选):用于加密HTTP通信,提升安全性。如果不需要安全链接,也不需要安装。 数据库(如MySQL、PostgreSQL)(可选):如果网页需要存储和管理大量的数据(如用户信息、文章内容、产品信息等),数据库就非常重要。 安装 安装nginx apache和nginx可以二选一 软件安装步骤 步骤1: 执行命令安装nginx sudo yum install nginx 步骤2: 启动nginx sudo systemctl start nginx 步骤3:查看启动状态 sudo systemctl status nginx 配置证书支持HTTPS 步骤1: 打开nginx.conf文件 /etc/nginx/nginx.conf 步骤2: 配置证书路径 步骤3: 重启nginx sudo systemctl restart nginx 根页面 在nginx.conf文件中,可以配置根页面,如上图的root /usr/share/nginx/html。 安装Apache 软件安装步骤 步骤1:执行命令安装Apache及其扩展包。 sudo yum -y install httpd httpd-manual httpd-devel mod_ssl mod_perl php-mysqli 如果回显信息显示Complete!,则表示Apache安装成功。 步骤2:依次执行命令启动Apache并设置自启动。 sudo systemctl start httpd sudo systemctl enable httpd 步骤3:执行命令查看Apache运行状态。如果回显信息显示active(running)时,表示Apache已启动。 systemctl status httpd 4.在本地电脑浏览器地址栏中输入http://<ECS服务器的公网IP>,测试Apache服务是否安装成功。如果显示如图所示的测试页面,表示Apache服务已安装成功。ECS服务器的弹性公网IP可以从ECS实例页面获取。 至此,按照上面流程,就可以访问一个页面了。页面的位置在;/usr/share/httpd/noindex/index.html 证书更新步骤 步骤1:找到ssl.conf文件 cd /etc/httpd/conf.d 步骤2: 编辑ssl.conf更新SSLCertificateFile、SSLCertificateKeyFile、SSLCertificateChainFile。 步骤3: 重启Apache。 sudo systemctl restart httpd 通过以上的步骤,把你的网页放到/var/www/html下面,远端就可以访问了。 数据库 暂时用不到,后续再补充。 PHP 暂时用不到,后续再补充。 其他 关于开机自启动 设置应用开机自启动 systemctl enable nginx 查询应用是否开机 systemctl list-unit-files | grep nginx 或者systemctl is-enabled nginx 关闭应用自启动 systemctl disable httpd
  • openwrt规则编译cmake软件包

    openwrt规则编译cmake软件包

    软件包中有完整CMakelists.txt include $(TOPDIR)/rules.mk PKG_NAME:=usrsctp PKG_VERSION:=0.9.5.0 PKG_RELEASE:=3 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:= https://codeload.github.com/sctplab/usrsctp/tar.gz/v$(PKG_VERSION)? PKG_HASH:=260107caf318650a57a8caa593550e39bca6943e93f970c80d6c17e59d62cd92 PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE PKG_BUILD_PARALLEL:=1 include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/cmake.mk define Package/usrsctp SECTION:=libs CATEGORY:=Libraries TITLE:=sctp stack URL:=https://github.com/sctplab/usrsctp endef define Package/cJSON/description this is a userland SCTP stack endef CMAKE_OPTIONS += -DBUILD_SHARED_LIBS=on #Provide compilation dependencies for other modules to call define Build/InstallDev $(INSTALL_DIR) $(1)/usr/include $(CP) $(PKG_INSTALL_DIR)/usr/include/usrsctp.h $(1)/usr/include $(INSTALL_DIR) $(1)/usr/lib $(CP) $(PKG_INSTALL_DIR)/usr/lib/libusrsctp.so* $(1)/usr/lib/ $(INSTALL_DIR) $(1)/usr/lib/pkgconfig $(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/usrsctp.pc $(1)/usr/lib/pkgconfig $(SED) 's,/usr,$(STAGING_DIR)/usr,g' $(1)/usr/lib/pkgconfig/usrsctp.pc endef #Installed to the root file system for call at runtime define Package/usrsctp/install $(INSTALL_DIR) $(1)/usr/lib $(CP) $(PKG_INSTALL_DIR)/usr/lib/libusrsctp.so* $(1)/usr/lib/ endef $(eval $(call BuildPackage,usrsctp)) 如上是示例,这种类型比较简单,包含了cmake.mk后,openwrt会自动生成编译,只需要安装需要被其他代码依赖的库和头文件,以及运行时需要的库即可。 软件包中有完整CMakelists.txt但需要创建build目录 include $(TOPDIR)/rules.mk PKG_NAME:=libsrtp PKG_VERSION:=2.6.0 PKG_RELEASE:=3 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/cisco/libsrtp/tar.gz/v$(PKG_VERSION)? PKG_HASH:=bf641aa654861be10570bfc137d1441283822418e9757dc71ebb69a6cf84ea6b PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE PKG_BUILD_PARALLEL:=1 include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/cmake.mk define Package/libsrtp SECTION:=libs CATEGORY:=Libraries TITLE:=libsrtp Client Library URL:=https://github.com/cisco/libsrtp endef define Package/libsrtp/description srtp Library endef CMAKE_BINARY_SUBDIR=build CMAKE_OPTIONS += -DBUILD_SHARED_LIBS=on -DTEST_APPS=off define Build/InstallDev $(INSTALL_DIR) $(1)/usr/include/srtp2 $(CP) $(PKG_INSTALL_DIR)/usr/include/srtp2/*.h $(1)/usr/include/srtp2 $(INSTALL_DIR) $(1)/usr/lib $(CP) $(PKG_INSTALL_DIR)/usr/lib/lib*.so* $(1)/usr/lib/ endef define Package/libsrtp/install $(INSTALL_DIR) $(1)/usr/lib $(CP) $(PKG_INSTALL_DIR)/usr/lib/lib*.so* $(1)/usr/lib/ endef $(eval $(call BuildPackage,libsrtp)) 这种相对前面的方式类似,只是这种软件包CMakelist.text要求需要创建build目录进行编译,因此在Makefile中添加CMAKE_BINARY_SUBDIR=build进行声明。因此在编译输出目录,可以看到openwrt会先创建build目录,而第一种方式是没有创建build目录。 软件包中只有部分CMakelist.text 这种方式就是软件包中没有提供完整的CMakelist.txt,但是提供了部分,需要用户包含提供部分的Cmakelist.txt实现,这种方式目前是通过打patches的方式生成主要CMakelist.txt。 Makefile文件 include $(TOPDIR)/rules.mk PKG_NAME:=coreMQTT PKG_VERSION:=2.3.1 PKG_RELEASE:=3 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:= https://codeload.github.com/FreeRTOS/coreMQTT/tar.gz/v$(PKG_VERSION)? PKG_HASH:=b8e95044e6ef8381610949b7fe546c5ddf4e52989ce9938209d5dd5f3371b5d7 PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE PKG_BUILD_PARALLEL:=1 include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/cmake.mk define Package/coreMQTT SECTION:=libs CATEGORY:=Libraries TITLE:=coreMQTT Client Library URL:=https://github.com/FreeRTOS/coreMQTT endef define Package/coreMQTT/description coreMQTT Client Library endef define Build/InstallDev $(INSTALL_DIR) $(1)/usr/include $(CP) $(PKG_INSTALL_DIR)/*.h $(1)/usr/include $(CP) $(PKG_INSTALL_DIR)/include/*.h $(1)/usr/include $(INSTALL_DIR) $(1)/usr/lib $(CP) $(PKG_INSTALL_DIR)/usr/lib/lib*.so* $(1)/usr/lib/ endef define Package/coreMQTT/install $(INSTALL_DIR) $(1)/usr/lib $(CP) $(PKG_INSTALL_DIR)/usr/lib/libcoreMQTT.so* $(1)/usr/lib/ endef $(eval $(call BuildPackage,coreMQTT)) patches diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..754a6a2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.10) + +project(CoreMQTT) + +include(${CMAKE_CURRENT_LIST_DIR}/mqttFilePaths.cmake) + +add_definitions("-DMQTT_DO_NOT_USE_CUSTOM_CONFIG") + +include_directories ( + ${MQTT_INCLUDE_PUBLIC_DIRS} +) + +add_library(coreMQTT SHARED ${MQTT_SOURCES} ${MQTT_SERIALIZER_SOURCES}) + +install(TARGETS coreMQTT + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +install(DIRECTORY ${MQTT_INCLUDE_PUBLIC_DIRS}/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ + FILES_MATCHING PATTERN "*.h" +) -- 2.25.1 以上是通过打patches生成主CMakelist.txt,可以看到包含了mqttFilePaths.cmake文件。
  • Cmake构建模版

    Cmake构建模版

    # 指定 CMake 的最低版本要求。它应该是 CMakeLists.txt 文件中的第一个命令。 cmake_minimum_required(VERSION 3.10) # 定义项目的名称 project(mpp_webrtc) # 要编译的源文件,先收集在SRCS变量中。 file(GLOB SRCS "*.c" "common/*.c") # 编译源文件的头文件路径 include_directories( ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/example/common /lib/xxx/a /lib/xxx/b ) # 需要链接的库路径 link_directories( /lib/A /lib/B /lib/C ) #可执行文件名称,以及依赖的源文件 add_executable(mpp_webrtc ${SRCS}) # 需要链接的库,如libwav.a, libaac.a等。其中可以使用-Wl,--start-group # 和-Wl,--end-group编译参数。 target_link_libraries(mpp_webrtc peer pthread -Wl,--start-group wav aac VE vencoder ... -Wl,--end-group )
  • 部署MQTT云服务

    部署MQTT云服务

    Centos安装 配置emqx的源 curl -s https://assets.emqx.com/scripts/install-emqx-rpm.sh | sudo bash 安装emqx sudo yum install emqx -y 启动emqx sudo systemctl start emqx 如果要卸载 sudo yum remove emqx 后台配置 访问前,服务端的端口18083权限打开。 截屏2024-12-01 08.53.43 xxx.xxx.xxx:18083 默认登录账号:admin,public。首次登录会要求更新密码。 如果要支持tcp/ssl/ws/wss这几种监听,也需要把相应的端口打开以及服务器端口权限打开。 测试访问 使用mosquitto测试 这里使用的是ubuntu系统上的mosquito_sub/pub工具测试。先安装工具: sudo apt-get install mosquitto-clients 订阅 mosquitto_sub -h www.xxx.xxx -t \"test\" -v 发布 mosquitto_pub -h www.laumy.tech -t \"test\" -m \"hello world\" 使用MQTTX软件测试 下载地址:https://mqttx.app/zh/downloads 建立一个回话连接用于订阅消息 接着对填写订阅主题 再建立一个回话用于发布消息 连接上后进行发布主题 这样订阅的回话就收到订阅消息了 开启SSL/TLS连接 参考:https://docs.emqx.com/zh/emqx/v5.8/network/overview.html 启动异常排查 查找emqx.log cd / find -name \"emqx.log*\" 参考: https://docs.emqx.com/zh/cloud/latest/connect_to_deployments/react_sdk.html https://docs.emqx.com/zh/emqx/v5.8/connect-emqx/developer-guide.html
\t