背景:

  2018年12月5日,微软宣布放弃浏览器Edge,转而推出一款新的浏览器,而这款新浏览器将会采用谷歌的Chromium 内核。。。

  好了,反正已经无力吐槽,微软烂尾的项目也不是一个两个了,之前放弃IE转Edge,现在有把Edge丢掉拥抱Chromium,

彻底结束了IE家族;居于这个原因,之前项目中有使用过一些跟IE绑定的ActiveX插件,也不得不做出改变了。

现状:

  网上搜索“web访问远程桌面”,没有找到合适的解决方案,基本上有以下几种:

  1、在Google浏览器上使用“IE TAB”插件,这个插件是Google浏览器应用商店上的插件;试过之后确实可以运行IE上的ActiveX插件;

缺点:

  1)最重要的,需要翻墙,由于国内不能直接方法Google应用商店,所以要安装这个插件必须翻墙出去;

这在大部分情况下就不适合,因为很多人不会;

  2)操作冗余,每次打开插件都需要在Google菜单栏点击一个小按钮,如何页面才会跟着转换,这会很奇怪;

  3)兼容性糟糕,如果js在不同的ie浏览器下调试通过的,那在Google上也需要右键“IE TAB”按钮,找到属性栏,

选择对应的IE版本;这体验就非常不好,因为很多用户根本不会使用浏览器插件,更不要说去设置参数

  4)还是翻墙,这个插件居然还引用了一个ajax的js文件,这个文件刚好是在Google域名下的;这就会导致设置插件

属性的时候不翻墙根本打不开;

  5)放弃。。

  2、使用IIS的远程桌面,基于“Remote desktop web connection”技术,详细可浏览这篇文章“远程桌面web连接”

缺点:

  1)页面和操作步骤不可定制,只能使用IIS封装好的页面

  2)每次离开需要重新输入账户,很不方便

  3)需要服务器该功能支持,以及各种配置操作

  4)优先级低下,放弃。。。

  3、微软最新的退出的“Remote Desktop”技术,详细可浏览这篇文章“微软宣推Remote Desktop远程桌面网页版”

以及官方介绍:“为用户设置远程桌面 Web 客户端”

缺点:

  1)需要windows service 2016火灾windows service 2019,并且安装RD网关;

  2)配置太繁琐,非人类操作,看官方的安装配置介绍的非常不清晰,各种配置、各种工具支持、

各种命令,要是决定使用这个方案,怕是会忍不住砸了电脑;

  3)放弃放弃。。,狗命要紧。

  4、guacamole,说是html5的远程web应用,支持rdp,vnc,ssh,在windows和Linux下都能运行,

需要很多配置、依赖等,支持docker下部署

缺点:

  1)门槛较高,非Linux原生码农使用起来可能会非常烦躁,而且安装包还不小,几百M操作复杂

  2)而且好像只支持Java的?不是很确定。

  3)本人没有使用过这种方式,不过看起来这个在web端访问远程桌面还是做的比较好的,国内外知名度很高

  4).net下没有找到有用的资料,高手可以尝试一番

  5)放弃,,

  5、remote spark的spark gateway,这个我一点都不了解,网上搜到的,具体查看这篇文章“HTML5远程工具”

缺点:

  1)资料太少,太小众,出问题都不知道怎么改

  2)放弃

  6、那就是使用ActiveX浏览器插件了,但是现在以及不合适了,ActiveX恐怕要彻底退出历史舞台;

功能需求:

  基于以上的各种调研,始终没有找到一个合适的、容易上手、部署简单、使用方便的web端访问服务器的方案;

因为我始终无法理解为什么把这种插件安装以及使用搞的那么复杂,完全没有必要啊?

  首先我们列举一下我们想要的功能:

  1)屏幕共享,在web端能实时查看服务器的屏幕信息

  2)鼠标事件,需要支持单击、双击、右键、光标移动,支持这4个功能足够了

  3)键盘事件,需要支持键盘按下、松开、组合键(比如切换输入法)

  4)拖动窗体、拖动选择文本  

  如果支持以上4点那就足够了,基本满足用户99%交互操作。

