虚拟机vnc端口切换时如何让guacamole切换端口重试连接
2017-09-30 10:34
851 查看
虚拟机vnc默认端口是5901,如果虚拟机不属于正常关闭,文件/tmp/.X11-unix/X1不会被删除,当虚拟机启动的时候不会使用5901端口启动vnc server服务,而是启用5902端口(如果文件/tmp/.X11-unix/X2也存在则使用5903端口,依次类推)。
![](https://img-blog.csdn.net/20170930105037867?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGlzcG93ZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
建立到guacamole代理服务器的tunnel时候需要带上vnc的连接信息,如果guacd连接目标虚拟机出错时不会抛出Exception,所以我们的代码没有办法捕获异常去换端口重试连接。
解决方案是重载GuacamoleHTTPTunnelServlet的doRead方法,通过读取webserver发出connect请求之后的第一个流数据,分析里面是否包含错误信息,如果有则重新创建一个tunnel(要拿到旧tunnel的uuid,所有的tunnel都放到map里面缓存的,key就是uuid),然后注册新的tunnel。
@Override
protected void doRead(HttpServletRequest request, HttpServletResponse response, String tunnelUUID)
throws GuacamoleException {
// Get tunnel, ensure tunnel exists
GuacamoleTunnel tunnel = getTunnel(tunnelUUID);
// Ensure tunnel is open
if (!tunnel.isOpen())
throw new GuacamoleResourceNotFoundException("Tunnel is closed.");
// Obtain exclusive read access
GuacamoleReader reader = tunnel.acquireReader();
try {
// Note that although we are sending text, Webkit browsers will
// buffer 1024 bytes before starting a normal stream if we use
// anything but application/octet-stream.
response.setContentType("application/octet-stream");
response.setHeader("Cache-Control", "no-cache");
// Get writer for response
Writer out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), "UTF-8"));
// Stream data to response, ensuring output stream is closed
try {
// Deregister tunnel and throw error if we reach EOF without
// having ever sent any data
char[] message = reader.read();
if (message == null)
throw new GuacamoleConnectionClosedException("Tunnel reached end of stream.");
// 这里是我添加的代码--begin
// guacd server error
if (new String(message).contains("3.519")) {
rebuildTunnel(request, response, tunnelUUID, tunnel);
}
// 这里是我添加的代码--end
// For all messages, until another stream is ready (we send at
// least one message)
do {
// Get message output bytes
out.write(message, 0, message.length);
// Flush if we expect to wait
if (!reader.available()) {
out.flush();
response.flushBuffer();
}
// No more messages another stream can take over
if (tunnel.hasQueuedReaderThreads())
break;
} while (tunnel.isOpen() && (message = reader.read()) != null);
// Close tunnel immediately upon EOF
if (message == null) {
deregisterTunnel(tunnel);
tunnel.close();
}
// End-of-instructions marker
out.write("0.;");
out.flush();
response.flushBuffer();
}
// Send end-of-stream marker and close tunnel if connection is
// closed
catch (GuacamoleConnectionClosedException e) {
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
// End-of-instructions marker
out.write("0.;");
out.flush();
response.flushBuffer();
}
catch (GuacamoleException e) {
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
throw e;
}
// Always close output stream
finally {
out.close();
}
} catch (IOException e) {
// Log typically frequent I/O error if desired
log.debug("Error writing to servlet output stream", e);
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
} finally {
tunnel.releaseReader();
}
}
建立到guacamole代理服务器的tunnel时候需要带上vnc的连接信息,如果guacd连接目标虚拟机出错时不会抛出Exception,所以我们的代码没有办法捕获异常去换端口重试连接。
解决方案是重载GuacamoleHTTPTunnelServlet的doRead方法,通过读取webserver发出connect请求之后的第一个流数据,分析里面是否包含错误信息,如果有则重新创建一个tunnel(要拿到旧tunnel的uuid,所有的tunnel都放到map里面缓存的,key就是uuid),然后注册新的tunnel。
@Override
protected void doRead(HttpServletRequest request, HttpServletResponse response, String tunnelUUID)
throws GuacamoleException {
// Get tunnel, ensure tunnel exists
GuacamoleTunnel tunnel = getTunnel(tunnelUUID);
// Ensure tunnel is open
if (!tunnel.isOpen())
throw new GuacamoleResourceNotFoundException("Tunnel is closed.");
// Obtain exclusive read access
GuacamoleReader reader = tunnel.acquireReader();
try {
// Note that although we are sending text, Webkit browsers will
// buffer 1024 bytes before starting a normal stream if we use
// anything but application/octet-stream.
response.setContentType("application/octet-stream");
response.setHeader("Cache-Control", "no-cache");
// Get writer for response
Writer out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), "UTF-8"));
// Stream data to response, ensuring output stream is closed
try {
// Deregister tunnel and throw error if we reach EOF without
// having ever sent any data
char[] message = reader.read();
if (message == null)
throw new GuacamoleConnectionClosedException("Tunnel reached end of stream.");
// 这里是我添加的代码--begin
// guacd server error
if (new String(message).contains("3.519")) {
rebuildTunnel(request, response, tunnelUUID, tunnel);
}
// 这里是我添加的代码--end
// For all messages, until another stream is ready (we send at
// least one message)
do {
// Get message output bytes
out.write(message, 0, message.length);
// Flush if we expect to wait
if (!reader.available()) {
out.flush();
response.flushBuffer();
}
// No more messages another stream can take over
if (tunnel.hasQueuedReaderThreads())
break;
} while (tunnel.isOpen() && (message = reader.read()) != null);
// Close tunnel immediately upon EOF
if (message == null) {
deregisterTunnel(tunnel);
tunnel.close();
}
// End-of-instructions marker
out.write("0.;");
out.flush();
response.flushBuffer();
}
// Send end-of-stream marker and close tunnel if connection is
// closed
catch (GuacamoleConnectionClosedException e) {
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
// End-of-instructions marker
out.write("0.;");
out.flush();
response.flushBuffer();
}
catch (GuacamoleException e) {
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
throw e;
}
// Always close output stream
finally {
out.close();
}
} catch (IOException e) {
// Log typically frequent I/O error if desired
log.debug("Error writing to servlet output stream", e);
// Deregister and close
deregisterTunnel(tunnel);
tunnel.close();
} finally {
tunnel.releaseReader();
}
}
private void rebuildTunnel(HttpServletRequest request, HttpServletResponse response, String tunnelUUID, GuacamoleTunnel tunnel) throws GuacamoleException { GuacamoleSocket socket = tunnel.getSocket(); GuacamoleConfiguration configuration = ((ConfiguredGuacamoleSocket) socket).getConfiguration(); if ("vnc".equalsIgnoreCase(configuration.getProtocol())) { int port = Integer.parseInt(configuration.getParameter("port")); log.warn(String.format("vnc service with [%s:%d] not work, increasing port and retry", configuration.getParameter("hostname"), port)); if (port++ < 5910) { configuration.setParameter("port", port + ""); deregisterTunnel(tunnel); tunnel.close(); // TODO get cloud providerId tunnel = new GuacamoleVncReconnectTunnel( new ConfiguredGuacamoleSocket(getSocket(""), configuration), tunnelUUID); registerTunnel(tunnel); doRead(request, response, tunnelUUID); } } }
相关文章推荐
- 如何修复在Microsoft Azure中“虚拟机防火墙打开,关闭RDP的连接端口”问题
- 如何修复在Microsoft Azure中“虚拟机防火墙打开,关闭RDP的连接端口”问题
- 如何修复在Microsoft Azure中“虚拟机防火墙打开,关闭RDP的连接端口”问题
- 如何修复在Microsoft Azure中“虚拟机防火墙打开,关闭RDP的连接端口”问题
- 电脑中多个Mysql存在,如何切换端口登陆
- 记一次VNC远程连接Linux问题解决记录(5900端口测试、KDE桌面安装)
- [转]虚拟机中的linux图形界面如何切换到字符界面
- 如何配置虚拟机ip并且让真机连接虚拟机
- mark:如何使用FileZilla连接虚拟机上的Fedora
- 局域网如何通过SSH连接虚拟机装的centOS系统
- NAT连接的虚拟机提供web服务,如何让HOST同一局域网机器访问
- 如何在 vmware esxi 中开放 VNC功能及端口实现远程管理 完整篇
- 物理机通过nat的端口影谢连接虚拟机的ssh方法
- 如何远程连接非默认端口SQL Server
- 悬而未决:如何动态统计连接到本机某一端口的同一IP的连接数量
- sql server更改端口后如何连接
- Fuel6.1 Mirantis OpenStack 中使用vnc无法连接虚拟机
- 电脑中多个Mysql存在,如何切换端口登陆
- 记一次VNC远程连接Linux问题解决记录(5900端口测试、KDE桌面安装)
- 如何使用 SPICE client (virt-viewer) 来连接远程虚拟机桌面?