Mercurial > hg > Applications > TightVNC_orginal
view src/main/java/jp/ac/u_ryukyu/treevnc/server/MyRfbProtoProxy.java @ 26:73b13bc335c2
add ZRLEESender.java, NullRenderer.java and NullRepaintController.java
author | one |
---|---|
date | Thu, 30 Aug 2012 16:51:46 +0900 |
parents | 6a4312344a5a |
children | 18fad65bc447 |
line wrap: on
line source
package jp.ac.u_ryukyu.treevnc.server; import static org.junit.Assert.*; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.LinkedList; import org.junit.Test; import com.glavsoft.exceptions.CommonException; import com.glavsoft.exceptions.TransportException; import com.glavsoft.rfb.encoding.EncodingType; import com.glavsoft.rfb.protocol.Protocol; import com.glavsoft.rfb.protocol.ProtocolContext; import com.glavsoft.transport.Reader; import com.glavsoft.transport.Writer; //import myVncProxy.MulticastQueue.Client; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; import jp.ac.u_ryukyu.treevnc.MulticastQueue; public class MyRfbProtoProxy { final static String versionMsg_3_855 = "RFB 003.855\n"; /** * CheckMillis is one of new msgType for RFB 3.855. */ final static byte SpeedCheckMillis = 4; final static int FramebufferUpdate = 0; final static int CheckDelay = 11; final static int FramebufferUpdateRequest = 3; // Secyrity type of OS X final static int SecTypeReqAccess = 32; // Supported authentication types final static int AuthAccess = 32; private static final int INFLATE_BUFSIZE = 1024 * 100; boolean printStatusFlag = false; long startCheckTime; private ServerSocket servSock; protected int acceptPort; // private byte initData[]; ProtocolContext context; private LinkedList<Socket> cliListTmp; private LinkedList<Socket> cliList; boolean createBimgFlag; boolean proxyFlag = true; ExecutorService executor; byte[] pngBytes; // private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new // MostRecentMultiCast<LinkedList<ByteBuffer>>(10); private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new MulticastQueue<LinkedList<ByteBuffer>>(); private int clients = 0; private Inflater inflater = new Inflater(); private Deflater deflater = new Deflater(); // private Thread requestThread; private RequestScreenThread rThread; private Thread requestThread; public MyRfbProtoProxy() { rThread = new RequestScreenThread(this); requestThread = new Thread(rThread); } public void setStream(Writer _writer) { // os = _writer; } void initServSock(int port) throws IOException { servSock = new ServerSocket(port); acceptPort = port; } /* * default port number is 5999. */ public void selectPort(int p) { if (servSock != null) return; int port = p; while (true) { try { initServSock(port); break; } catch (BindException e) { port++; continue; } catch (IOException e) { } } System.out.println("accept port = " + port); } int getAcceptPort() { return acceptPort; } void setSoTimeout(int num) throws IOException { servSock.setSoTimeout(num); } public Socket accept() throws IOException { return servSock.accept(); } void addSock(Socket sock) { cliList.add(sock); } void addSockTmp(Socket sock) { System.out.println("connected " + sock.getInetAddress()); cliListTmp.add(sock); } synchronized void changeStatusFlag() { printStatusFlag = true; } void printMills() { if (printStatusFlag) { changeStatusFlag(); } else { changeStatusFlag(); } } void requestThreadStart() { requestThread.start(); } public synchronized void requestThreadNotify() { rThread.reStart(); } /** * gzip byte arrays * * @param deflater * @param inputs * byte data[] * @param inputIndex * @param outputs * byte data[] * @return byte length in last byte array * @throws IOException */ public int zip(Deflater deflater, LinkedList<ByteBuffer> inputs, int inputIndex, LinkedList<ByteBuffer> outputs) throws IOException { int len = 0; ByteBuffer c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); while (inputIndex < inputs.size()) { ByteBuffer b1 = inputs.get(inputIndex++); deflater.setInput(b1.array(), b1.position(), b1.remaining()); /** * If we finish() stream and reset() it, Deflater start new gzip * stream, this makes continuous zlib reader unhappy. if we remove * finish(), Deflater.deflate() never flushes its output. The * original zlib deflate has flush flag. I'm pretty sure this a kind * of bug of Java library. */ if (inputIndex == inputs.size()) deflater.finish(); int len1 = 0; do { len1 = deflater.deflate(c1.array(), c1.position(), c1.remaining()); if (len1 > 0) { len += len1; c1.position(c1.position() + len1); if (c1.remaining() == 0) { c1.flip(); outputs.addLast(c1); c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); } } } while (len1 > 0 || !deflater.needsInput()); // &&!deflater.finished()); } if (c1.position() != 0) { c1.flip(); outputs.addLast(c1); } deflater.reset(); return len; } /** * gunzip byte arrays * * @param inflater * @param inputs * byte data[] * @param outputs * byte data[] * @return number of total bytes * @throws IOException */ public int unzip(Inflater inflater, LinkedList<ByteBuffer> inputs, int inputIndex, LinkedList<ByteBuffer> outputs, int bufSize) throws DataFormatException { int len = 0; ByteBuffer buf = ByteBuffer.allocate(bufSize); while (inputIndex < inputs.size()) { ByteBuffer input = inputs.get(inputIndex++); inflater.setInput(input.array(), input.position(), input.limit()); // if (inputIndex==inputs.size()) if inflater/deflater has symmetry, // we need this // inflater.end(); but this won't work do { int len0 = inflater.inflate(buf.array(), buf.position(), buf.remaining()); if (len0 > 0) { buf.position(buf.position() + len0); len += len0; if (buf.remaining() == 0) { buf.flip(); outputs.addLast(buf); buf = ByteBuffer.allocate(bufSize); } } } while (!inflater.needsInput()); } if (buf.position() != 0) { buf.flip(); outputs.addLast(buf); } return len; } float maxMag = 1; /** * send data to clients * * @param dataLen * @param is * @throws IOException * @throws DataFormatException * * Zlibed packet is compressed in context dependent way, that * is, it have to send from the beginning. But this is * impossible. So we have to compress it again for each clients. * Separate deflater for each clients is necessary. * * Java's deflater does not support flush. This means to get the * result, we have to finish the compression. Reseting start new * compression, but it is not accepted well in zlib continuous * reading. So we need new Encoding ZRLEE which reset decoder * for each packet. ZRLEE can be invisible from user, but it * have to be implemented in the clients. ZRLEE compression is * not context dependent, so no recompression is necessary. * @throws TransportException */ public void readSendData(int dataLen, Reader is) throws TransportException { LinkedList<ByteBuffer> bufs = new LinkedList<ByteBuffer>(); ByteBuffer header = ByteBuffer.allocate(16); is.readBytes(header.array(), 0, 16); header.limit(16); if (header.get(0) == FramebufferUpdate) { int encoding = header.getInt(12); if (encoding == EncodingType.ZRLE.getId() || encoding == EncodingType.ZLIB.getId()) { // ZRLEE is // already // recompressed ByteBuffer len = ByteBuffer.allocate(4); is.readBytes(len.array(), 0, 4); len.limit(4); ByteBuffer inputData = ByteBuffer.allocate(dataLen - 20); is.readBytes(inputData.array(), 0, inputData.capacity()); // System.out.println(dataLen); inputData.limit(dataLen - 20); LinkedList<ByteBuffer> inputs = new LinkedList<ByteBuffer>(); inputs.add(inputData); header.putInt(12, EncodingType.ZRLEE.getId()); // means // recompress // every time // using new Deflecter every time is incompatible with the // protocol, clients have to be modified. Deflater nDeflater = deflater; // new Deflater(); LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>(); int len2 = 0; try { unzip(inflater, inputs, 0, out, INFLATE_BUFSIZE); len2 = zip(nDeflater, out, 0, bufs); } catch (DataFormatException e) { throw new TransportException(e); } catch (IOException e) { throw new TransportException(e); } ByteBuffer blen = ByteBuffer.allocate(4); blen.putInt(len2); blen.flip(); bufs.addFirst(blen); bufs.addFirst(header); // if(dataLen<=64000) multicastqueue.put(bufs); // is.reset(); /* * System.out.println("ZRLE = "+dataLen); * System.out.println("ZRLEE = "+(len2+20)); float mag = * (float)dataLen / (float)(len2 + 20); * System.out.println("ZRLE / ZRLEE = "+ mag); if(mag > maxMag) * maxMag = mag; System.out.println("maxMag = "+maxMag); */ return; } bufs.add(header); if (dataLen > 16) { ByteBuffer b = ByteBuffer.allocate(dataLen - 16); is.readBytes(b.array(), 0, dataLen - 16); b.limit(dataLen - 16); bufs.add(b); } multicastqueue.put(bufs); // is.reset(); return; } is.reset(); // It may be compressed. We can inflate here to avoid repeating clients // decompressing here, // but it may generate too many large data. It is better to do it in // each client. // But we have do inflation for all input data, so we have to do it // here. } public void newClient(AcceptThread acceptThread, final Socket newCli, final Writer os, final Reader is) throws IOException { // createBimgFlag = true; // rfb.addSockTmp(newCli); // addSock(newCli); final int myId = clients; final MulticastQueue.Client<LinkedList<ByteBuffer>> c = multicastqueue.newClient(); final AtomicInteger writerRunning = new AtomicInteger(); writerRunning.set(1); /** * Timeout thread. If a client is suspended, it has top of queue * indefinitely, which caused memory overflow. After the timeout, we * poll the queue and discard it. Start long wait if writer is running. */ final Runnable timer = new Runnable() { public void run() { int count = 0; for (;;) { long timeout = 50000 / 8; try { synchronized (this) { int state, flag; writerRunning.set(0); wait(timeout); flag = 0; while ((state = writerRunning.get()) == 0) { c.poll(); // discard, should be timeout count++; if (flag == 0) { System.out.println("Discarding " + myId + " count=" + count); flag = 1; } wait(10); // if this is too short, writer cannot // take the poll, if this is too // long, memory will overflow... } if (flag == 1) System.out.println("Resuming " + myId + " count=" + count); if (state != 1) { System.out.println("Client died " + myId); break; } } } catch (InterruptedException e) { } } } }; new Thread(timer).start(); /** * discard all incoming from clients */ final Runnable reader = new Runnable() { public void run() { byte b[] = new byte[4096]; for (;;) { try { int c = is.readByte(b); if (c <= 0) throw new IOException(); // System.out.println("client read "+c); } catch (IOException e) { try { writerRunning.set(2); os.close(); is.close(); } catch (IOException e1) { } catch (TransportException e1) { e1.printStackTrace(); } return; } catch (TransportException e) { e.printStackTrace(); } } } }; /** * send packets to a client */ Runnable sender = new Runnable() { public void run() { writerRunning.set(1); try { requestThreadNotify(); // rThread.checkDelay(); /** * initial connection of RFB protocol */ sendRfbVersion(os); // readVersionMsg(is); readVersionMsg(is, os); sendSecurityType(os); readSecType(is); sendSecResult(os); readClientInit(is); sendInitData(os); new Thread(reader).start(); // discard incoming packet here // after. // writeFramebufferUpdateRequest(0,0, framebufferWidth, // framebufferHeight, false ); for (;;) { LinkedList<ByteBuffer> bufs = c.poll(); int inputIndex = 0; ByteBuffer header = bufs.get(inputIndex); if (header == null) continue; else if (header.get(0) == CheckDelay) { writeToClient(os, bufs, inputIndex); continue; } else if (header.get(0) == FramebufferUpdate) { // System.out.println("client "+ myId); } /* * if(i%20==0){ sendDataCheckDelay(); } i++; */ writeToClient(os, bufs, inputIndex); writerRunning.set(1); // yes my client is awaking. } } catch (IOException e) { try { writerRunning.set(2); os.close(); } catch (IOException e1) { } /* if socket closed cliList.remove(newCli); */ } catch (TransportException e) { e.printStackTrace(); } } public void writeToClient(final Writer os, LinkedList<ByteBuffer> bufs, int inputIndex) throws TransportException { while (inputIndex < bufs.size()) { ByteBuffer b = bufs.get(inputIndex++); os.write(b.array(), b.position(), b.limit()); } os.flush(); } }; clients++; new Thread(sender).start(); } public void dump32(LinkedList<ByteBuffer> bufs) { int len = 0; for (ByteBuffer b : bufs) len += b.remaining(); ByteBuffer top = bufs.getFirst(); ByteBuffer end = bufs.getLast(); System.err.println("length: " + len); System.err.print("head 0: "); for (int i = 0; i < 16 && i < top.remaining(); i++) { System.err.print(" " + top.get(i)); } System.err.print("tail 0: "); for (int i = 0; i < 16 && i < end.remaining(); i++) { System.err.print(" " + end.get(i)); } System.err.println(); } @Test public void test1() { try { LinkedList<ByteBuffer> in = new LinkedList<ByteBuffer>(); LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>(); LinkedList<ByteBuffer> out2 = new LinkedList<ByteBuffer>(); // if (false) { // for(int i=0;i<10;i++) { // in.add(ByteBuffer.wrap("test1".getBytes())); // in.add(ByteBuffer.wrap("test2".getBytes())); // in.add(ByteBuffer.wrap("test3".getBytes())); // in.add(ByteBuffer.wrap("test44".getBytes())); // } // } else { String t = ""; for (int i = 0; i < 10; i++) { t += "test1"; t += "test2"; t += "test3"; t += "test44"; } in.add(ByteBuffer.wrap(t.getBytes())); } LinkedList<ByteBuffer> in1 = clone(in); Deflater deflater = new Deflater(); zip(deflater, in, 0, out); // LinkedList<ByteBuffer> out3 = clone(out); zipped result is depend // on deflator's state unzip(inflater, out, 0, out2, INFLATE_BUFSIZE); // inflater.reset(); equalByteBuffers(in1, out2); LinkedList<ByteBuffer> out4 = new LinkedList<ByteBuffer>(); deflater = new Deflater(); zip(deflater, out2, 0, out4); LinkedList<ByteBuffer> out5 = new LinkedList<ByteBuffer>(); unzip(inflater, out4, 0, out5, INFLATE_BUFSIZE); int len = equalByteBuffers(in1, out5); System.out.println("Test Ok. " + len); } catch (Exception e) { assertEquals(0, 1); } } private LinkedList<ByteBuffer> clone(LinkedList<ByteBuffer> in) { LinkedList<ByteBuffer> copy = new LinkedList<ByteBuffer>(); for (ByteBuffer b : in) { ByteBuffer c = b.duplicate(); copy.add(c); } return copy; } public int equalByteBuffers(LinkedList<ByteBuffer> in, LinkedList<ByteBuffer> out2) { int len = 0; Iterable<Byte> i = byteBufferIterator(in); Iterator<Byte> o = byteBufferIterator(out2).iterator(); for (int b : i) { len++; if (o.hasNext()) { int c = o.next(); assertEquals(b, c); } else assertEquals(0, 1); } if (o.hasNext()) assertEquals(0, 1); // System.out.println(); return len; } private Iterable<Byte> byteBufferIterator(final LinkedList<ByteBuffer> in) { return new Iterable<Byte>() { public Iterator<Byte> iterator() { return new Iterator<Byte>() { int bytes = 0; int buffers = 0; public boolean hasNext() { for (;;) { if (buffers >= in.size()) return false; ByteBuffer b = in.get(buffers); if (!(bytes < b.remaining())) { buffers++; bytes = 0; } else return true; } } public Byte next() { ByteBuffer bf = in.get(buffers); byte b = bf.get(bytes++); if (bf.remaining() <= bytes) { buffers++; bytes = 0; } // System.out.print(b); return b; } public void remove() { } }; } }; } void sendRfbVersion(Writer writer) throws IOException, TransportException { // os.write(versionMsg_3_8.getBytes()); writer.write(versionMsg_3_855.getBytes()); } int readVersionMsg(Reader reader, Writer writer) throws IOException, TransportException { byte[] b = new byte[12]; reader.readBytes(b); if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) { throw new IOException("this is not an RFB server"); } int rfbMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); int rfbMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); if (rfbMajor < 3) { throw new IOException( "RFB server does not support protocol version 3"); } if (rfbMinor == 855) { sendProxyFlag(writer); if (proxyFlag) sendPortNumber(writer); } return rfbMinor; } void sendProxyFlag(Writer writer) throws TransportException { if (proxyFlag) writer.writeInt(1); else writer.writeInt(0); } void sendPortNumber(Writer writer) throws TransportException { byte[] b = new byte[4]; //b = castIntByte(getHost.getPort()); b = castIntByte(9999); writer.write(b); } void sendSecurityType(Writer os) throws TransportException { // number-of-security-types os.writeInt(1); // security-types // 1:None os.writeInt(1); /* * os.write(4); os.write(30); os.write(31); os.write(32); os.write(35); * os.flush(); */ } void readSecType(Reader reader) throws TransportException { byte[] b = new byte[1]; reader.read(b); } void readSecType(Reader is, Writer os) throws TransportException { byte[] b = new byte[1]; is.readBytes(b); int count = 260; int[] data = { 0, 2, 0, -128, -1, -1, -1, -1, -1, -1, -1, -1, -55, 15, -38, -94, 33, 104, -62, 52, -60, -58, 98, -117, -128, -36, 28, -47, 41, 2, 78, 8, -118, 103, -52, 116, 2, 11, -66, -90, 59, 19, -101, 34, 81, 74, 8, 121, -114, 52, 4, -35, -17, -107, 25, -77, -51, 58, 67, 27, 48, 43, 10, 109, -14, 95, 20, 55, 79, -31, 53, 109, 109, 81, -62, 69, -28, -123, -75, 118, 98, 94, 126, -58, -12, 76, 66, -23, -90, 55, -19, 107, 11, -1, 92, -74, -12, 6, -73, -19, -18, 56, 107, -5, 90, -119, -97, -91, -82, -97, 36, 17, 124, 75, 31, -26, 73, 40, 102, 81, -20, -26, 83, -127, -1, -1, -1, -1, -1, -1, -1, -1, -111, 73, -29, 30, 57, -67, -75, -77, -49, -50, -99, -76, -80, -80, 14, 65, 57, -105, -103, -54, -102, 3, 39, -44, 39, 35, 118, -84, -64, 37, -117, -21, 89, -31, -68, 70, 5, 122, -92, -119, 9, 121, 63, -112, -60, 122, -46, -69, -36, 92, -103, -92, 74, 92, -73, 87, 120, -8, 116, -47, 111, 20, -41, 110, 122, -3, -94, 14, 42, -51, -59, 48, -54, -125, 117, 60, 77, -52, -31, 98, 32, -2, -102, -15, -29, 58, -14, -106, -116, -32, -86, 50, -32, -16, -3, -123, 87, 88, -118, 10, 120, -107, -37, 125, -110, 59, 87, 93, -24, 124, -99, 18, 78, -13, -49, -34, -24, -27, 1, 114, -67, -98, -56, -3, 85, -67, -126, 77 }; for (int i = 0; i < count; i++) { os.write((byte) data[i]); os.flush(); } byte[] c = new byte[256]; is.readBytes(c); System.out.println(new String(c)); } void sendSecResult(Writer os) throws TransportException { byte[] b = castIntByte(0); os.write(b); } byte[] castIntByte(int len) { byte[] b = new byte[4]; b[0] = (byte) ((len >>> 24) & 0xFF); b[1] = (byte) ((len >>> 16) & 0xFF); b[2] = (byte) ((len >>> 8) & 0xFF); b[3] = (byte) ((len >>> 0) & 0xFF); return b; } void readClientInit(Reader in) throws TransportException { byte[] b = new byte[0]; in.readBytes(b); } void sendInitData(Writer os) throws TransportException { os.write(context.getInitData()); } public void writeFramebufferUpdateRequest(int x, int y, int w, int h, boolean incremental) throws TransportException { byte[] b = new byte[10]; b[0] = (byte) FramebufferUpdateRequest; b[1] = (byte) (incremental ? 1 : 0); b[2] = (byte) ((x >> 8) & 0xff); b[3] = (byte) (x & 0xff); b[4] = (byte) ((y >> 8) & 0xff); b[5] = (byte) (y & 0xff); b[6] = (byte) ((w >> 8) & 0xff); b[7] = (byte) (w & 0xff); b[8] = (byte) ((h >> 8) & 0xff); b[9] = (byte) (h & 0xff); // os.write(b); } public void setProtocolContext(Protocol workingProtocol) { context = workingProtocol; } }