自研:

  现在需求有了,那干脆也不继续找现有别人的产品了,既然没有,那就自己搞一个出来。

  1、工具基本架构

  

  2、实现原理

  1)web端使用html5的websocket与web后台通信,web后台与服务端使用TCP协议通信

  2)定制传输协议,发送命令与接收数据包需要一个传输协议保证数据完整,组包、检验包

  3)web端接收到后台推送过来的频幕位图流数据,直接在img或canvas元素上渲染出来;

  4)服务端接收到前端命令,转成各种方式实现相应操作

  3、前端功能列举

  1)websocket,接收到数据包赋值给img

    var wsImpl = window.WebSocket || window.MozWebSocket;
var ws = new wsImpl('ws://localhost:7184/'); ws.onmessage = function (evt) {
var image = document.getElementById("desktop");
image.src = evt.data;
...
}

  2)键盘事件

    $(window).keypress(function (event) {
ws.send("{'action':3,'c':'" + event.which + "'}");
}); $(window).keydown(function (event) {
if (event.key == "Shift") {
if (event.ctrlKey) {
ws.send("{'action':3,'c':'" + 201 + "'}");
}
else {
ws.send("{'action':3,'c':'" + 200 + "'}");
}
}
});

  3)鼠标事件

        $(desktop).mousemove(function (e) {
sendXY(e.offsetX, e.offsetY);
}); $(desktop).mousedown(function (event) {
sendMousedown(event);
}); $(desktop).mouseup(function (event) {
sendMouseup(event);
}); $(desktop).click(function (e) {
sendClick();
}); $(desktop).dblclick(function (e) {
sendDoubleClick();
}); ...

  4)前端HTML

 <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<script src="../Scripts/jquery-1.10.2.js"></script>
<script src="../Scripts/jquery.webDesktop.min.js"></script>
<script type="text/javascript">
$(function () {
$(".desktop_login").click(function (e) {
$(".webDesktop").desktop({ 'ip': 'localhost' });
});
});
</script> <style>
body {
padding: 0px;
margin: 0px;
background-color: #000033;
color: white;
font-weight: bold;
} .main {
width: 100%;
height: 100%;
} .webDesktop {
text-align: left;
} .tip {
margin-left: 100px;
margin-top: 100px;
font-size: 22px;
} .user {
clear: both;
min-height: 100px;
} .desktop_name {
margin-left: 100px;
margin-top: 36px;
font-size: 18px;
float: left;
} .desktop_login {
padding: 3px 20px 3px 20px;
margin-left: 30px;
margin-top: 30px;
font-size: 20px;
float: left;
border: 3px solid #ffffff;
cursor: pointer;
} .time {
position: fixed;
left: 100px;
bottom: 250px;
width: 100%;
height: 50px;
z-index: 9999;
font-size: 120px;
font-weight: normal;
} .day {
position: fixed;
left: 100px;
bottom: 80px;
width: 100%;
height: 50px;
z-index: 9998;
font-size: 55px;
font-weight: normal;
}
</style>
</head> <body onselectstart="return false">
<div class="webDesktop">
<div class="tip">按下 "登录服务器" 进入。</div>
<div class="user">
<div class="desktop_name">Administrator 已登录。</div>
<div class="desktop_login">登录服务器</div>
</div>
<div class="time">15:19</div>
<div class="day">10月21日, 星期一</div>
</div>
</body>
</html>

  5、后台界面

  1)处理前端命令

 Task.Run(() =>
{
while (true)
{
try
{
if (isBreak) break; if (clientSocket == null || !clientSocket.Connected)
{
break;
} var data = new byte[];
var length = clientSocket.Receive(data);
if (length > )
{
log.Clear(); var cmd = StringHelper.BytesToStruct<SDesktop>(data, typeof(SDesktop));
if (cmd.action != 0x02)
{
lastActionTime = DateTime.Now;
}
log.AppendLine(clientSocket.RemoteEndPoint.ToString());
log.AppendFormat(">> action:{0},x:{1},y:{2},c:{3},k:{4}", cmd.action, cmd.x, cmd.y, cmd.c, cmd.k).AppendLine(); if (cmd.action == 0x03)
{
if (cmd.k == || cmd.k == )
{
SwitchToLanguageMode(ref log);
}
else
{
SendKeys.SendWait(cmd.c.ToString());
log.AppendFormat(">> SendKeys:({0})", cmd.c).AppendLine();
}
}
else
{
Desktop.Cmd(cmd, log);
} AppendLog(log.ToString());
}
}
catch (Exception ex)
{
AppendLog(clientSocket.RemoteEndPoint.ToString() + " >> Receive出错,ex:" + ex.Message);
}
}
});

  2)处理屏幕数据

 Task.Run(() =>
{
while (true)
{
try
{
if (isBreak) break; if (clientSocket == null || !clientSocket.Connected)
{
break;
} var screen = GetScreen();
for (var i = ; i < screen.Length; i = i + packsize)
{
lock (objSocketSend)
{
if (screen.Length - i < packsize)
{
clientSocket.Send(screen, i, screen.Length - i, SocketFlags.None);
}
else
{
clientSocket.Send(screen, i, packsize, SocketFlags.None);
}
}
}
}
catch (Exception ex)
{
AppendLog(clientSocket.RemoteEndPoint.ToString() + " >> Send出错,ex:" + ex.Message);
} var tm = (DateTime.Now - lastActionTime).TotalMilliseconds;
if (tm > )
{
var speed = cbSpeed.Text.ToInt();
Thread.Sleep(speed); var content = string.Format("{0} >> Screen.{1},tm:{2}", clientSocket.RemoteEndPoint.ToString(), speed, tm);
AppendLog(content);
}
else
{
Thread.Sleep();
AppendLog(clientSocket.RemoteEndPoint.ToString() + " >> Screen,tm:" + tm);
}
} connCount--;
});

