局域网内浏览器实现远程控制windows设备的解决方案

news2024/9/30 7:21:36

使用VNC技术实现的局域网内windows远程桌面

  • 项目用途
  • 服务端的技术实现
    • 1. 安装UItraVNC软件,只部署server端即可
    • 2. 为UItraVNC设定自定义密码
    • Python 环境的安装
    • Websockify 的使用
    • 使用nssm工具将run.bat注册为系统服务,并开机自启
    • 结束,这里就是服务端全部程序
  • 客户端的技术实现
    • 列表页面
    • 设备远程桌面
  • 走过的坑
  • 有需求,请留言联系

项目用途

该项目仅限于局域网内部使用的windows系统远程桌面显示和远程控制。主要用于展厅内的设备控制,包括小屏控制大屏,一屏多显,多屏同步功能。
该系统只关注远程界面和远程控制,其他部分均有信息发布系统实现。
控制主要分为服务端和客户端两部分。受控设备为服务端,控制设备为客户端。
下方连接均为官方网站,切勿在第三方平台下载安装来源不明的软件。本文作者遵循信息安全管理条例!!!

服务端的技术实现

1. 安装UItraVNC软件,只部署server端即可

官网下载地址:https://uvnc.com/downloads/ultravnc.html
官方介绍:
UltraVNC服务器和查看器是一个功能强大、易于使用的免费软件,可以将一台计算机(服务器)的屏幕显示在另一台计算机(查看器)的屏幕上。
一个计算机(服务器)的屏幕显示在另一个计算机(浏览器)的屏幕上。该程序允许浏览者使用他们的鼠标
和键盘来远程控制服务器计算机。
UltraVNC是一个为Windows PC量身定做的VNC应用程序,具有其他VNC产品所没有的一些功能。

使用:
如果想本地测试,可以将viewer和server都安装上进行连接调试,其他受控设备只安装server端即可。
作用:
遵循VNC协议架构,将设备的远程帧和控制指令,通过TCP默认5900端口进行双向通信。

2. 为UItraVNC设定自定义密码

在这里插入图片描述
在这里插入图片描述

Python 环境的安装

官网下载地址:https://www.python.org/downloads/
这里不讲解,主要是下个部分用
这里一定要试运行一下python指令,否则权限问题无法调用

Websockify 的使用

官网下载地址: https://github.com/novnc/websockify
官方介绍:
websockify的前身是wsproxy,是noVNC项目的一部分。
在最基本的层面上,websockify只是将WebSockets流量翻译成正常的套接字流量。Websockify接受WebSockets握手,对其进行解析,然后开始在客户端和目标端之间双向转发流量。

这里要说一下项目需求,目的是要多端实现对windows设备进行远程控制,能多端的也就当属Javascript了吧。所以用到了No-vnc的开源项目,而No-vnc的通信实现就是基于websockify的。
使用:
将websockify目录copy到系统目录中,如:C:\Users\Administrator\AppData\Local\websockify
先创建一个win环境下的运行脚本run.bat并存放至C:\Users\Administrator\AppData\Local\websockify\run.bat。
内容如下,上述参数如有变更,请自行调整

C:
cd "\Users\Administrator\AppData\Local\websockify"
"C:\Users\Administrator\AppData\Local\Programs\Python\Python310\python.exe" -m websockify 5901 127.0.0.1:5900

将受控设备的TCP:5900端口代理到WS:5901端口
为了操作方便,写了一个copy指定目录的脚本xcopyOfWebsockify.bat
如果目录已经存放或不修改存放目录,可直接执行xcopyOfWebsockify.bat

echo off
chcp 65001
set ws_path="%~dp0websockify"
set targetFolder="C:\Users\Administrator\AppData\Local\websockify"
echo 检查当前目录websockify是否存在
if not exist %ws_path% (
	echo %ws_path% 不存在,请确认
	pause 
	goto exitCode
 )
) 
echo %ws_path% 存在,即将检查文件并复制
echo 复制websockify
xcopy /S /Y %ws_path% %targetFolder%
echo 复制websockify至windows系统用户目录完成


