浏览器动态显示服务器日志,基于 websocket 实现远程实时日志 在浏览器中查看设备的运行日志...

2023-11-12

本文介绍一个基于websocket实现的远程实时日志系统,可以通过浏览器查看远程移动设备的实时运行日志。

系统由三个部分组成:

1. 服务器:与移动设备和浏览器建立websocket连接,将移动设备websocket上读取的实时日志转发到对应的浏览器的websocket中

2. 浏览器日志查看页面:与服务器建立websocket连接,通过websocket接收指定设备的实时运行日志并显示

3. 移动设备:与服务器建立websocket连接,将运行日志通过websocket连接上传至服务器

服务器端实现

Tomcat 7.0.27 开始支持Websocket了。本文的服务器端Servlet程序是搭建在Tomcat上的。关于在Tomcat上面实现支持websocket的servlet,可以参考

服务器Servlet源码:

importjava.io.IOException;

importjava.nio.ByteBuffer;

importjava.nio.CharBuffer;

importjava.util.Map;

importjava.util.Set;

importjava.util.concurrent.ConcurrentHashMap;

importjava.util.concurrent.CopyOnWriteArraySet;

importjavax.servlet.annotation.WebServlet;

importjavax.servlet.http.HttpServletRequest;

importorg.apache.catalina.websocket.MessageInbound;

importorg.apache.catalina.websocket.StreamInbound;

importorg.apache.catalina.websocket.WebSocketServlet;

importorg.apache.catalina.websocket.WsOutbound;

/**

* Servlet implementation class WebLogcat

*/

@WebServlet("/WebLogcat")

publicclassWebLogcatextendsWebSocketServlet {

privatestaticfinallongserialVersionUID = 1L;

privatefinalSet connections =

newCopyOnWriteArraySet();

privatefinalMap devices =

newConcurrentHashMap();

privatefinalMap> browsers =

newConcurrentHashMap>();

@Override

protectedStreamInbound createWebSocketInbound(String arg0,

HttpServletRequest arg1) {

String id = arg1.getParameter("id");

String type = arg1.getParameter("type");

if( id !=null&& type !=null) {

if( type.equalsIgnoreCase("device") ) {

returnnewDeviceMessageInbound( id );

} elseif( type.equalsIgnoreCase("browser") ) {

returnnewBrowserMessageInbound( id );

}

}

// return NULL will lead to Exception

returnnewLogMessageInbound();

}

privatefinalclassDeviceMessageInboundextendsMessageInbound {

privateString _id;

DeviceMessageInbound(String id) {

_id = id;

}

@Override

protectedvoidonClose(intstatus) {

// remove me from device hash map

devices.remove(_id);

super.onClose(status);

}

@Override

protectedvoidonOpen(WsOutbound outbound) {

// add me to device hash map

devices.put(_id, this);

super.onOpen(outbound);

}

@Override

protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {

}

@Override

protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {

// broadcast to all browser with the same id as me

String message = newString(arg0.array());

Set list = browsers.get( _id );

if( list !=null) {

for(BrowserMessageInbound connection : list) {

try{

CharBuffer buffer = CharBuffer.wrap(message);

connection.getWsOutbound().writeTextMessage(buffer);

} catch(IOException ignore) {

// Ignore

}

}

}

}

}

privatefinalclassBrowserMessageInboundextendsMessageInbound {

privateString _id;

@Override

protectedvoidonClose(intstatus) {

synchronized( browsers ) {

Set list = browsers.get( _id );

if( list !=null) {

list.remove(this);

if( list.isEmpty() ) {

browsers.remove(_id);

}

}

}

super.onClose(status);

}

@Override

protectedvoidonOpen(WsOutbound outbound) {

synchronized( browsers ) {

if( browsers.containsKey(_id) ) {

browsers.get(_id).add(this);

} else{

Set list = newCopyOnWriteArraySet();

list.add(this);

browsers.put(_id, list);

}

}

super.onOpen(outbound);

}

BrowserMessageInbound(String id) {

_id = id;

}

@Override

protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {

}

@Override

protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {

}

}

privatefinalclassLogMessageInboundextendsMessageInbound {

@Override

protectedvoidonClose(intstatus) {

connections.remove(this);

super.onClose(status);

}

@Override

protectedvoidonOpen(WsOutbound outbound) {

super.onOpen(outbound);

connections.add(this);

}

@Override

protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {

}

@Override

protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {

}

}

}

