VNC之代理
[size=large] 背景:使用VNC客户端去连接DC上的VNC Server,采用的是代理的方式去访问。
基础研究: 使用Java实现代理服务器
参考资料:
http://www.ibm.com/developerworks/cn/java/l-javaproxy/index.html
http://ethen.iteye.com/blog/783338
参考代码:
package com.xxx.proxyserver;import java.io.*;import java.net.*;public class MyHttpProxy extends Thread {static public int CONNECT_RETRIES = 5; // 尝试与目标主机连接次数static public int CONNECT_PAUSE = 5; // 每次建立连接的间隔时间static public int TIMEOUT = 50; // 每次尝试连接的最大时间static public int BUFSIZ = 1024; // 缓冲区最大字节数static public boolean logging = false; // 是否记录日志static public OutputStream log_S = null; // 日志输出流static public OutputStream log_C = null; // 日志输出流static public String LOGFILENAME_S = "log_S.txt";static public String LOGFILENAME_C = "log_C.txt";// 与客户端相连的Socketprotected Socket csocket;public MyHttpProxy(Socket cs) {csocket = cs;start();}public void writeLog(int c, boolean browser) throws IOException {if (browser)log_C.write((char) c);elselog_S.write((char) c);}public void writeLog(byte[] bytes, int offset, int len, boolean browser)throws IOException {for (int i = 0; i < len; i++)writeLog((int) bytes[offset + i], browser);}public void run() {String buffer = ""; // 读取请求头String URL = "http://www.baidu.com"; // 读取请求URLString host = ""; // 读取目标主机hostint port = 80; // 默认端口80Socket ssocket = null;// cis为客户端输入流,sis为目标主机输入流InputStream cis = null, sis = null;// cos为客户端输出流,sos为目标主机输出流OutputStream cos = null, sos = null;try {csocket.setSoTimeout(TIMEOUT);cis = csocket.getInputStream();cos = csocket.getOutputStream();while (true) {int c = cis.read();if (c == -1)break; // -1为结尾标志if (c == '\r' || c == '\n')break;// 读入第一行数据buffer = buffer + (char) c;if (logging)writeLog(c, true);}// 抽取URL(<A href="http://www.baidu.com/">http://www.baidu.com/</A>)URL = getRequestURL(buffer);int n;// 抽取hostn = URL.indexOf("//");if (n != -1)host = URL.substring(n + 2); // www.baidu.com/n = host.indexOf('/');if (n != -1)host = host.substring(0, n);// www.baidu.com// 分析可能存在的端口号n = host.indexOf(':');if (n != -1) {port = Integer.parseInt(host.substring(n + 1));host = host.substring(0, n);}int retry = CONNECT_RETRIES;while (retry-- != 0) {try {ssocket = new Socket(host, port); // 尝试建立与目标主机的连接break;} catch (Exception e) {}// 等待Thread.sleep(CONNECT_PAUSE);}if (ssocket != null) {ssocket.setSoTimeout(TIMEOUT);sis = ssocket.getInputStream();sos = ssocket.getOutputStream();sos.write(buffer.getBytes()); // 将请求头写入pipe(cis, sis, sos, cos); // 建立通信管道}} catch (Exception e) {e.printStackTrace();} finally {try {csocket.close();cis.close();cos.close();} catch (Exception e1) {System.out.println("\nClient Socket Closed Exception:");e1.printStackTrace();}try {ssocket.close();sis.close();sos.close();} catch (Exception e2) {System.out.println("\nServer Socket Closed Exception:");e2.printStackTrace();}}}public String getRequestURL(String buffer) {String[] tokens = buffer.split(" ");String URL = "";for (int index = 0; index < tokens.length; index++) {if (tokens[index].startsWith("http://")) {URL = tokens[index];break;}}return URL;}/***供客户端和目标服务器通过两个Socket通信*/public void pipe(InputStream cis, InputStream sis, OutputStream sos,OutputStream cos) {try {int length;byte bytes[] = new byte[BUFSIZ];while (true) {try {if ((length = cis.read(bytes)) > 0) {sos.write(bytes, 0, length);if (logging)writeLog(bytes, 0, length, true);} else if (length < 0)break;} catch (SocketTimeoutException e) {} catch (InterruptedIOException e) {System.out.println("\nRequest Exception:");e.printStackTrace();}try {if ((length = sis.read(bytes)) > 0) {cos.write(bytes, 0, length);if (logging)writeLog(bytes, 0, length, false);} else if (length < 0)break;} catch (SocketTimeoutException e) {} catch (InterruptedIOException e) {System.out.println("\nResponse Exception:");e.printStackTrace();}}} catch (Exception e0) {System.out.println("Pipe异常: " + e0);}}public static void startProxy(int port, Class clobj) {try {ServerSocket ssock = new ServerSocket(port);while (true) {Class[] sarg = new Class[1];Object[] arg = new Object[1];sarg[0] = Socket.class;try {java.lang.reflect.Constructor cons = clobj.getDeclaredConstructor(sarg);arg[0] = ssock.accept();cons.newInstance(arg); // 创建HttpProxy或其派生类的实例} catch (Exception e) {Socket esock = (Socket) arg[0];try {esock.close();} catch (Exception ec) {}}}} catch (IOException e) {System.out.println("\nStartProxy Exception:");e.printStackTrace();}}// 测试用的简单main方法static public void main(String args[]) throws FileNotFoundException {System.out.println("在端口808启动代理服务器\n");OutputStream file_S = new FileOutputStream(new File(LOGFILENAME_S));OutputStream file_C = new FileOutputStream(new File(LOGFILENAME_C));MyHttpProxy.log_S = file_S;MyHttpProxy.log_C = file_C;MyHttpProxy.logging = true;MyHttpProxy.startProxy(808, MyHttpProxy.class);}} public Map<String, PipeInfo> getPipeInfos(String datacenterId) { Map<String, PipeInfo> result = new HashMap<String, PipeInfo>(); PipeInfo consolePipeInfo = getConsolePipeInfo(); PipeInfo datacenterPipeInfo = getDatacenterPipeInfo(datacenterId); result.put(PipeInfo.CONSOLE_PIPE_INFO_KEY, consolePipeInfo); result.put(PipeInfo.DATACENTER_PIPE_INFO_KEY, datacenterPipeInfo); return result; } private PipeInfo getConsolePipeInfo() { PipeInfo pipeInfo = new PipeInfo(); pipeInfo.setPipeIp(SystemConfigUtil.getCloudOuterIp()); pipeInfo.setPipePort(pipeService.getPort()); return pipeInfo; }public class PipeInfo implements Serializable { private static final long serialVersionUID = 1L; public static String CONSOLE_PIPE_INFO_KEY = "CONSOLE_PIPE_INFO_KEY"; public static String DATACENTER_PIPE_INFO_KEY = "DATACENTER_PIPE_INFO_KEY"; private String pipeIp; private int pipePort; public String getPipeIp() { return pipeIp; } public void setPipeIp(String pipeIp) { this.pipeIp = pipeIp; } public int getPipePort() { return pipePort; } public void setPipePort(int pipePort) { this.pipePort = pipePort; } public class PipeService implements InitializingBean, DisposableBean, Runnable{ private int port; public PipeService(){ } public int getPort() { return port; } public void setPort(int port) { this.port = port; } private PipeServer server; @Override public void afterPropertiesSet() throws Exception { new Thread(this).start(); } @Override public void destroy() throws Exception { if(server != null) { server.stop(); } } @Override public void run() { try { server = new PipeServer(); InetAddress localAddress = InetAddress.getByName("0.0.0.0"); server.start(port, 5, localAddress); } catch (UnknownHostException e) { e.printStackTrace(); } } public class PipeServer implements Runnable {static final int START_MODE = 0;static final int ACCEPT_MODE = 1;static final int PIPE_MODE = 2;static final int ABORT_MODE = 3;static final int BUF_SIZE = 8192;Socket sock = null, remote_sock = null;ServerSocket ss = null;InputStream in, remote_in;OutputStream out, remote_out;PipeMessage msg;int mode;Thread pipe_thread1, pipe_thread2;long lastReadTime;static int iddleTimeout = 180000; // 3 minutesstatic PrintStream log = System.out;PipeServer() {}public PipeServer(Socket s) {this.sock = s;mode = START_MODE;}public void start(int port, int backlog, InetAddress localIP) {try {ss = new ServerSocket(port, backlog, localIP);log("Starting Pipe Server on:" + ss.getInetAddress().getHostAddress() + ":" + ss.getLocalPort());while (true) {Socket s = ss.accept();log("Accepted from:" + s.getInetAddress().getHostName() + ":" + s.getPort());PipeServer ps = new PipeServer(s);(new Thread(ps)).start();}} catch (IOException ioe) {ioe.printStackTrace();} finally {}}static final void log(String s) {if (log != null) {log.println(s);log.flush();}}public static void setLog(OutputStream out) {if (out == null) {log = System.out;} else {log = new PrintStream(out, true);}}@Overridepublic void run() {switch (mode) {case START_MODE:try {startSession();} catch (IOException ioe) {// handleException(ioe);ioe.printStackTrace();} finally {abort();log("Client to remote, stopped.");}break;case PIPE_MODE:try {pipe(remote_in, out);} catch (IOException ioe) {} finally {abort();log("Remote to client, stopped");}break;case ABORT_MODE:break;default:log("Unexpected MODE " + mode);}}private void pipe(InputStream in, OutputStream out) throws IOException {lastReadTime = System.currentTimeMillis();byte[] buf = new byte[BUF_SIZE];int len = 0;while (len >= 0) {try {if (len != 0) {out.write(buf, 0, len);out.flush();}len = in.read(buf);lastReadTime = System.currentTimeMillis();} catch (InterruptedIOException iioe) {if (iddleTimeout == 0)break;// Other thread interrupted us.long timeSinceRead = System.currentTimeMillis() - lastReadTime;if (timeSinceRead >= iddleTimeout - 1000) // -1s for adjustment.break;len = 0;}}}private synchronized void abort() {if (mode == ABORT_MODE)return;mode = ABORT_MODE;try {log("Aborting operation");if (pipe_thread1 != null) { pipe_thread1.interrupt(); } if (pipe_thread2 != null) { pipe_thread2.interrupt(); } if (sock.isBound() && !sock.isClosed()) { sock.close(); } if (remote_sock.isBound() && !remote_sock.isClosed()) {remote_sock.close();}//if (ss != null) {//ss.close();//}} catch (IOException ioe) {}}private void startSession() throws IOException {sock.setSoTimeout(iddleTimeout);in = sock.getInputStream();out = sock.getOutputStream();msg = readMsg(in);handleRequest(msg);}private PipeMessage readMsg(InputStream in) throws IOException {PipeMessage msg;msg = new PipeMessage(in);return msg;}private void handleRequest(PipeMessage msg) throws IOException {log(msg.toString());switch (msg.command) {case PipeMessage.PIPE_CMD_CONNECT:onConnect(msg);break;default:throw new PipeException(PipeMessage.PIPE_CMD_NOT_SUPPORTED, "Pipe command does not support");}}private void onConnect(PipeMessage msg) throws IOException {Socket s = null;try {if (msg.ipsSize == 1) {s = new Socket(msg.ips[0], msg.ports[0]);} else {PipeMessage pm = new PipeMessage(PipeMessage.PIPE_CMD_CONNECT, msg.ips, msg.ports, 1);s = new Socket(msg.ips[0], msg.ports[0]);pm.write(s.getOutputStream());}log("Connected to " + s.getInetAddress() + ":" + s.getPort());} catch (Exception sE) {log("Failed connecting to remote socket. Exception: " + sE.getLocalizedMessage());}if (s != null) {startPipe(s);} else {throw (new RuntimeException("onConnect() Failed to create Socket()"));}return;}private void startPipe(Socket s) {mode = PIPE_MODE;remote_sock = s;try {remote_in = s.getInputStream();remote_out = s.getOutputStream();pipe_thread1 = Thread.currentThread();pipe_thread2 = new Thread(this);pipe_thread2.start();pipe(in, remote_out);} catch (IOException ioe) {ioe.printStackTrace();}}static public void main(String[] args) throws UnknownHostException {int port = 5900;if (args.length > 0) {port = Integer.parseInt(args[0]);}PipeServer server = new PipeServer();server.start(port, 5, InetAddress.getByName("0.0.0.0"));} public void stop() { try { if (ss.isBound() && !ss.isClosed()) { ss.close(); } } catch (IOException e) { log("failed to close server socket. " + e.getMessage()); } } public class PipeMessage {public int command;public String host = null;public InetAddress[] ips = null;public int[] ports = null;public int ipsSize = 0;private byte[] msgBytes;private int msgLength;static final int PIPE_CMD_CONNECT = 0x1;static final int PIPE_CMD_NOT_SUPPORTED = 1;// request messagePipeMessage(int command, InetAddress[] ips, int[] ports) {this.command = command;this.ips = ips;this.ports = ports;msgLength = ips.length * 6 + 2;msgBytes = new byte[msgLength];msgBytes[0] = (byte) command;msgBytes[1] = (byte) (ips.length);int msgP = 2;for (int i = 0; i < ips.length; i++) {byte[] addr;msgBytes[msgP] = (byte) (ports[i] >> 8);msgBytes[msgP + 1] = (byte) (ports[i]);msgP += 2;addr = ips[i].getAddress();System.arraycopy(addr, 0, msgBytes, msgP, 4);msgP += 4;}}PipeMessage(int command, InetAddress[] ips, int[] ports,int offset) {this.command = command;this.ips = ips;this.ports = ports;msgLength = (ips.length-offset) * 6 + 2;msgBytes = new byte[msgLength];msgBytes[0] = (byte) command;msgBytes[1] = (byte) (ips.length-offset);int msgP = 2;for (int i = offset; i < ips.length; i++) {byte[] addr;msgBytes[msgP] = (byte) (ports[i] >> 8);msgBytes[msgP + 1] = (byte) (ports[i]);msgP += 2;addr = ips[i].getAddress();System.arraycopy(addr, 0, msgBytes, msgP, 4);msgP += 4;}}// response messagePipeMessage(int command, int status) {this.command = command;msgLength = 3;msgBytes = new byte[msgLength];msgBytes[0] = (byte) command;msgBytes[1] = (byte) status;msgBytes[msgBytes.length - 1] = 0;}PipeMessage() {}public PipeMessage(InputStream in) throws IOException {msgBytes = null;read(in);}public void read(InputStream in) throws IOException {DataInputStream d_in = new DataInputStream(in);command = d_in.readUnsignedByte();switch (command) {case PipeMessage.PIPE_CMD_CONNECT:ipsSize = d_in.readUnsignedByte();ips = new InetAddress[ipsSize];ports = new int[ipsSize];for (int i = 0; i < ipsSize; i++) {ports[i] = d_in.readUnsignedShort();byte[] addr = new byte[4];d_in.readFully(addr);ips[i] = bytes2IP(addr);}break;default:throw new PipeException(PipeMessage.PIPE_CMD_NOT_SUPPORTED, "Pipe command does not support at reading");}}public void write(OutputStream out) throws IOException {if (msgBytes == null) {PipeMessage msg = new PipeMessage(command, ips, ports);msgBytes = msg.msgBytes;msgLength = msg.msgLength;}out.write(msgBytes);for (int i=0;i<msgLength;i++) System.err.print(Integer.toHexString(msgBytes[i])+" ");System.err.println();}public int getCommand() {return command;}public void setCommand(int command) {this.command = command;}public String getHost() {return host;}public void setHost(String host) {this.host = host;}public InetAddress[] getIps() {return ips;}public void setIps(InetAddress[] ips) {this.ips = ips;}public int getIpsSize() {return ipsSize;}public void setIpsSize(int ipsSize) {this.ipsSize = ipsSize;}static InetAddress bytes2IP(byte[] addr) {String s = bytes2IPV4(addr, 0);try {return InetAddress.getByName(s);} catch (UnknownHostException uh_ex) {return null;}}static final String bytes2IPV4(byte[] addr, int offset) {String hostName = "" + (addr[offset] & 0xFF);for (int i = offset + 1; i < offset + 4; ++i)hostName += "." + (addr[i] & 0xFF);return hostName;}@Overridepublic String toString() {return "PipeMessage: command=" + command + "\n host=" + host + "\n ips=" + Arrays.toString(ips) + "\n ipsSize=" + ipsSize + "\n ports=" + Arrays.toString(ports) + "\n";}