pause

请添加图片描述

使用nssm工具将run.bat注册为系统服务,并开机自启

官方网站:https://nssm.cc/download
具体如何使用可以自行查阅,这里提供了构建脚本,通过管理员身份运行即可。nssm.bat

echo off
chcp 65001
echo 即将开始采用nssm安装应用程序为windows服务,请确认以系统管理员身份运行
set servicename=HYwebsockify
REM %~dp0 为BAT脚本取当前系统目录命令,API_HOST.EXE为需要包装为服务的应用程序
set app_path="\Users\Administrator\AppData\Local\websockify\run.bat"
set nssm_path="%~dp0nssm.exe"
REM 将NSSM复制至系统盘目录,或者 添加 windows 环境变量亦可达到目的
set targetFolder="C:\windows\System32\nssm.exe"
REM 检查NSSM.exe文件是否存在
echo 检查当前目录nssm.exe文件是否存在
if not exist %nssm_path% (
	echo %nssm_path% 不存在,请确认
	pause 
	goto exitCode
 )
) 
echo %nssm_path% 存在,即将检查文件并复制
REM 复制nssm
if not exist %targetFolder% (
	copy /y %nssm_path% %targetFolder%
	echo 复制nssm至windows系统目录完成
)
echo 即将创建服务 %servicename%
echo  ****************************************

REM 判断service 是否存在,若存在,先停止,至删除
echo 检查服务是否存在,存在则停止服务后删除,再安装
sc query|find /i "%servicename%" >nul 2>nul
if not errorlevel 1 (
	echo 服务已存在,停止运行服务
	echo stop %servicename%
	REM NSSM停止服务命令:nssm stop <ServiceName>
	nssm stop %servicename%	
	echo 开始移除服务 %servicename%
	echo remove service %servicename%
	REM NSSM删除服务命令:nssm remove <ServiceName> confirm
	REM 移除命令最后的 confirm 即表示无限弹窗确认,直接移除。
	nssm remove %servicename% confirm
	echo 移除服务完成
)

echo *********************************
echo 开始创建服务 %servicename%
REM NSSM命令:nssm install <服务名> <服务需要执行的程序>
nssm install %servicename% %app_path%
echo 开始设置服务信息
echo set service property
echo 设置服务显示名称
REM nssm set <ServiceName> DisplayName <ServiceName>
nssm set %servicename% 展厅商显远程控制服务 %servicename%
echo 设置服务描述
REM nssm set <ServiceName> Description <ServiceName>
nssm set %servicename% 主要用于win设备的远程监控及控制操作 
echo 设置服务启动方式为:自动
nssm set %servicename% Start SERVICE_AUTO_START
echo *********************************
echo 启动服务 %servicename%
echo start service %servicename%
nssm start %servicename%
echo 服务创建并启动完成

:exitCode
pause

结束,这里就是服务端全部程序

在这里插入图片描述

客户端的技术实现

列表页面

<template>
	<view class="content">		    
		<view class="devices tn-flex tn-flex-direction-row tn-flex-wrap">
			<block v-for="(item, index) in devices" :key="index">
				<view class="device">
					<view class="device-img" @click="openScreen(item)">
						<text class="tn-text-xl-xxl tn-icon-computer" style="font-size: 100px;"></text>
					</view>
					<view class="device-text">
						<text>{{ item.title }}</text>
						<text @click="showInfo(item)" class="tn-float-right tn-text-xxl tn-icon-tips"></text>
					</view>
				</view>
			</block>
		</view>