要实现支持websocket的servlet,需要继承WebSocketServlet, 实现createWebSocketInbound函数,返回代表websocket连接的对象。

本文中,程序接收请求中传递的id和type参数,iid代表了设备的id,或者浏览器想要查看实时日志的设备的id。type代表了连接请求是来自设备还是浏览器。Servlet根据type的值创建对应类型的websocket连接。如果连接请求是来自设备device,则创建DeviceMessageInbound。如果设备请求是来自浏览器browser,则创建BrowserMessageInbound。

DeviceMessageInbound 和 BrowserMessageInbound 都继承自MessageInbound

对于DeviceMessageInbound,

在onOpen()中,将自己加入到设备Map表,

在onClose()中,将自己从设备Map表中移除。

onTextMessage()就是Servlet收到了来自设备的文本消息。Servlet根据设备id,找到所有正在监听此设备实时日志的browser,把文本消息转发给browser。

对于BrowserMessageInbound,

在onOpen()中,要将自己加入到监听对于id的browser列表中。由于针对同一个设备id,允许多个浏览器同时查看其实时日志。所以要先判断是否已经存在对应id的浏览器连接。如果不存在,则创建一个列表,并将此列表插入到browsers这个Map中。如果已经存在,则根据id找到列表,将自己加入到此列表中。

在onClose中,通过id找到列表,将自己从列表中移除。移除后如果列表为空,在将列表从browsers Map中移除。

浏览器端实现

html>

WebLogcat

input#chat {

width: 410px

}

#console-container {

width: 400px;

}

#console {

border: 1px solid #CCCCCC;

border-right-color: #999999;

border-bottom-color: #999999;

height: 170px;

overflow-y: scroll;

padding: 5px;

width: 100%;

}

#console p {

padding: 0;

margin: 0;

}

var Chat= {};

Chat.socket=null;

Chat.connect= (function(host) {

if ('WebSocket' in window) {

Chat.socket=newWebSocket(host);

} else if ('MozWebSocket' in window) {

Chat.socket=newMozWebSocket(host);

} else {

Console.log('Error: WebSocket is not supported by this browser.');

return;

}

Chat.socket.onopen=function() {

Console.log('Info: WebSocket connection opened.');

document.getElementById('chat').οnkeydοwn=function(event) {

if (event.keyCode== 13) {

Chat.sendMessage();

}

};

};

Chat.socket.onclose=function() {

document.getElementById('chat').οnkeydοwn=null;

Console.log('Info: WebSocket closed.');

};

Chat.socket.onmessage=function(message) {

Console.log(message.data);

};

});

Chat.initialize=function() {

if (window.location.protocol== 'http:') {

Chat.connect('ws://' + window.location.host + '/WebLogcat/WebLogcat?id=fv0557&type=browser');

} else {

Chat.connect('wss://' + window.location.host + '/WebLogcat/WebLogcat?id=fv0557&type=browser');

}

};

Chat.sendMessage= (function() {

var message=document.getElementById('chat').value;

if (message != '') {

Chat.socket.send(message);

document.getElementById('chat').value='';

}

});

var Console= {};

Console.log= (function(message) {

var console=document.getElementById('console');

var p=document.createElement('p');

p.style.wordWrap='break-word';

p.innerHTML=message;

console.appendChild(p);

while (console.childNodes.length >25) {

console.removeChild(console.firstChild);

}

console.scrollTop=console.scrollHeight;

});

Chat.initialize();

Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable

Javascript and reload this page!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

