最近公司有个项目要求远程虚拟机,最后选择使用了Guacamole进行实现,网上搜了一下感觉资料不是很多,对guacamole-common.js里的API介绍也没查到(捂脸),可能是我的问题。网上的教程都是教后端怎么搭建远程桌面系统。对web端使用的资料很少,可能大佬们觉着不需要(╥╯^╰╥)。下面我把在项目中用到的一下方法,总结一下,万一有人也需要呐。哈哈
下载guacamole-common或者直接引入guacamole-common.js文件;在项目中的引用初始化Guacamoleconst initGuacamole = () => {//XXXXX 后端提供的连接地址 guacamole.tunnel = new Guacamole.WebSocketTunnel('XXXXX') if (guacamole.client) { guacamole.display.scale(0) uninstallKeyboard() } guacamole.client = new Guacamole.Client(guacamole.tunnel) guacamole.tunnel.onerror = error => { // console.error(`Tunnel failed ${JSON.stringify(error)}`) // 这里是断开重连的机智 当code值是下面这些时会进行重连5次, if (error.code === 512 || error.code === 514 || error.code === 515 || error.code === 769 || error.code === 776) { if (guacamole.curRetryCount === 0) { guacamole.retryTask = setInterval(() => { if (guacamole.curRetryCount < guacamole.maxRetryCount) { uninstallKeyboard() nextTick(() => { initGuacamole('error') }) guacamole.curRetryCount++ } else if (guacamole.curRetryCount >= guacamole.maxRetryCount) { guacamole.loading = false guacamole.loadingText = '' uninstallKeyboard() clearInterval(guacamole.retryTask) guacamole.retryTask = null } }, 3000) } }else { if (!guacamole.retryTask) { guacamole.loading = false guacamole.loadingText = '' } } } guacamole.tunnel.onstatechange = state => { // console.log('tunnel.onstatechange',Guacamole.Tunnel.State,state); switch (state) { case Guacamole.Tunnel.State.CONNECTING: guacamole.connectionState = TUNNEL_STATES.CONNECTING break; // Connection is established / no longer unstable case Guacamole.Tunnel.State.OPEN: guacamole.connectionState = TUNNEL_STATES.CONNECTED break; // Connection is established but misbehaving case Guacamole.Tunnel.State.UNSTABLE: // TODO guacamole.connectionState = '不稳定' // this.$message.error("不稳定") break; // Connection has closed case Guacamole.Tunnel.State.CLOSED: guacamole.connectionState = TUNNEL_STATES.DISCONNECTED break; } } guacamole.client.onstatechange = clientState => { // console.log('clientState', clientState, TUNNEL_STATES); switch (clientState) { case 0: guacamole.connectionState = TUNNEL_STATES.IDLE break case 1: // connecting ignored for some reason? break case 2: guacamole.connectionState = TUNNEL_STATES.WAITING break case 3: guacamole.connectionState = TUNNEL_STATES.CONNECTED // 取消加载 guacamole.loading = false guacamole.loadingText = '' // 重连清空定时器和次数 guacamole.curRetryCount = 0 clearInterval(guacamole.retryTask) guacamole.retryTask = null//监听视图变化,并发送最新的宽高给Guacamole进行重新绘制 window.addEventListener('resize', resize) case 4: case 5: // disconnected, disconnecting break } } guacamole.client.onerror = error => { guacamole.client.disconnect() console.error(`Client error ${JSON.stringify(error)}`) } guacamole.display = guacamole.client.getDisplay() display.value.appendChild(guacamole.display.getElement()) display.value.addEventListener('contextmenu', e => { e.stopPropagation(); if (e.preventDefault) { e.preventDefault(); } e.returnValue = false; }) // 后端需要的参数 let param = { userName: '', password: '', type: 'VNC/RDP' } guacamole.client.connect(param); // 视图大小发生变化时触发(rdp和VNC同时连接时rdp可以动态设置分辨率,但是VNC不可以,所以前端可以通过onresize拿到rdp连接后,VNC通道拿到流的宽高进行等比缩放) guacamole.display.onresize = function(width, height) { const resizeScale = Math.min( viewport.value.clientWidth / Math.max(guacamole.display.getWidth(), 1), viewport.value.clientHeight / Math.max(guacamole.display.getHeight(), 1) ) guacamole.display.scale(resizeScale) } window.onunload = () => guacamole.client.disconnect(); // 解决连接多个虚拟机时有的虚拟机不能查看z-index: -1 guacamole.client.getDisplay().scale(1) display.value.onclick = () => { display.value.focus() } display.value.onfocus = () => { display.value.className = 'focus' } display.value.onblur = () => { display.value.className = '' } guacamole.keyboard = new Guacamole.Keyboard(display.value); installKeyboard() // Mouse 参数最好使用guacamole.client.getDisplay().getElement(),之前写的是父级元素,会导致出现两个鼠标的问题 guacamole.mouse = new Guacamole.Mouse(guacamole.client.getDisplay().getElement()); // 鼠标离开显示器时隐藏软件光标 guacamole.mouse.onmouseout = () => { if (!guacamole.display) return; guacamole.display.showCursor(false); } guacamole.mouse.onmousedown = guacamole.mouse.onmouseup = guacamole.mouse.onmousemove = handleMouseState setTimeout(() => { resize() display.value.focus() }, 1000);//居中显示(当返回的流宽高小于屏幕宽高时) guacamole.client.getDisplay().getElement().style.setProperty('margin', 'auto') }
缩放方法 const resize = () => { const elm = viewport.value; if (!elm || !elm.offsetWidth) { return } // let pixelDensity = window.devicePixelRatio || 1 (pixelDensity 获取当前电脑的分辨率百分比) let pixelDensity = 1 const width = elm.clientWidth * pixelDensity const height = elm.clientHeight * pixelDensity if (guacamole.display.getWidth() !== width || guacamole.display.getHeight() !== height) { guacamole.client.sendSize(width, height) } // setting timeout so display has time to get the correct size setTimeout(() => { // 计算缩放比例 const scale = Math.min( elm.clientWidth / Math.max(guacamole.display.getWidth(), 1), elm.clientHeight / Math.max(guacamole.display.getHeight(), 1) ) // guacamole.display.scale(1) if (props.websocketUrl === '/webSocket') { guacamole.display.scale(1) }else { guacamole.display.scale(scale) } }, 1000) }
html <div ref="display" class="display" tabindex="0"/>
以上就是我在项目中的使用情况
1、连接Tunnel
guacamole.tunnel = new Guacamole.WebSocketTunnel(props.websocketUrl)
2、把流放到视图中
guacamole.client = new Guacamole.Client(guacamole.tunnel)
3、监听tunnel和client 的值进行状态判断
4、把流添加到视图中
guacamole.client.getDisplay().appendChild(guacamole.display.getElement())
5、给后端传参
let param = {userName: ’ ',password: ’ '}
guacamole.client.connect(param);
6、断开虚拟机连接
guacamole.client.disconnect();
7、键盘事件
// display.value div元素
new Guacamole.Keyboard(display.value)
8、鼠标事件
new Guacamole.Mouse(guacamole.client.getDisplay().getElement());