<!-- 		<tn-fab
			:btnList="btnList"
		  :right="50"
		  :bottom="100"
		  :iconSize="64"
		  backgroundColor="#01BEFF"
		  fontColor="#FFFFFF"
		  icon="open"
		  animationType="up"
		  :showMask="true"
		  @click="clickFabItem"
		>
		</tn-fab> -->
		<!-- 添加设备 -->
		<tn-modal v-model="showModal" :custom="true" width="50%">
			<view class="custom-modal-title tn-text-center">
				<text v-if="isInfo"  class=" tn-text-bold tn-text-xl">添加设备</text>
				<text v-if="!isInfo" class=" tn-text-bold tn-text-xl">查看设备信息</text>
			</view>
			<view class="custom-modal-content">
				<tn-form :model="form" :labelWidth="200" labelAlign="right">
					<tn-form-item label="设备名称:" prop="title">
						<tn-input v-model="form.title" :disabled="isInfo" />
					</tn-form-item>
					<tn-form-item label="设备IP:" prop="ip">
						<tn-input v-model="form.ip" :disabled="isInfo"/>
					</tn-form-item>
					<tn-form-item label="通信端口:" prop="port">
						<tn-input v-model="form.port" placeholder="默认为5901,不修改可留空!!!" :disabled="isInfo" />
					</tn-form-item>
					<tn-form-item label="设备密钥:" prop="pwd">
						<tn-input typy="password" v-model="form.pwd" type="password" :disabled="isInfo"/>
					</tn-form-item>
				</tn-form>
				<view class="tn-padding" v-if="!isInfo">
					<tn-button backgroundColor="#01BEFF" fontColor="#FFFFFF" width="100%" @click="submit">提交</tn-button>
				</view>
			</view>
		</tn-modal>
		<view class="tn-padding-bottom-lg"></view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				title: 'HY商显设备控制系统',
				isInfo:false,
				showModal:false,
				form:{
					ip:'',
					port:'5901',
					pwd:'',
					title:'',
				},
				devices:devices,
				btnList:[
					{
					  icon: 'add',
					  text: '添加设备',
					  bgColor: '#E72F00'
					},
				],
				form:{}
			}
		},
		onLoad() {
			
		},
		methods: {
			click(event) {
        this[event.methods] && this[event.methods](event)
      },
			// 点击悬浮按钮的内容
			clickFabItem(e) {
			  // this.$tn.message.toast(`点击了第 ${e.index} 个选项`)
				if(e.index==0){
					this.showModal = true;
				}
			},
			openScreen(obj){
				uni.navigateTo({
					url:'/pages/vnc/index?ip='+obj.ip+'&port='+obj.port+'&pwd='+obj.pwd+'&title='+obj.title,
				})
			},
			showInfo(obj){
				this.form = obj
				this.showModal = true;
				this.isInfo = true;
			}
		}
	}
</script>

<style>
	.content {
		padding-top:44px;
		width:100vw;
		height:100vh;
		background-size:100% 100%;
		background-repeat: no-repeat;
		background-image: url('@/static/images/bg.jpg');
	}
	.devices{
		justify-content: space-evenly;
	}
	.device{
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		background-color: aliceblue;
		width: 30%;
		height:300rpx;
		margin-bottom:60rpx;
		position: relative;
	}
	.device-img{
		width: 100%;
		text-align: center;
	}
	.device-text {
		font-size: 30rpx;
		color:#ffffff;
		background-color: #00000090;
		position: absolute;
		left: 0;
		bottom: 0;
		width: 100%;
		padding: 5px;
	}
</style>

设备远程桌面

<template>
  <div class="full" :style="'width:'+windowWidth+'px;height:'+windowHeight+'px;'">
		<navigation-bar :title="title"/>
    <div id="screen" class="full" :style="'width:'+windowWidth+'px;height:'+windowHeight+'px;'"></div>
  </div>
</template>

<script>
import RFB from '@novnc/novnc/core/rfb';