浏览器动态显示服务器日志,基于 websocket 实现远程实时日志 在浏览器中查看设备的运行日志... 的相关文章

  • pyinstaller 打包pyqt5 之报错 no module named pyqt5.sip

    使用PyQt5做了一个GUI程序 在ubuntu 18运行源码没有问题 但是打包以后报错 虽然在我的程序中没有显式的使用到PyQt5 sip 但是PyQt5 Qtcore中会导入该模块 no module named pyqt5 sip 下
  • java 运行 加载jar_如何在运行时加载JAR文件

    用现有数据重新加载现有类可能会破坏一些东西 您可以相对容易地将新代码加载到新的类加载器中 ClassLoader loader URLClassLoader newInstance new URL yourURL getClass getC
  • JavaScript 的 Worker API 实现代码

    JavaScript 的 Worker API 允许你在后台运行脚本 从而不影响页面的性能 以下是一个简单的例子 创建一个名为 worker js 的文件 并在里面编写要在后台运行的脚本 self addEventListener mess
  • AutoSar标准下ADC的结果访问(Adc_ReadGroup与Adc_GetStreamLastPointer)的异同

    系列文章目录 等待更新中 文章目录 系列文章目录 前言 1 什么是ADC的结果访问 2 ADC访问模式 3 AUTOSAR标准配置案例分析 3 1 Configuration 3 2 图形展示结果指针初始化 3 3 使用Adc GetStr
  • 数据集下载与保存

    数据集下载与保存 使用如下代码发现无法正常下载 import torchvision from torch utils tensorboard import SummaryWriter dataset transform torchvisi
  • openSUSE Leap 15.4 防火墙开启与关闭,端口开启与关闭

    openSUSE Leap 15 4 防火墙开启与关闭 端口开启与关闭 1 查看防火墙状态 开启 关闭 查看防火墙当前状态 rcfirewalld status 示例结果 当前状态为开启中 开启防火墙 rcfirewalld start 关
  • 安卓APP_ 布局(2) —— RelativeLayout相对布局

    摘自 安卓APP 布局 2 RelativeLayout相对布局 作者 丶PURSUING 发布时间 2021 04 05 20 19 54 网址 https blog csdn net weixin 44742824 article de
  • 解决Nginx部署Vue项目第一次访问正常第二次访问404的问题

    location add header Access Control Allow Origin root data static project dist try files uri uri index html index index h
  • DNS资源记录详解

    资源记录 resourse record 就是域名服务器保存的记录 也是解析器请求的内容 资源记录保存在zone文件中 域 domain 以 www baidu com 为例 com是一个域 baidu com是一个域 他是com域的一个子
  • 啥是ddl?

    我们在安装软件的时候 经常出现某一个dll文件丢失 无法安装 让人头痛不已 那么到底啥是ddl 还是有度娘好呀 下面是百度百科的词条 数据库模式定义语言DDL Data Definition Language 是用于描述数据库中要存储的现实
  • NFTScan NFT API 在 NFTFi 开发中的应用

    NFTFi 是 NFT 和 Finance 的缩写 旨在 增加 NFT 流动性 提供现金流 NFTFi 是为 NFT 提供金融实用性的去中心化协议和应用程序的新兴生态系统 及使用 NFT 作为基础层在其上建设经济基础设施 在实践中 NFTF
  • 7.7 SHEIN希音笔试

    投的Java工程师岗位 题型为10道单选 5道多选 1道算法题 1道sql题 时长1h 选择题 1 spring事物的传播特性 错误的是 PROPAGATION REQUIRED 支持当前事务 如果当前没有事务 就抛出异常 应该是 支持当前
  • wireshark流量分析网络安全

    目录 前言 题目1 题目2 题目3 题目4 题目5 题目6 题目7 题目8 前言 这是关于一篇wireshark分析数据包的文章 主要是网络安全里的应用 讲述了wireshark的使用方法 主要使用的事wrieshark里的追踪流 欢迎大家
  • elementUI 下拉框select 为多选 赋值回显问题

    下拉框为多选时 绑定的值为数组状态 所以点击编辑赋值时 需要处理为数组 但我遇到的问题是 可以赋值 但不是想要的结果 我想要显示label 但显示的是value 不想得到的效果 想要的是 在赋值后面加 map Number 就可以对号入座了
  • 【翻译】 使用 SFrame 进行可靠的用户空间堆栈跟踪

    请考虑订阅 LWN订阅是 LWN net 的生命线 如果您喜欢这些内容并希望看到更多 您的订阅将有助于确保 LWN 继续蓬勃发展 请访问此页面加入我们 让 LWN 继续在网络上传播 作者 Jonathan Corbet 2023 年 5 月
  • html页面传list,后端list集合中的数据传递到前台HTML中显示(表格形式)

    关键字 web项目中前后台数据传递问题 在学习web项目的过程中 我们肯定会遇到前后台数据交换问题 这个问题我也思考了很久 今天借此总结一下 由于博主水平有限 如有不当之处 还请大家多多指正 废话不所说进入正题 一 HTML页面通过ajax
  • 《Thinking_in_java_4th》持续输出中.......

    目录标题 一 文章目录 二 源码链接 一 文章目录 Java设计者们说过 设计这门语言的灵感主要来自C Java编程思想 第 2章 一切都是对象 Java编程思想 第 4章 控制执行流程 Java编程思想 第14章 类型信息 Java编程思
  • 排名前 16 的 Java 工具类

    原链接 https mp weixin qq com s s6IfovcE LGlZJxIKfT dw 目录 org apache commons io IOUtils org apache commons io FileUtils org
  • 磁耦隔离与传统隔离的区别

    磁耦隔离与传统隔离的区别 传统隔离技术 传统的隔离方式有哪些 这里有三种通常的隔离技术 光电隔离 变压器隔离 磁耦是芯片级变压器隔离技术 电容隔离 在体积 成本 性能等各方面都有优缺点 传统的隔离方式是光电隔离 什么是光耦 什么是光隔离 光

