当我们需要通过网页直接访问XenServer的vm console时,我们可以直接使用websocket建立和xenserver的连接,这种方式有个问题是xenserver的主机为了支持这种WebSocket的访问方式,会在Xenserver的主机上面起很多wsproxy,作为xenserver vm console和websocket的代理。若我们不希望Xenserver加大负担,我们希望实现自己的webproxy,然后访问Xenserver Vm console,下面就是这种方法的介绍。
思路很简单,两个步骤:
1、创建一个websocketproxy服务
2、基于这个服务访问Xenserver vm console
这个websocketproxy的实现基于netty(netty是一个高性能的异步事件IO框架),下面是关键代码:
收到消息时先进行判断,若是建立websocket的连接,则消息类型肯定是HttpRequest,因为websocket协议是基于Http协议的。简单来说就是将http协议进行升级,从而变为websocket协议。进而调用ensureTargetConnect函数,连接具体的VM Console。
在ensureTargetConnect函数中,我们先是建立和Xenserver的连接,当连接建立之后,再发送消息给Xenserver,这个消息采用类似HTTP格式的非标准HTTP协议,具体参见makeHeaders函数,若和具体的VM Console建立好连接,则Xenserver会返回一个HTTP 200的状态码,表示我们已经和VM Console 成功建立连接了,这时Xenserver会立马发送这个VM Console的协议版本号。@Override public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) throws Exception { Object msg = e.getMessage(); // An HttpRequest means either an initial websocket connection // or a web server request if (msg instanceof HttpRequest) { handleHttpRequest(ctx, (HttpRequest) msg, e); // A WebSocketFrame means a continuation of an established websocket connection } else if (msg instanceof WebSocketFrame) { handleWebSocketFrame(ctx, (WebSocketFrame) msg, e); // A channel buffer we treat as a VNC protocol request } else if (msg instanceof ChannelBuffer) { handleVncDirect(ctx, (ChannelBuffer) msg, e, null); } } private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req, final MessageEvent e) throws Exception { // Allow only GET methods. if (req.getMethod() != GET) { Logger.getLogger(WebsockifyProxyHandler.class.getName()).info("Just support GET Method."); return; } String upgradeHeader = req.getHeader("Upgrade"); if(upgradeHeader != null && upgradeHeader.toUpperCase().equals("WEBSOCKET")){ Logger.getLogger(WebsockifyProxyHandler.class.getName()).fine("Websocket request from " + e.getRemoteAddress() + "."); // Handshake WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( this.getWebSocketLocation(req), "base64", false); this.handshaker = wsFactory.newHandshaker(req); if (this.handshaker == null) { wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel()); } else { // deal with a bug in the flash websocket emulation // it specifies WebSocket-Protocol when it seems it should specify Sec-WebSocket-Protocol String protocol = req.getHeader("WebSocket-Protocol"); String secProtocol = req.getHeader("Sec-WebSocket-Protocol"); if(protocol != null && secProtocol == null ) { req.addHeader("Sec-WebSocket-Protocol", protocol); } this.handshaker.handshake(ctx.getChannel(), req); } ensureTargetConnection (e, true, null, req.getUri()); } } private void ensureTargetConnection(ChannelEvent e, boolean websocket, final Object sendMsg, String para) throws Exception { if(outboundChannel == null) { String[] paras = para.split("&"); String[] uuids = paras[0].split("="); String[] authids = paras[1].split("="); // Suspend incoming traffic until connected to the remote host. final Channel inboundChannel = e.getChannel(); inboundChannel.setReadable(false); Logger.getLogger(WebsockifyProxyHandler.class.getName()).info("Inbound proxy connection from " + inboundChannel.getRemoteAddress() + " uuid=" + uuids[1] + " authid=" + authids[1] + "."); // resolve the target Console console = Websockify.ConsoleMap.get(uuids[1]); final String location = console.getLocation(Websockify.Conn); InetSocketAddress target = new InetSocketAddress(new URL(location).getHost(), 80); // Start the connection attempt. ClientBootstrap cb = new ClientBootstrap(cf); cb.getPipeline().addLast("aggregator", new HttpChunkAggregator(65536)); if ( websocket ) { cb.getPipeline().addLast("handler", new OutboundWebsocketHandler(e.getChannel(), trafficLock)); } else { cb.getPipeline().addLast("handler", new OutboundHandler(e.getChannel(), trafficLock)); } ChannelFuture f = cb.connect(target); outboundChannel = f.getChannel(); if ( sendMsg != null ) outboundChannel.write(sendMsg); f.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { // Connection attempt succeeded: // Begin to accept incoming traffic. connectVmConsole(location); inboundChannel.setReadable(true); Logger.getLogger(WebsockifyProxyHandler.class.getName()).info("Created outbound connection to " + location + "."); } else { Logger.getLogger(WebsockifyProxyHandler.class.getName()).severe("Failed to create outbound connection to " + location + "."); inboundChannel.close(); } } }); } else { if ( sendMsg != null ) outboundChannel.write(sendMsg); } } private void connectVmConsole(String location) throws BadServerResponse, XenAPIException, XmlRpcException, MalformedURLException { URL uri = new URL(location); String headers[] = makeHeaders(uri.getPath().concat("?").concat(uri.getQuery()), uri.getHost(), Websockify.Conn.getSessionReference()); for (String header : headers) { ChannelBuffer msgdata = new BigEndianHeapChannelBuffer(header.getBytes()); outboundChannel.write(msgdata); outboundChannel.write(new BigEndianHeapChannelBuffer("\r\n".getBytes())); } } private String[] makeHeaders(String path, String host, String session) { String[] headers = { String.format("CONNECT %s HTTP/1.1", path), String.format("Host: %s", host), String.format("Cookie: session_id=%s", session), "" }; return headers; }
参考资料:
http://docs.vmd.citrix.com/XenServer/5.6.0fp1/1.0/en_gb/sdk.html#retrieving_vnc_consoles_with_api
http://blogs.citrix.com/2011/02/11/xenserverconsole-examples/转自:http://blog.csdn.net/pingnanlee/article/details/13627573