export default {
  name: 'Novnc',
  data() {
    return {
      url: '',
      rfb: null,
			windowWidth:1920,
			windowHeight:1080,
			title:'远程桌面',
			ip:'',
			port:'',
			pwd:'',
			
    }
  },
	onLoad(option) {
		uni.getSystemInfo({
			success: (res) => {
				this.windowWidth = res.windowWidth;
				this.windowHeight = res.windowHeight;
			},
		})
		this.title = '【'+option.title+'】的远程桌面';
		this.ip = option.ip;
		this.port = option.port;
		this.pwd = option.pwd;
	},
  mounted() {
    this.url = this.getUrl(this.$route.params.host);
    this.connectVnc();
  },
  methods: {
    getUrl(host) {
      let protocol = '';
      if (window.location.protocol === 'https:') {
        protocol = 'wss://';
      } else {
        protocol = 'ws://';
      }
      // 加window.location.host可以走vue.config.js的代理,ws://localhost:8081/vnc/192.168.18.57:5900
      // const wsUrl = `${protocol}${window.location.host}/vnc/${host}`;
      const wsUrl = `${protocol}${this.ip}:${this.port}/websockify`;
      console.log(wsUrl);
      return wsUrl;
    },
    // vnc连接断开的回调函数
    disconnectedFromServer(msg) {
      uni.showToast({
      	title:'连接断开',
      	icon:'none'
      })
      // clean是boolean指示终止是否干净。在发生意外终止或错误时 clean将设置为 false。
      if(msg.detail.clean){
        // 根据 断开信息的msg.detail.clean 来判断是否可以重新连接
				this.rfb = null;
				// this.connectVnc();
      } else {
        // 这里做不可重新连接的一些操作
        this.$tn.message.toast('连接不可用(可能需要密码)')
      }      
    },
    // 连接成功的回调函数
    connectedToServer() {
			uni.hideLoading();
      uni.showToast({
      	title:'连接成功',
      	icon:'success'
      })
    },
    //连接vnc的函数
    connectVnc() {
			uni.showLoading({
				title:'正在连接服务'
			})
      const PASSWORD = '';
      let rfb = new RFB(document.getElementById('screen'), this.url, {
        // 向vnc 传递的一些参数,比如说虚拟机的开机密码等
        credentials: {password: this.pwd}
      });
      rfb.addEventListener('connect', this.connectedToServer);
      rfb.addEventListener('disconnect', this.disconnectedFromServer);
      // scaleViewport指示是否应在本地扩展远程会话以使其适合其容器。禁用时,如果远程会话小于其容器,则它将居中,或者根据clipViewport它是否更大来处理。默认情况下禁用。
      rfb.scaleViewport = true;
      // 是一个boolean指示是否每当容器改变尺寸应被发送到调整远程会话的请求。默认情况下禁用
      rfb.resizeSession = true;
      this.rfb = rfb;
    }
  },
  beforeDestroy() {
    this.rfb && this.rfb.disconnect();
  }
}
</script>
<style>
</style>

走过的坑

1.起初使用Python的OpenAI和Numpy做技术支撑,通过对屏幕截图,压缩,差异化比较,进行图像传输。前端采用vue-js-canvas做图像还原展示操作,奈何算法技术太差,图像传输量依然太大,图像显示会有2秒的延迟展示,实时性差。
2.第二个方案采用的rtmp直播流技术,搭建SRS全称为simple-rtmp-server开源流媒体服务器,将本地图像录屏为媒体流文件,上传到服务器进行分发同步,流畅性非常的好,但是对于屏幕实时远程控制来说,不同步让人无法接受。查看的官方文档,也确实验证了延时在0.8s-3s之间。这个方案可以用在别的项目了。
3.第三个方案尝试了NO-VNC的多端代理机制,后来发现,远程连接一台设备需要一分钟。后台转换思路,将websockify直接安装到被控设备上,实现了远程秒连。

有需求,请留言联系

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370598.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为OD机试用Python实现 -【查找树中的元素 or 查找二叉树节点】(2023-Q1 新题)

华为OD机试题 华为OD机试300题大纲查找树中的元素 or 查找二叉树节点题目描述输入描述输出描述说明示例一输入输出示例二输入输出Python 代码实现代码编写思路华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 O…

啊哈 算法读书笔记 第 2 章 栈、队列、链表