随机推荐

  • Qt GraphicsView框架中实现多个item之间的层次调整功能

    目的 要实现GraphicsView中多个item之间的层次调整功能 即 选中的item可以实现 移动至顶层 移动至底层 上移一层 下移一层 等功能 之前盲目地认为Qt API会提供 获取与之相邻的sibling item 类似这样的接口
  • 2023全新SF授权系统源码 V3.7全开源无加密版本,亲测可用

    2023全新SF授权系统源码 V3 7全开源无加密版本 网站搭建很简单 大致看来一下应该域名解析后上传源码解压 访问域名 install就能直接安装 程序功能简介 1 盗版入库 26种 2 快捷登录 3 采用layuiadmin框架 4 易
  • ASP.NET core MVC动作过滤器执行顺序

    using Microsoft AspNetCore Mvc Filters using System using System Threading Tasks namespace dotnet core Filter public cla
  • 两片74161实现60进制_74LS161设计60进制计数器-数电课程设计

    计数器是一个用以实现计数功能的时序部件 它不仅可用来及脉冲数 还常用作数子系统的定时 分频和执行数字运算以及其它特定的逻辑功能 计数器种类很多 按构成计数器中的各触发器是否使用一个时钟脉冲源来分 有同步计数器和异步计数器 根据计数制的不同
  • js怎么改变样式中的属性值

    可以使用JavaScript来改变HTML元素的样式属性值 具体方法如下 通过id属性获取要修改的元素对象 var obj document getElementById element id 修改元素的样式属性值 obj style pr
  • 错误: 至少有一个需要的隐性或转发依赖函数没找到。_【翻译】自动柯里化Rust函数...

    原文标题 Auto currying Rust Functions 原文链接 https peppe rs posts auto currying rust functions 公众号 Rust碎碎念 本文包含Rust中过程宏 proced
  • 《疯狂Java讲义》读书笔记(一):面向对象,数据类型和运算符,流程控制与数组

    序言 疯狂Java讲义 这本书深入介绍了Java编程的相关方面 全书内容覆盖了Java的基本语法结构 Java的面向对象特征 Java集合框架体系 Java泛型 异常处理 JavaGUI编程 JDBC数据库编程 Java注释 Java的IO
  • Yii Framework 开发教程(6) CComponent 组件

    在Hangman中定义的GameController使用到一些属性word 可以使用 this gt word 的格式来读写这个属性 但实际上在GameController对应到这个属性的方法为 php view plain copy pr
  • 机器学习之集成学习

    一 介绍 集成学习 Ensemble Learning 是一种机器学习技术 通过结合多个学习器 例如决策树 神经网络 支持向量机等 的预测结果 来达到更好的分类或回归预测性能 集成学习可以通过降低模型的方差 提高模型的稳定性和泛化性能 从而
  • greenDao官网

    http greenrobot org greendao documentation
  • 基于Keras实战项目-猫狗熊猫分类大战

    欢迎来到本博客 本次博客内容将继续讲解关于OpenCV的相关知识 作者简介 目前计算机研究生在读 主要研究方向是人工智能和群智能算法方向 目前熟悉深度学习 keras pytorch yolo python网页爬虫 机器学习 计算机视觉 O
  • 三个月华为od工作感受:关于转正,身份和适合谁

    三个月对Od认识的变化 关于华为Od在网上已经被讨论得很多了 在各大IT求职论坛中Od都成为流量密码了 一旦有人谈起od评论区就会开吵 这几个月中我对Od的认识也是从浅入深 对Od的态度也在变化 今年 2022年 4月份的时候那时候我刚入职
  • Redis实现商品秒杀

    随着互联网的发展和消费者的需求越来越高 商品的销售也变得越来越激烈 而对于商家来说 最直观的解决方式即为促销活动 然而 促销活动也会引发一定的风险 如果处理得不当 可能会出现 抢购 活动中的库存不足等问题 本文将利用Redis实现商品秒杀
  • 离线部署node项目、nuxt项目

    如果你的目标系统不具备互联网访问功能 或者具有严格的防火墙管控 并且你想部署一个node应用 那么以下内容可能对你有些帮助 准备好源代码工程 准备好一个具有相同node环境且具备访问互联网功能的同种系统 以下称NetOS 将源代码工程目录拷
  • 一个简单的登录注册界面流程介绍

    登录页面实现 其他页面的实现可以到github上克隆下来 login interface login server 一 用户登录 1 密码登录 流程 用户输入密码 表单使用正则验证用户名和密码格式 点击登录 对密码进行加密 并发送登录验证请
  • LeetCode每日一练 —— 88. 合并两个有序数组

    前言 Wassup guys 我是Edison 今天是 LeetCode 上的 leetcode 88 合并两个有序数组 Let s get it 文章目录 1 题目分析 2 题目图解 思路一 思路二 3 代码实现 1 题目分析 给你两个按
  • ENU、EPSG、ECEF坐标系科普(三维重建)

    科普一 ENU和EPSG实际上代表了两个不同的概念 这两者并不是直接对比的 1 ENU坐标系 ENU坐标系是一种本地切面坐标系 用于表示与地理位置相关的空间数据 在ENU坐标系中 E代表东 East N代表北 North U代表上 Up 它
  • LeetCode 406. Queue Reconstruction by Height 解题报告

    LeetCode 406 Queue Reconstruction by Height 解题报告 题目描述 Suppose you have a random list of people standing in a queue Each
  • 算法—反转链表

    题目 实现单链表的逆转函数 输入一个链表 反转链表后 返回翻转之后的链表 分析 利用三个指针 head node nodeNext node指向当前结点 head指向当前结点的前一个结点 nodeNext指向当前结点的后一个结点 先将hea
  • 浏览器动态显示服务器日志,基于 websocket 实现远程实时日志 在浏览器中查看设备的运行日志...

    本文介绍一个基于websocket实现的远程实时日志系统 可以通过浏览器查看远程移动设备的实时运行日志 系统由三个部分组成 1 服务器 与移动设备和浏览器建立websocket连接 将移动设备websocket上读取的实时日志转发到对应的浏