Mercurial > hg > Applications > TightVNC_orginal
comparison src/main/java/jp/ac/u_ryukyu/treevnc/server/MyRfbProtoProxy.java @ 12:12c3a73be47f
rename package
author | one |
---|---|
date | Tue, 21 Aug 2012 14:24:38 +0900 |
parents | src/main/java/ac/ryukyu/treevnc/server/MyRfbProtoProxy.java@57ae9fbb1245 |
children | ff01665d26b4 |
comparison
equal
deleted
inserted
replaced
11:57ae9fbb1245 | 12:12c3a73be47f |
---|---|
1 package jp.ac.u_ryukyu.treevnc.server; | |
2 | |
3 import static org.junit.Assert.*; | |
4 | |
5 import java.awt.Graphics; | |
6 import java.awt.Image; | |
7 import java.awt.image.BufferedImage; | |
8 import java.io.BufferedOutputStream; | |
9 import java.io.BufferedReader; | |
10 import java.io.ByteArrayInputStream; | |
11 import java.io.ByteArrayOutputStream; | |
12 import java.io.IOException; | |
13 import java.io.InputStream; | |
14 import java.io.InputStreamReader; | |
15 import java.net.BindException; | |
16 import java.net.ServerSocket; | |
17 import java.net.Socket; | |
18 import java.nio.ByteBuffer; | |
19 import java.util.Iterator; | |
20 import java.util.LinkedList; | |
21 | |
22 import javax.imageio.ImageIO; | |
23 | |
24 import org.junit.Test; | |
25 | |
26 | |
27 //import myVncProxy.MulticastQueue.Client; | |
28 | |
29 import java.util.concurrent.ExecutorService; | |
30 import java.util.concurrent.atomic.AtomicInteger; | |
31 import java.util.zip.DataFormatException; | |
32 import java.util.zip.Deflater; | |
33 import java.util.zip.Inflater; | |
34 import java.io.OutputStream; | |
35 | |
36 import jp.ac.u_ryukyu.treevnc.MulticastQueue; | |
37 | |
38 public class MyRfbProtoProxy { | |
39 final static String versionMsg_3_855 = "RFB 003.855\n"; | |
40 /** | |
41 * CheckMillis is one of new msgType for RFB 3.855. | |
42 */ | |
43 final static byte SpeedCheckMillis = 4; | |
44 | |
45 // Secyrity type of OS X | |
46 final static int SecTypeReqAccess = 32; | |
47 | |
48 // Supported authentication types | |
49 final static int AuthAccess = 32; | |
50 | |
51 private static final int INFLATE_BUFSIZE = 1024 * 100; | |
52 boolean printStatusFlag = false; | |
53 long startCheckTime; | |
54 private int messageType; | |
55 private int rectangles; | |
56 private int rectX; | |
57 private int rectY; | |
58 private int rectW; | |
59 private int rectH; | |
60 private int encoding; | |
61 private int zLen; | |
62 private boolean clicomp = false; | |
63 | |
64 private ServerSocket servSock; | |
65 protected int acceptPort; | |
66 // private byte initData[]; | |
67 byte initData[]; | |
68 private LinkedList<Socket> cliListTmp; | |
69 private LinkedList<Socket> cliList; | |
70 boolean createBimgFlag; | |
71 boolean proxyFlag = true; | |
72 | |
73 ExecutorService executor; | |
74 | |
75 byte[] pngBytes; | |
76 | |
77 // private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new | |
78 // MostRecentMultiCast<LinkedList<ByteBuffer>>(10); | |
79 private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new MulticastQueue<LinkedList<ByteBuffer>>(); | |
80 private int clients = 0; | |
81 private Inflater inflater = new Inflater(); | |
82 private Deflater deflater = new Deflater(); | |
83 private CreateThread geth; | |
84 // private Thread requestThread; | |
85 private RequestScreenThread rThread; | |
86 private Thread requestThread; | |
87 | |
88 public MyRfbProtoProxy() throws IOException { | |
89 } | |
90 | |
91 | |
92 void initServSock(int port) throws IOException { | |
93 servSock = new ServerSocket(port); | |
94 acceptPort = port; | |
95 } | |
96 | |
97 void authenticationRequestAccess() throws IOException { | |
98 | |
99 byte[] headBuf = new byte[2]; | |
100 is.read(headBuf); | |
101 if (headBuf[1] == 2) { | |
102 byte[] b = new byte[258]; | |
103 is.read(b); | |
104 | |
105 byte[] outBuf = new byte[256]; | |
106 os.write(outBuf); | |
107 os.flush(); | |
108 } else if (headBuf[1] == 23) { | |
109 byte[] b = new byte[130]; | |
110 is.read(b); | |
111 byte[] outBuf = new byte[192]; | |
112 os.write(outBuf); | |
113 os.flush(); | |
114 } | |
115 | |
116 int result = readU32(); | |
117 if (result != 0) { | |
118 System.out.println("faild authentication "); | |
119 throw new IOException(); | |
120 } | |
121 | |
122 } | |
123 | |
124 /* | |
125 * default port number is 5999. | |
126 */ | |
127 public void selectPort(int p) { | |
128 if (servSock != null) | |
129 return; | |
130 int port = p; | |
131 while (true) { | |
132 try { | |
133 initServSock(port); | |
134 break; | |
135 } catch (BindException e) { | |
136 port++; | |
137 continue; | |
138 } catch (IOException e) { | |
139 | |
140 } | |
141 } | |
142 System.out.println("accept port = " + port); | |
143 } | |
144 | |
145 int getAcceptPort() { | |
146 return acceptPort; | |
147 } | |
148 | |
149 void setSoTimeout(int num) throws IOException { | |
150 servSock.setSoTimeout(num); | |
151 } | |
152 | |
153 public Socket accept() throws IOException { | |
154 return servSock.accept(); | |
155 } | |
156 | |
157 void addSock(Socket sock) { | |
158 cliList.add(sock); | |
159 } | |
160 | |
161 void addSockTmp(Socket sock) { | |
162 System.out.println("connected " + sock.getInetAddress()); | |
163 cliListTmp.add(sock); | |
164 } | |
165 | |
166 boolean markSupported() { | |
167 return is.markSupported(); | |
168 } | |
169 | |
170 synchronized void changeStatusFlag() { | |
171 printStatusFlag = true; | |
172 } | |
173 | |
174 void printMills() { | |
175 if (printStatusFlag) { | |
176 | |
177 changeStatusFlag(); | |
178 } else { | |
179 changeStatusFlag(); | |
180 } | |
181 } | |
182 | |
183 | |
184 void requestThreadStart() { | |
185 requestThread.start(); | |
186 } | |
187 | |
188 public synchronized void requestThreadNotify() { | |
189 rThread.reStart(); | |
190 } | |
191 | |
192 /** | |
193 * gzip byte arrays | |
194 * | |
195 * @param deflater | |
196 * @param inputs | |
197 * byte data[] | |
198 * @param inputIndex | |
199 * @param outputs | |
200 * byte data[] | |
201 * @return byte length in last byte array | |
202 * @throws IOException | |
203 */ | |
204 public int zip(Deflater deflater, LinkedList<ByteBuffer> inputs, | |
205 int inputIndex, LinkedList<ByteBuffer> outputs) throws IOException { | |
206 int len = 0; | |
207 ByteBuffer c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); | |
208 while (inputIndex < inputs.size()) { | |
209 ByteBuffer b1 = inputs.get(inputIndex++); | |
210 deflater.setInput(b1.array(), b1.position(), b1.remaining()); | |
211 /** | |
212 * If we finish() stream and reset() it, Deflater start new gzip | |
213 * stream, this makes continuous zlib reader unhappy. if we remove | |
214 * finish(), Deflater.deflate() never flushes its output. The | |
215 * original zlib deflate has flush flag. I'm pretty sure this a kind | |
216 * of bug of Java library. | |
217 */ | |
218 if (inputIndex == inputs.size()) | |
219 deflater.finish(); | |
220 int len1 = 0; | |
221 do { | |
222 len1 = deflater.deflate(c1.array(), c1.position(), | |
223 c1.remaining()); | |
224 if (len1 > 0) { | |
225 len += len1; | |
226 c1.position(c1.position() + len1); | |
227 if (c1.remaining() == 0) { | |
228 c1.flip(); | |
229 outputs.addLast(c1); | |
230 c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); | |
231 } | |
232 } | |
233 } while (len1 > 0 || !deflater.needsInput()); // &&!deflater.finished()); | |
234 } | |
235 if (c1.position() != 0) { | |
236 c1.flip(); | |
237 outputs.addLast(c1); | |
238 } | |
239 deflater.reset(); | |
240 return len; | |
241 } | |
242 | |
243 /** | |
244 * gunzip byte arrays | |
245 * | |
246 * @param inflater | |
247 * @param inputs | |
248 * byte data[] | |
249 * @param outputs | |
250 * byte data[] | |
251 * @return number of total bytes | |
252 * @throws IOException | |
253 */ | |
254 public int unzip(Inflater inflater, LinkedList<ByteBuffer> inputs, | |
255 int inputIndex, LinkedList<ByteBuffer> outputs, int bufSize) | |
256 throws DataFormatException { | |
257 int len = 0; | |
258 ByteBuffer buf = ByteBuffer.allocate(bufSize); | |
259 while (inputIndex < inputs.size()) { | |
260 ByteBuffer input = inputs.get(inputIndex++); | |
261 inflater.setInput(input.array(), input.position(), input.limit()); | |
262 // if (inputIndex==inputs.size()) if inflater/deflater has symmetry, | |
263 // we need this | |
264 // inflater.end(); but this won't work | |
265 do { | |
266 int len0 = inflater.inflate(buf.array(), buf.position(), | |
267 buf.remaining()); | |
268 if (len0 > 0) { | |
269 buf.position(buf.position() + len0); | |
270 len += len0; | |
271 if (buf.remaining() == 0) { | |
272 buf.flip(); | |
273 outputs.addLast(buf); | |
274 buf = ByteBuffer.allocate(bufSize); | |
275 } | |
276 } | |
277 } while (!inflater.needsInput()); | |
278 } | |
279 if (buf.position() != 0) { | |
280 buf.flip(); | |
281 outputs.addLast(buf); | |
282 } | |
283 return len; | |
284 } | |
285 | |
286 float maxMag = 1; | |
287 | |
288 /** | |
289 * send data to clients | |
290 * | |
291 * @param dataLen | |
292 * @throws IOException | |
293 * @throws DataFormatException | |
294 * | |
295 * Zlibed packet is compressed in context dependent way, that | |
296 * is, it have to send from the beginning. But this is | |
297 * impossible. So we have to compress it again for each clients. | |
298 * Separate deflater for each clients is necessary. | |
299 * | |
300 * Java's deflater does not support flush. This means to get the | |
301 * result, we have to finish the compression. Reseting start new | |
302 * compression, but it is not accepted well in zlib continuous | |
303 * reading. So we need new Encoding ZRLEE which reset decoder | |
304 * for each packet. ZRLEE can be invisible from user, but it | |
305 * have to be implemented in the clients. ZRLEE compression is | |
306 * not context dependent, so no recompression is necessary. | |
307 */ | |
308 | |
309 void readSendData(int dataLen) throws IOException, DataFormatException { | |
310 LinkedList<ByteBuffer> bufs = new LinkedList<ByteBuffer>(); | |
311 ByteBuffer header = ByteBuffer.allocate(16); | |
312 readFully(header.array(), 0, 16); | |
313 header.limit(16); | |
314 if (header.get(0) == RfbProto.FramebufferUpdate) { | |
315 int encoding = header.getInt(12); | |
316 if (encoding == RfbProto.EncodingZRLE | |
317 || encoding == RfbProto.EncodingZlib) { // ZRLEE is already | |
318 // recompressed | |
319 ByteBuffer len = ByteBuffer.allocate(4); | |
320 readFully(len.array(), 0, 4); | |
321 len.limit(4); | |
322 ByteBuffer inputData = ByteBuffer.allocate(dataLen - 20); | |
323 | |
324 startTiming(); | |
325 readFully(inputData.array(), 0, inputData.capacity()); | |
326 // System.out.println(dataLen); | |
327 inputData.limit(dataLen - 20); | |
328 stopTiming(); | |
329 | |
330 LinkedList<ByteBuffer> inputs = new LinkedList<ByteBuffer>(); | |
331 inputs.add(inputData); | |
332 | |
333 header.putInt(12, RfbProto.EncodingZRLEE); // means recompress | |
334 // every time | |
335 // using new Deflecter every time is incompatible with the | |
336 // protocol, clients have to be modified. | |
337 Deflater nDeflater = deflater; // new Deflater(); | |
338 LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>(); | |
339 unzip(inflater, inputs, 0, out, INFLATE_BUFSIZE); | |
340 // dump32(inputs); | |
341 int len2 = zip(nDeflater, out, 0, bufs); | |
342 ByteBuffer blen = ByteBuffer.allocate(4); | |
343 blen.putInt(len2); | |
344 blen.flip(); | |
345 bufs.addFirst(blen); | |
346 | |
347 bufs.addFirst(header); | |
348 // if(dataLen<=64000) | |
349 multicastqueue.put(bufs); | |
350 // is.reset(); | |
351 | |
352 /* | |
353 * System.out.println("ZRLE = "+dataLen); | |
354 * System.out.println("ZRLEE = "+(len2+20)); float mag = | |
355 * (float)dataLen / (float)(len2 + 20); | |
356 * System.out.println("ZRLE / ZRLEE = "+ mag); if(mag > maxMag) | |
357 * maxMag = mag; System.out.println("maxMag = "+maxMag); | |
358 */ | |
359 return; | |
360 } | |
361 bufs.add(header); | |
362 if (dataLen > 16) { | |
363 ByteBuffer b = ByteBuffer.allocate(dataLen - 16); | |
364 startTiming(); | |
365 readFully(b.array(), 0, dataLen - 16); | |
366 b.limit(dataLen - 16); | |
367 stopTiming(); | |
368 bufs.add(b); | |
369 } | |
370 multicastqueue.put(bufs); | |
371 // is.reset(); | |
372 return; | |
373 } | |
374 is.reset(); | |
375 | |
376 // It may be compressed. We can inflate here to avoid repeating clients | |
377 // decompressing here, | |
378 // but it may generate too many large data. It is better to do it in | |
379 // each client. | |
380 // But we have do inflation for all input data, so we have to do it | |
381 // here. | |
382 } | |
383 | |
384 public void newClient(AcceptThread acceptThread, final Socket newCli, | |
385 final OutputStream os, final InputStream is) throws IOException { | |
386 // createBimgFlag = true; | |
387 // rfb.addSockTmp(newCli); | |
388 // addSock(newCli); | |
389 final int myId = clients; | |
390 final MulticastQueue.Client<LinkedList<ByteBuffer>> c = multicastqueue | |
391 .newClient(); | |
392 final AtomicInteger writerRunning = new AtomicInteger(); | |
393 writerRunning.set(1); | |
394 /** | |
395 * Timeout thread. If a client is suspended, it has top of queue | |
396 * indefinitely, which caused memory overflow. After the timeout, we | |
397 * poll the queue and discard it. Start long wait if writer is running. | |
398 */ | |
399 final Runnable timer = new Runnable() { | |
400 public void run() { | |
401 int count = 0; | |
402 for (;;) { | |
403 long timeout = 50000 / 8; | |
404 try { | |
405 synchronized (this) { | |
406 int state, flag; | |
407 writerRunning.set(0); | |
408 wait(timeout); | |
409 flag = 0; | |
410 while ((state = writerRunning.get()) == 0) { | |
411 c.poll(); // discard, should be timeout | |
412 count++; | |
413 if (flag == 0) { | |
414 System.out.println("Discarding " + myId | |
415 + " count=" + count); | |
416 flag = 1; | |
417 } | |
418 wait(10); // if this is too short, writer cannot | |
419 // take the poll, if this is too | |
420 // long, memory will overflow... | |
421 } | |
422 if (flag == 1) | |
423 System.out.println("Resuming " + myId | |
424 + " count=" + count); | |
425 if (state != 1) { | |
426 System.out.println("Client died " + myId); | |
427 break; | |
428 } | |
429 } | |
430 } catch (InterruptedException e) { | |
431 } | |
432 } | |
433 } | |
434 }; | |
435 new Thread(timer).start(); | |
436 /** | |
437 * discard all incoming from clients | |
438 */ | |
439 final Runnable reader = new Runnable() { | |
440 public void run() { | |
441 byte b[] = new byte[4096]; | |
442 for (;;) { | |
443 try { | |
444 int c = is.read(b); | |
445 if (c <= 0) | |
446 throw new IOException(); | |
447 // System.out.println("client read "+c); | |
448 } catch (IOException e) { | |
449 try { | |
450 writerRunning.set(2); | |
451 os.close(); | |
452 is.close(); | |
453 } catch (IOException e1) { | |
454 } | |
455 return; | |
456 } | |
457 } | |
458 } | |
459 }; | |
460 /** | |
461 * send packets to a client | |
462 */ | |
463 Runnable sender = new Runnable() { | |
464 public void run() { | |
465 writerRunning.set(1); | |
466 try { | |
467 requestThreadNotify(); | |
468 // rThread.checkDelay(); | |
469 | |
470 /** | |
471 * initial connection of RFB protocol | |
472 */ | |
473 sendRfbVersion(os); | |
474 // readVersionMsg(is); | |
475 int rfbMinor = readVersionMsg(is, os); | |
476 sendSecurityType(os); | |
477 readSecType(is); | |
478 sendSecResult(os); | |
479 readClientInit(is); | |
480 sendInitData(os); | |
481 new Thread(reader).start(); // discard incoming packet here | |
482 // after. | |
483 // writeFramebufferUpdateRequest(0,0, framebufferWidth, | |
484 // framebufferHeight, false ); | |
485 int i = 0; | |
486 for (;;) { | |
487 LinkedList<ByteBuffer> bufs = c.poll(); | |
488 int inputIndex = 0; | |
489 ByteBuffer header = bufs.get(inputIndex); | |
490 if (header == null) | |
491 continue; | |
492 else if (header.get(0) == RfbProto.CheckDelay) { | |
493 writeToClient(os, bufs, inputIndex); | |
494 continue; | |
495 } else if (header.get(0) == RfbProto.FramebufferUpdate) { | |
496 // System.out.println("client "+ myId); | |
497 } | |
498 /* | |
499 if(i%20==0){ | |
500 sendDataCheckDelay(); | |
501 } | |
502 i++; | |
503 */ | |
504 writeToClient(os, bufs, inputIndex); | |
505 writerRunning.set(1); // yes my client is awaking. | |
506 } | |
507 } catch (IOException e) { | |
508 try { | |
509 writerRunning.set(2); | |
510 os.close(); | |
511 } catch (IOException e1) { | |
512 } | |
513 /* if socket closed cliList.remove(newCli); */ | |
514 } | |
515 } | |
516 | |
517 public void writeToClient(final OutputStream os, | |
518 LinkedList<ByteBuffer> bufs, int inputIndex) | |
519 throws IOException { | |
520 while (inputIndex < bufs.size()) { | |
521 ByteBuffer b = bufs.get(inputIndex++); | |
522 os.write(b.array(), b.position(), b.limit()); | |
523 } | |
524 os.flush(); | |
525 } | |
526 }; | |
527 clients++; | |
528 new Thread(sender).start(); | |
529 | |
530 } | |
531 | |
532 public void dump32(LinkedList<ByteBuffer> bufs) { | |
533 int len = 0; | |
534 for (ByteBuffer b : bufs) | |
535 len += b.remaining(); | |
536 ByteBuffer top = bufs.getFirst(); | |
537 ByteBuffer end = bufs.getLast(); | |
538 System.err.println("length: " + len); | |
539 System.err.print("head 0: "); | |
540 for (int i = 0; i < 16 && i < top.remaining(); i++) { | |
541 System.err.print(" " + top.get(i)); | |
542 } | |
543 System.err.print("tail 0: "); | |
544 for (int i = 0; i < 16 && i < end.remaining(); i++) { | |
545 System.err.print(" " + end.get(i)); | |
546 } | |
547 System.err.println(); | |
548 } | |
549 | |
550 @Test | |
551 public void test1() { | |
552 try { | |
553 LinkedList<ByteBuffer> in = new LinkedList<ByteBuffer>(); | |
554 LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>(); | |
555 LinkedList<ByteBuffer> out2 = new LinkedList<ByteBuffer>(); | |
556 // if (false) { | |
557 // for(int i=0;i<10;i++) { | |
558 // in.add(ByteBuffer.wrap("test1".getBytes())); | |
559 // in.add(ByteBuffer.wrap("test2".getBytes())); | |
560 // in.add(ByteBuffer.wrap("test3".getBytes())); | |
561 // in.add(ByteBuffer.wrap("test44".getBytes())); | |
562 // } | |
563 // } else | |
564 { | |
565 String t = ""; | |
566 for (int i = 0; i < 10; i++) { | |
567 t += "test1"; | |
568 t += "test2"; | |
569 t += "test3"; | |
570 t += "test44"; | |
571 } | |
572 in.add(ByteBuffer.wrap(t.getBytes())); | |
573 } | |
574 | |
575 LinkedList<ByteBuffer> in1 = clone(in); | |
576 | |
577 Deflater deflater = new Deflater(); | |
578 zip(deflater, in, 0, out); | |
579 // LinkedList<ByteBuffer> out3 = clone(out); zipped result is depend | |
580 // on deflator's state | |
581 unzip(inflater, out, 0, out2, INFLATE_BUFSIZE); | |
582 // inflater.reset(); | |
583 equalByteBuffers(in1, out2); | |
584 LinkedList<ByteBuffer> out4 = new LinkedList<ByteBuffer>(); | |
585 deflater = new Deflater(); | |
586 zip(deflater, out2, 0, out4); | |
587 LinkedList<ByteBuffer> out5 = new LinkedList<ByteBuffer>(); | |
588 unzip(inflater, out4, 0, out5, INFLATE_BUFSIZE); | |
589 int len = equalByteBuffers(in1, out5); | |
590 | |
591 System.out.println("Test Ok. " + len); | |
592 } catch (Exception e) { | |
593 assertEquals(0, 1); | |
594 } | |
595 } | |
596 | |
597 private LinkedList<ByteBuffer> clone(LinkedList<ByteBuffer> in) { | |
598 LinkedList<ByteBuffer> copy = new LinkedList<ByteBuffer>(); | |
599 for (ByteBuffer b : in) { | |
600 ByteBuffer c = b.duplicate(); | |
601 copy.add(c); | |
602 } | |
603 return copy; | |
604 } | |
605 | |
606 public int equalByteBuffers(LinkedList<ByteBuffer> in, | |
607 LinkedList<ByteBuffer> out2) { | |
608 int len = 0; | |
609 Iterable<Byte> i = byteBufferIterator(in); | |
610 Iterator<Byte> o = byteBufferIterator(out2).iterator(); | |
611 | |
612 for (int b : i) { | |
613 len++; | |
614 if (o.hasNext()) { | |
615 int c = o.next(); | |
616 assertEquals(b, c); | |
617 } else | |
618 assertEquals(0, 1); | |
619 } | |
620 if (o.hasNext()) | |
621 assertEquals(0, 1); | |
622 // System.out.println(); | |
623 return len; | |
624 } | |
625 | |
626 private Iterable<Byte> byteBufferIterator(final LinkedList<ByteBuffer> in) { | |
627 return new Iterable<Byte>() { | |
628 public Iterator<Byte> iterator() { | |
629 return new Iterator<Byte>() { | |
630 int bytes = 0; | |
631 int buffers = 0; | |
632 | |
633 public boolean hasNext() { | |
634 for (;;) { | |
635 if (buffers >= in.size()) | |
636 return false; | |
637 ByteBuffer b = in.get(buffers); | |
638 if (!(bytes < b.remaining())) { | |
639 buffers++; | |
640 bytes = 0; | |
641 } else | |
642 return true; | |
643 } | |
644 } | |
645 | |
646 public Byte next() { | |
647 ByteBuffer bf = in.get(buffers); | |
648 byte b = bf.get(bytes++); | |
649 if (bf.remaining() <= bytes) { | |
650 buffers++; | |
651 bytes = 0; | |
652 } | |
653 // System.out.print(b); | |
654 return b; | |
655 } | |
656 | |
657 public void remove() { | |
658 } | |
659 }; | |
660 } | |
661 }; | |
662 } | |
663 | |
664 } |