第 2 章 栈、队列、链表 目录 第 2 章 栈、队列、链表 队列&#xff1a; 解密回文——栈 纸牌游戏&#xff1a; 链表 模拟链表 队列&#xff1a; 首先将第 1 个数删除&#xff0c;紧接着将第 2 个数放到这串数的末尾&#xff0c;再将第 3 个数删除并将第 4 个数放到这串…

axios 中如何取消请求_从使用到原理_番茄出品

start 最近频繁遇到一个问题&#xff0c;axios 是如何取消请求的&#xff1f;这篇文章将从新手小白的视角出发&#xff0c;从 axios 取消逻辑的基础使用&#xff0c;到原理分析&#xff0c;带你彻底了解并掌握 axios 中取消请求的“秘密”。编写时间&#xff1a;2023/02/24-23…

Prometheus -- 浅谈Exporter

Prometheus系统 – Exporter原理 为什么我们需要Exporter&#xff1f; 广义上讲所有可以向Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target&#xff0c;如下所示&#xff0c;Prometheus通过轮询的方式定期从这些target中获取样本…

你知道Java架构师学习路线该怎么走吗?你所缺少的是学习方法以及完整规划!

怎么成为一名Java架构师&#xff1f;都需要掌握哪些技术&#xff1f;Java架构师&#xff0c;首先要是一个高级Java攻城狮&#xff0c;熟练使用各种框架&#xff0c;并知道它们实现的原理。jvm虚拟机原理、调优&#xff0c;懂得jvm能让你写出性能更好的代码;池技术&#xff0c;什…

java 面向对象三大特性之多态 万字详解(超详细)

目录 前言 : 一、为什么需要多态 : 1.白璧微瑕 : 2.举栗&#xff08;请甘雨,刻晴,钟离吃饭&#xff09;: 3.代码 : 4.问题 : 二、什么是多态 : 1.定义 : 2.多态的实现步骤&#xff08;重要&#xff09; : 三、多态的使用 : 1.多态中成员方法的使用&#xff08;重要…

一块GPU搞定ChatGPT;ML系统入坑指南;理解GPU底层架构

1. 跑ChatGPT体量模型&#xff0c;从此只需一块GPU 在发展技术&#xff0c;让大模型掌握更多能力的同时&#xff0c;也有人在尝试降低AI所需的算力资源。最近&#xff0c;一种名为FlexGen的技术因为「一块RTX 3090跑ChatGPT体量模型」而获得了人们的关注。 虽然FlexGen加速后的…

Java 修饰符和多态

文章目录一、修饰符1. 权限修饰符2. 状态修饰符2.1 final2.2 static二、多态1. 成员访问特点2. 多态中的转型3. 多态案例一、修饰符 1. 权限修饰符 2. 状态修饰符 2.1 final final 关键字是最终的意思&#xff0c;可以修饰成员方法、成员变量及类。 //1.修饰成员变量 publi…

Git ---- IDEA 集成 Git

Git ---- IDEA 集成 Git1. 配置 Git 忽略文件2. 定位 Git 程序3. 初始化本地库4. 添加到暂存区5. 提交到本地库6. 切换版本7. 创建分支8. 切换分支9. 合并分支10. 解决冲突1. 配置 Git 忽略文件 1. Eclipse 特定文件 2. IDEA 特定文件 3. Maven 工程的 target 目录 问题1…

使用eNSP搭建基础IP网络 和 单交换机与VLAN分布实验(二层+三层)

Hello, 好久不见。上学期因为个人原因一直没有更新&#xff08;主要原因是上学期小小的摆了一下&#xff09;&#xff0c;这个学期我会继续在平台上分享我的学习经验。主要包括网络互联以及攻防的内容&#xff0c;也可能会更新深度学习相关的东西&#xff0c;主要就是看我到底有…

开源启智,筑梦未来!第四届OpenI/O启智开发者大会开幕