最终效果:

1)鼠标事件

2)键盘事件

结语:

1)前端和后台服务端搭配使用,除此之外无需任何配置,只需要打开后台服务端即可,前端设置后IP地址就可以直接访问

2)可以控制图片压缩比例,默认30%,1600 *1280的分辨率出来的图片大小为130K左右,这个分辨率对对象清晰度影响不大

3)可以控制帧流速,无操作动作时默认0.7秒发送一次屏幕信息,有交互时,不等待,连续发送

  

在web端使用html5访问远程桌面的更多相关文章

  1. realvnc viewer 5.3.2无需输入用户名和密码访问远程桌面

    我从https://www.realvnc.com/download/viewer/下载了realvnc viewer用于访问远程的Linux桌面,这个版本不需要安装,直接运行就可以了.但在访问远程桌 ...

  2. 使用HslCommunication实现PLC数据的远程客户端监视,以及web端实时监视,远程操作设备示例

    前言 本文主要是演示一个例子,服务器后台程序从PLC采集数据,并推送给在线客户端显示,以及推送给web端进行实时的显示,还支持远程操作,支持安卓端的同步监视和远程操作,关于HslCommunicati ...

  3. 特定IP访问远程桌面

    1. 新建IP安全策略 (远程端口没有修改情况下的设置) WIN+R打开运行对话框,输入“gpedit.msc”进入组策略编辑器. 依次打开:本地计算机策略--计算机配置--Windows设置--安全 ...

  4. Windows通过远程桌面访问Ubuntu

    关于Windows通过远程桌面访问Ubuntu 问题及目标 Window环境通过远程桌面访问Ubuntu Ubuntu机器端   1.  安装所需软件包   sudoapt-get install x ...

  5. 远程桌面web连接

      我们可以利用web浏览器搭配远程桌面技术来连接远程计算机,这个功能被称为远程桌面web连接(Remote desktop web connection),要享有此功能,请先在网络上一台window ...

  6. 远程桌面(RDP)上的渗透测试技巧和防御

      0x00 前言 在本文中,我们将讨论四种情况下的远程桌面渗透测试技巧方法.通过这种攻击方式,我们试图获取攻击者如何在不同情况下攻击目标系统,以及管理员在激活RDP服务时来抵御攻击时应采取哪些主要的 ...

  7. 修改Win7远程桌面端口

    Win7与XP不同,在开启远程桌面修改端口后是无法直接访问的,原因是还未修改远程桌面在防火墙入站规则中的端口号. 修改远程桌面端口: [HKEY_LOCAL_MACHINE/SYSTEM/Curren ...

  8. Xrdp - 通过Windows的RDP连接Linux远程桌面(Ubuntu/CentOS/Redhat 7)(转载)

            您多久访问一次Linux桌面? 您使用什么工具来访问远程桌面? Xrdp是一个开源工具,允许用户通过Windows RDP访问Linux远程桌面. 除了Windows RDP之外,xr ...

  9. Xrdp - 通过Windows的RDP连接Linux远程桌面(Ubuntu/CentOS/Redhat 7)

    Xrdp - 通过Windows的RDP连接Linux远程桌面(Ubuntu/CentOS/Redhat 7) 您多久访问一次Linux桌面? 您使用什么工具来访问远程桌面? Xrdp是一个开源工具, ...

随机推荐

  1. Stanford公开课《编译原理》学习笔记(1~4课)

    目录 一. 编译的基本流程 二. Lexical Analysis(词法分析阶段) 2.1 Lexical Specification(分词原则) 2.2 Finite Automata (典型分词算 ...

  2. Python—字符串和常用数据结构

    目录 1. 字符串 2. 列表 2.1 列表的增删改查 2.2 列表的切片和排序 2.3 生成式语法 3. 元组 4.集合 5. 字典 5.1 字典的增删改查 5.2 字典的常见操作 序言:这一章我们 ...

  3. Fragment的创建与通信

    由于这里涉及到接口回调的问题,所以先来看一看什么是接口回调: 这就好比老板和员工的微妙关系,老板需要员工去工作,员工挣钱了以后还要告诉老板自己挣了多少钱,然后由老板来处理这些钱. 首先创建一个接口: ...

  4. 52 (OC)* 苹果手机各种尺寸详细表以及iPhoneX、iPhoneXS、iPhoneXR、iPhoneXSMax屏幕适配

     iPhone设备 物理分辨率是硬件所支持的,逻辑分辨率是软件可以达到的. 代数 设备 操作系统 逻辑分辨率(point) 物理分辨率(pixel) 屏幕尺寸(对角线长度) 缩放因子   iPhone ...

  5. 【linux】【ELK】利用elasticproxy对elasticsearch进行二次排序

    做过elk的人应该了解kibana排序至支持到秒级别,但同一秒内出现多个日志的时候那么kibana展示的日志就会混轮,加上该代理可以解决该问题 # 拉取elasticproxy镜像 [root@loc ...

  6. docker部署jenkins

    步骤一: 查找jenkins镜像(也可以直接去jenkins官网找镜像docker pull jenkins/jenkins)(官方版本文档https://hub.docker.com/_/jenki ...

  7. WPF中资源的引用方法

    一.引用同一个程序中的资源 1.使用相对Uri来引用资源,如下所示 img.Source=new BitmapImage(new Uri(@"d"\iamges\Backgroun ...

  8. 基于Docker搭建大数据集群(二)基础组件配置

    主要内容 jdk环境搭建 scala环境搭建 zookeeper部署 mysql部署 前提 docker容器之间能免密钥登录 yum源更换为阿里源 安装包 微云分享 | tar包目录下 JDK 1.8 ...

  9. leveldb 源码--总体架构分析

    一 本文目的 对leveldb的总体设计框架分析(关于leveldb基本原理,此文不做阐述,读者可以自行检索文章阅读即可),对leveldb中底层数据存储数据格式,内存数据模型,compact,版本管 ...

  10. java后端研发经典面试题总结

    垃圾回收算法 1.标记-清除算法 标记-清除算法是最基本的算法,和他的名字一样,分为两个步骤,一个步骤是标记需要回收的对象.在标记完成后统一回收被标记的对象.这个算法两个问题.一个是效率问题,标记和清 ...