2023年2月24日&#xff0c;第四届OpenI/O启智开发者大会在深圳顺利开幕。本次活动由鹏城实验室、新一代人工智能产业技术创新战略联盟&#xff08;AITISA&#xff09;主办&#xff0c;OpenI启智社区、中关村视听产业技术创新联盟&#xff08;AVSA&#xff09;承办&#xff0c;华…

阿里 Java 程序员面试经验分享,附带个人学习笔记、路线大纲

背景经历 当时我工作近5年&#xff0c;明显感觉到了瓶颈期。说句不好听的成了老油条&#xff0c;可以每天舒服的混日子&#xff08;这也有好处&#xff0c;有时间准备面试&#xff09;。这对于个人成长不利&#xff0c;长此以往可能面临大龄失业。所以我觉得需要痛下决心改变一…

Spring Boot系列03--自动配置原理

目录1. 相关注解2. 自动配置原理分析3. 自动配置图示Spring Boot的核心优势&#xff1a;自动装配、约定大于配置。 1. 相关注解 ConfigurationProperties(prefix "前缀名")该注解用于自动配置的绑定&#xff0c;可以将application.properties配置中的值注入到 Bean…

加油站ai系统视频监测 yolov5

加油站ai系统视频监测通过yolov5网络模型深度学习边缘计算技术&#xff0c;加油站ai系统视频监测对现场卸油过程中人员违规离岗、现场灭火器没有按要求正确摆放、以及卸油前需要遵守静电释放15分钟、打电话、明火烟雾情况、抽烟行为进行自动识别。YOLO系列算法是一类典型的one-…

九龙证券|不惧美联储重回鹰派,这个板块强势领涨!游戏才刚刚开始?

美联储开释鹰派信号&#xff0c;商场再度堕入博弈美元反弹的预期之中。 美联储近日发布的2月议息会议纪要显现&#xff0c;上行通胀危险是影响美联储前景的要害因素&#xff0c;在通胀持续回落至2%之前&#xff0c;需求采取限制性方针。叠加欧元区1月份中心通胀升至历史最高纪录…

Spring MVC 源码- HandlerExceptionResolver 组件

HandlerExceptionResolver 组件HandlerExceptionResolver 组件&#xff0c;处理器异常解析器&#xff0c;将处理器&#xff08; handler &#xff09;执行时发生的异常&#xff08;也就是处理请求&#xff0c;执行方法的过程中&#xff09;解析&#xff08;转换&#xff09;成对…

Python学习-----模块5.0(文件管理大师-->os模块)

目录 前言&#xff1a; 1.os.getcwd() 2. os.listdir(path) 3.os.walk(path) 4.os.path.exists(path) 5.os.mkdir(path) 6.os.makedirs(path,exist_okTrue) 7.os.rmdir(path) 8.os.remove(path) 9.os.path.join(p1,p2) 10.os.path.split(path) 11.os.path.isdi…

【python】类的详解

注&#xff1a;最后有面试挑战&#xff0c;看看自己掌握了吗 文章目录PO verses OOPOOO当一个类很复杂的时候&#xff0c;考虑多弄一个类的改造私有类的模块化静态类verses动态类动态类查看模块源代码对象机制的基石 PyObjectPO verses OO PO PO耦合性高&#xff0c;很多过程…

手写Android性能监测工具,支持Fps/流量/内存/启动等

App性能如何量化:如何衡量一个APP性能好坏&#xff1f;直观感受就是&#xff1a;启动快、流畅、不闪退、耗电少等感官指标&#xff0c;反应到技术层面包装下就是&#xff1a;FPS&#xff08;帧率&#xff09;、界面渲染速度、Crash率、网络、CPU使用率、电量损耗速度等&#xf…

Linux命令之awk

awk是一个有强大的文本格式化能力的linux命令&#xff0c;早期是在Unix上实现的&#xff0c;linux后来也可以使用了&#xff0c;我们在Linux上使用的awk是gawk&#xff08;GNU awk的意思&#xff09; 语法 awk [option] 模式{动作} file option表示awk的可选参数&#xff0c;可…