Mercurial > hg > Members > nobuyasu > tightVNCClient
annotate src/myVncClient/MyRfbProto.java @ 81:a542f92500ab
modify EchoClient.java
author | one |
---|---|
date | Wed, 31 Aug 2011 16:56:00 +0900 |
parents | fe94e353d595 |
children | 29e43be288ca |
rev | line source |
---|---|
17 | 1 package myVncClient; |
45 | 2 |
69 | 3 |
19 | 4 import java.awt.Graphics; |
5 import java.awt.Image; | |
18 | 6 import java.awt.image.BufferedImage; |
65 | 7 import java.io.BufferedInputStream; |
19 | 8 import java.io.BufferedOutputStream; |
15 | 9 import java.io.BufferedReader; |
18 | 10 import java.io.ByteArrayInputStream; |
19 | 11 import java.io.ByteArrayOutputStream; |
65 | 12 import java.io.DataInputStream; |
7 | 13 import java.io.IOException; |
27 | 14 import java.io.InputStream; |
15 | 15 import java.io.InputStreamReader; |
18 | 16 import java.net.BindException; |
10 | 17 import java.net.ServerSocket; |
7 | 18 import java.net.Socket; |
69 | 19 import java.net.UnknownHostException; |
60 | 20 import java.nio.ByteBuffer; |
21 import java.util.Iterator; | |
10 | 22 import java.util.LinkedList; |
8 | 23 |
18 | 24 import javax.imageio.ImageIO; |
25 | |
45 | 26 import myVncClient.MulticastQueue.Client; |
27 | |
25 | 28 import java.util.concurrent.ExecutorService; |
69 | 29 import java.util.concurrent.atomic.AtomicBoolean; |
30 import java.util.concurrent.atomic.AtomicInteger; | |
60 | 31 import java.util.zip.DataFormatException; |
32 import java.util.zip.Deflater; | |
33 import java.util.zip.Inflater; | |
25 | 34 import java.io.OutputStream; |
7 | 35 |
60 | 36 public |
69 | 37 class MyRfbProto extends RfbProto { |
27 | 38 final static String versionMsg_3_998 = "RFB 003.998\n"; |
60 | 39 /** |
40 * CheckMillis is one of new msgType for RFB 3.998. | |
41 */ | |
42 final static byte SpeedCheckMillis = 4; | |
43 private static final int INFLATE_BUFSIZE = 1024*100; | |
44 boolean printStatusFlag = false; | |
45 long startCheckTime; | |
45 | 46 |
15 | 47 private int messageType; |
48 private int rectangles; | |
19 | 49 private int rectX; |
50 private int rectY; | |
51 private int rectW; | |
52 private int rectH; | |
15 | 53 private int encoding; |
19 | 54 private int zLen; |
69 | 55 private boolean clicomp = false; |
15 | 56 |
19 | 57 private ServerSocket servSock; |
18 | 58 private int acceptPort; |
8 | 59 private byte initData[]; |
45 | 60 private LinkedList<Socket> cliListTmp; |
61 private LinkedList<Socket> cliList; | |
19 | 62 boolean createBimgFlag; |
72 | 63 //override |
64 InterfaceForViewer viewer; | |
34 | 65 |
25 | 66 ExecutorService executor; |
45 | 67 |
18 | 68 byte[] pngBytes; |
45 | 69 |
60 | 70 // private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new MostRecentMultiCast<LinkedList<ByteBuffer>>(10); |
71 private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new MulticastQueue<LinkedList<ByteBuffer>>(); | |
72 private int clients = 0; | |
73 private Inflater inflater = new Inflater(); | |
69 | 74 private Deflater deflater = new Deflater(); |
45 | 75 |
69 | 76 public |
77 MyRfbProto() throws IOException { | |
78 } | |
79 | |
45 | 80 MyRfbProto(String h, int p, VncViewer v) throws IOException { |
7 | 81 super(h, p, v); |
45 | 82 cliList = new LinkedList<Socket>(); |
83 cliListTmp = new LinkedList<Socket>(); | |
19 | 84 createBimgFlag = false; |
45 | 85 // sendThreads = new LinkedList<Thread>(); |
86 // executor = Executors.newCachedThreadPool(); | |
87 // executor = Executors.newSingleThreadExecutor(); | |
10 | 88 } |
72 | 89 |
15 | 90 MyRfbProto(String h, int p) throws IOException { |
91 super(h, p); | |
45 | 92 cliList = new LinkedList<Socket>(); |
93 cliListTmp = new LinkedList<Socket>(); | |
19 | 94 createBimgFlag = false; |
45 | 95 // sendThreads = new LinkedList<Thread>(); |
96 // executor = Executors.newCachedThreadPool(); | |
97 // executor = Executors.newSingleThreadExecutor(); | |
15 | 98 } |
72 | 99 |
100 | |
101 void readVersionMsg() throws Exception { | |
102 | |
103 byte[] b = new byte[12]; | |
104 | |
105 readFully(b); | |
106 | |
107 if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') | |
108 || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') | |
109 || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') | |
110 || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') | |
111 || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) { | |
112 throw new Exception("Host " + host + " port " + port | |
113 + " is not an RFB server"); | |
114 } | |
115 | |
116 serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); | |
117 serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); | |
118 | |
119 if (serverMajor < 3) { | |
120 throw new Exception( | |
121 "RFB server does not support protocol version 3"); | |
122 } | |
123 | |
124 if (serverMinor == 998) { | |
125 | |
126 } | |
127 | |
128 } | |
73 | 129 byte[] readEchoPort() throws Exception { |
130 byte[] b = new byte[4]; | |
131 readFully(b); | |
132 | |
133 return b; | |
134 } | |
135 | |
136 | |
72 | 137 |
69 | 138 void changeParent(String h, int p) throws IOException { |
139 host = h; | |
140 port = p; | |
141 | |
142 sock = new Socket(host, port); | |
143 is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), | |
144 16384)); | |
145 os = sock.getOutputStream(); | |
146 | |
147 timing = false; | |
148 timeWaitedIn100us = 5; | |
149 timedKbits = 0; | |
150 } | |
151 | |
28 | 152 // over write |
27 | 153 void writeVersionMsg() throws IOException { |
154 clientMajor = 3; | |
155 if (serverMinor >= 9) { | |
45 | 156 clientMinor = 9; |
157 os.write(versionMsg_3_998.getBytes()); | |
27 | 158 } else if (serverMajor > 3 || serverMinor >= 8) { |
159 clientMinor = 8; | |
160 os.write(versionMsg_3_8.getBytes()); | |
161 } else if (serverMinor >= 9) { | |
162 clientMinor = 9; | |
163 os.write(versionMsg_3_998.getBytes()); | |
164 } else if (serverMinor >= 7) { | |
165 clientMinor = 7; | |
166 os.write(versionMsg_3_7.getBytes()); | |
167 } else { | |
168 clientMinor = 3; | |
169 os.write(versionMsg_3_3.getBytes()); | |
170 } | |
171 protocolTightVNC = false; | |
172 initCapabilities(); | |
173 } | |
174 | |
45 | 175 void initServSock(int port) throws IOException { |
10 | 176 servSock = new ServerSocket(port); |
18 | 177 acceptPort = port; |
178 } | |
45 | 179 |
69 | 180 // 5550を開けるが、開いてないなら+1のポートを開ける。 |
60 | 181 void selectPort(int p) { |
182 int port = p; | |
45 | 183 while (true) { |
184 try { | |
60 | 185 initServSock(port); |
18 | 186 break; |
45 | 187 } catch (BindException e) { |
60 | 188 port++; |
18 | 189 continue; |
45 | 190 } catch (IOException e) { |
19 | 191 |
18 | 192 } |
193 } | |
60 | 194 System.out.println("accept port = " + port); |
18 | 195 } |
45 | 196 |
197 int getAcceptPort() { | |
18 | 198 return acceptPort; |
7 | 199 } |
45 | 200 |
10 | 201 void setSoTimeout(int num) throws IOException { |
202 servSock.setSoTimeout(num); | |
203 } | |
45 | 204 |
10 | 205 Socket accept() throws IOException { |
206 return servSock.accept(); | |
207 } | |
208 | |
45 | 209 void addSock(Socket sock) { |
10 | 210 cliList.add(sock); |
211 } | |
45 | 212 |
213 void addSockTmp(Socket sock) { | |
214 System.out.println("connected " + sock.getInetAddress()); | |
19 | 215 cliListTmp.add(sock); |
216 } | |
45 | 217 |
8 | 218 boolean markSupported() { |
7 | 219 return is.markSupported(); |
220 } | |
45 | 221 |
8 | 222 void readServerInit() throws IOException { |
45 | 223 |
60 | 224 is.mark(255); |
8 | 225 skipBytes(20); |
226 int nlen = readU32(); | |
45 | 227 int blen = 20 + 4 + nlen; |
10 | 228 initData = new byte[blen]; |
60 | 229 is.reset(); |
10 | 230 |
60 | 231 is.mark(blen); |
8 | 232 readFully(initData); |
60 | 233 is.reset(); |
45 | 234 |
7 | 235 framebufferWidth = readU16(); |
236 framebufferHeight = readU16(); | |
237 bitsPerPixel = readU8(); | |
238 depth = readU8(); | |
239 bigEndian = (readU8() != 0); | |
240 trueColour = (readU8() != 0); | |
241 redMax = readU16(); | |
242 greenMax = readU16(); | |
243 blueMax = readU16(); | |
244 redShift = readU8(); | |
245 greenShift = readU8(); | |
246 blueShift = readU8(); | |
8 | 247 byte[] pad = new byte[3]; |
248 readFully(pad); | |
249 int nameLength = readU32(); | |
250 byte[] name = new byte[nameLength]; | |
251 readFully(name); | |
252 desktopName = new String(name); | |
7 | 253 |
8 | 254 // Read interaction capabilities (TightVNC protocol extensions) |
255 if (protocolTightVNC) { | |
256 int nServerMessageTypes = readU16(); | |
257 int nClientMessageTypes = readU16(); | |
258 int nEncodingTypes = readU16(); | |
259 readU16(); | |
260 readCapabilityList(serverMsgCaps, nServerMessageTypes); | |
261 readCapabilityList(clientMsgCaps, nClientMessageTypes); | |
262 readCapabilityList(encodingCaps, nEncodingTypes); | |
263 } | |
264 | |
265 inNormalProtocol = true; | |
7 | 266 } |
267 | |
45 | 268 void sendRfbVersion(OutputStream os) throws IOException { |
78 | 269 // os.write(versionMsg_3_998.getBytes()); |
270 os.write(versionMsg_3_8.getBytes()); | |
27 | 271 } |
45 | 272 |
28 | 273 void readVersionMsg(InputStream is) throws IOException { |
274 | |
275 byte[] b = new byte[12]; | |
276 | |
277 is.read(b); | |
278 | |
279 if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') | |
280 || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') | |
281 || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') | |
282 || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') | |
283 || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) { | |
284 throw new IOException("Host " + host + " port " + port | |
285 + " is not an RFB server"); | |
286 } | |
287 | |
288 serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); | |
289 serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); | |
290 | |
291 if (serverMajor < 3) { | |
292 throw new IOException( | |
293 "RFB server does not support protocol version 3"); | |
45 | 294 } |
295 | |
73 | 296 |
45 | 297 } |
298 | |
73 | 299 |
27 | 300 void sendSecurityType(OutputStream os) throws IOException { |
301 // number-of-security-types | |
302 os.write(1); | |
45 | 303 // security-types |
27 | 304 // 1:None |
305 os.write(1); | |
306 } | |
45 | 307 |
28 | 308 void readSecType(InputStream is) throws IOException { |
309 byte[] b = new byte[1]; | |
310 is.read(b); | |
311 | |
312 } | |
45 | 313 |
28 | 314 void sendSecResult(OutputStream os) throws IOException { |
315 byte[] b = castIntByte(0); | |
316 os.write(b); | |
317 } | |
45 | 318 |
27 | 319 void readClientInit(InputStream in) throws IOException { |
320 byte[] b = new byte[0]; | |
321 in.read(b); | |
322 } | |
45 | 323 |
324 void sendInitData(OutputStream os) throws IOException { | |
325 os.write(initData); | |
8 | 326 } |
10 | 327 |
45 | 328 |
329 void sendPngImage() { | |
330 try { | |
331 for (Socket cli : cliListTmp) { | |
332 try { | |
19 | 333 sendPngData(cli); |
334 addSock(cli); | |
45 | 335 } catch (IOException e) { |
19 | 336 // if socket closed |
337 cliListTmp.remove(cli); | |
338 } | |
339 } | |
45 | 340 // System.out.println("cliSize="+cliSize()); |
341 } catch (Exception e) { | |
19 | 342 } |
343 cliListTmp.clear(); | |
344 } | |
345 | |
15 | 346 boolean ready() throws IOException { |
347 BufferedReader br = new BufferedReader(new InputStreamReader(is)); | |
348 return br.ready(); | |
45 | 349 } |
10 | 350 |
45 | 351 int cliSize() { |
10 | 352 return cliList.size(); |
45 | 353 } |
354 | |
355 void printNumBytesRead() { | |
356 System.out.println("numBytesRead=" + numBytesRead); | |
357 } | |
358 | |
44
034528dfb650
modify MuRfbProto.java VncCanvas.java. create sendDataToClient method.
e085711
parents:
34
diff
changeset
|
359 |
45 | 360 |
361 void regiFramebufferUpdate() throws IOException { | |
60 | 362 is.mark(20); |
363 messageType = readU8(); // 0 | |
364 skipBytes(1); // 1 | |
365 rectangles = readU16(); // 2 | |
366 rectX = readU16(); // 4 | |
367 rectY = readU16(); // 6 | |
368 rectW = readU16(); // 8 | |
369 rectH = readU16(); // 10 | |
370 encoding = readU32(); // 12 | |
78 | 371 // System.out.println("encoding = "+encoding); |
69 | 372 if (encoding == EncodingZRLE|| encoding==EncodingZRLEE||encoding==EncodingZlib) |
44
034528dfb650
modify MuRfbProto.java VncCanvas.java. create sendDataToClient method.
e085711
parents:
34
diff
changeset
|
373 zLen = readU32(); |
60 | 374 else |
375 zLen = 0; | |
376 is.reset(); | |
69 | 377 |
60 | 378 } |
379 | |
380 int checkAndMark() throws IOException { | |
381 int dataLen; | |
382 switch (encoding) { | |
383 case RfbProto.EncodingRaw: | |
384 dataLen = rectW * rectH * 4 + 16; | |
385 is.mark(dataLen); | |
386 break; | |
387 case RfbProto.EncodingCopyRect: | |
388 dataLen = 16 + 4; | |
389 is.mark(dataLen); | |
390 break; | |
391 case RfbProto.EncodingRRE: | |
392 case RfbProto.EncodingCoRRE: | |
393 case RfbProto.EncodingHextile: | |
394 case RfbProto.EncodingTight: | |
395 dataLen = zLen + 20; | |
396 is.mark(dataLen); | |
397 break; | |
398 case RfbProto.EncodingZlib: | |
399 case RfbProto.EncodingZRLE: | |
69 | 400 case RfbProto.EncodingZRLEE: |
60 | 401 dataLen = zLen + 20; |
402 is.mark(dataLen); | |
403 break; | |
404 case RfbProto.EncodingXCursor: | |
405 case RfbProto.EncodingRichCursor: | |
406 int pixArray = rectW * rectH * 4; | |
407 int u8Array = (int)Math.floor((rectW + 7)/8) * rectH; | |
408 dataLen = pixArray + u8Array; | |
409 printFramebufferUpdate(); | |
410 is.mark(dataLen); | |
411 break; | |
412 default: | |
413 dataLen = 1000000; | |
414 is.mark(dataLen); | |
415 } | |
44
034528dfb650
modify MuRfbProto.java VncCanvas.java. create sendDataToClient method.
e085711
parents:
34
diff
changeset
|
416 return dataLen; |
034528dfb650
modify MuRfbProto.java VncCanvas.java. create sendDataToClient method.
e085711
parents:
34
diff
changeset
|
417 } |
60 | 418 |
45 | 419 |
60 | 420 void sendDataToClient() throws Exception { |
44
034528dfb650
modify MuRfbProto.java VncCanvas.java. create sendDataToClient method.
e085711
parents:
34
diff
changeset
|
421 regiFramebufferUpdate(); |
034528dfb650
modify MuRfbProto.java VncCanvas.java. create sendDataToClient method.
e085711
parents:
34
diff
changeset
|
422 int dataLen = checkAndMark(); |
034528dfb650
modify MuRfbProto.java VncCanvas.java. create sendDataToClient method.
e085711
parents:
34
diff
changeset
|
423 readSendData(dataLen); |
25 | 424 } |
45 | 425 |
426 BufferedImage createBufferedImage(Image img) { | |
427 BufferedImage bimg = new BufferedImage(img.getWidth(null), | |
428 img.getHeight(null), BufferedImage.TYPE_INT_RGB); | |
19 | 429 |
430 Graphics g = bimg.getGraphics(); | |
431 g.drawImage(img, 0, 0, null); | |
432 g.dispose(); | |
433 return bimg; | |
434 } | |
435 | |
45 | 436 void createPngBytes(BufferedImage bimg) throws IOException { |
437 pngBytes = getImageBytes(bimg, "png"); | |
19 | 438 } |
45 | 439 |
440 byte[] getBytes(BufferedImage img) throws IOException { | |
19 | 441 byte[] b = getImageBytes(img, "png"); |
442 return b; | |
15 | 443 } |
45 | 444 |
445 byte[] getImageBytes(BufferedImage image, String imageFormat) | |
446 throws IOException { | |
19 | 447 ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
448 BufferedOutputStream os = new BufferedOutputStream(bos); | |
449 image.flush(); | |
450 ImageIO.write(image, imageFormat, os); | |
451 os.flush(); | |
452 os.close(); | |
453 return bos.toByteArray(); | |
454 } | |
455 | |
45 | 456 void sendPngData(Socket sock) throws IOException { |
19 | 457 byte[] dataLength = castIntByte(pngBytes.length); |
458 sock.getOutputStream().write(dataLength); | |
459 sock.getOutputStream().write(pngBytes); | |
460 } | |
45 | 461 |
462 byte[] castIntByte(int len) { | |
19 | 463 byte[] b = new byte[4]; |
45 | 464 b[0] = (byte) ((len >>> 24) & 0xFF); |
465 b[1] = (byte) ((len >>> 16) & 0xFF); | |
466 b[2] = (byte) ((len >>> 8) & 0xFF); | |
467 b[3] = (byte) ((len >>> 0) & 0xFF); | |
19 | 468 return b; |
469 } | |
45 | 470 |
471 BufferedImage createBimg() throws IOException { | |
19 | 472 BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(pngBytes)); |
473 return bimg; | |
474 } | |
69 | 475 |
45 | 476 void printFramebufferUpdate() { |
477 | |
15 | 478 System.out.println("messageType=" + messageType); |
45 | 479 System.out.println("rectangles=" + rectangles); |
15 | 480 System.out.println("encoding=" + encoding); |
60 | 481 System.out.println("rectX = "+rectX+": rectY = "+rectY); |
482 System.out.println("rectW = "+rectW+": rectH = "+rectH); | |
45 | 483 switch (encoding) { |
19 | 484 case RfbProto.EncodingRaw: |
45 | 485 System.out.println("rectW * rectH * 4 + 16 =" + rectW * rectH * 4 |
486 + 16); | |
19 | 487 break; |
488 default: | |
489 } | |
15 | 490 } |
60 | 491 |
492 void readSpeedCheck() throws IOException { | |
493 byte[] b = new byte[1]; | |
494 readFully(b); | |
495 } | |
496 | |
497 void startSpeedCheck() { | |
498 ByteBuffer b = ByteBuffer.allocate(10); | |
499 b.put((byte)SpeedCheckMillis); | |
500 b.flip(); | |
501 startCheckTime = System.currentTimeMillis(); | |
502 System.out.println("startChckTime = "+ startCheckTime); | |
503 LinkedList<ByteBuffer>bufs = new LinkedList<ByteBuffer>(); | |
504 bufs.add(b); | |
505 multicastqueue.put(bufs); | |
506 } | |
507 | |
508 void endSpeedCheck() { | |
509 long accTime = System.currentTimeMillis(); | |
510 long time = accTime - startCheckTime; | |
511 System.out.println("checkMillis: " + time); | |
512 } | |
513 | |
514 | |
515 synchronized void changeStatusFlag() { | |
516 printStatusFlag = true; | |
517 } | |
518 | |
519 void printMills() { | |
520 if(printStatusFlag) { | |
521 | |
522 changeStatusFlag(); | |
523 } else { | |
524 changeStatusFlag(); | |
525 } | |
526 } | |
527 | |
528 void speedCheckMillis() { | |
529 Runnable stdin = new Runnable() { | |
530 public void run() { | |
531 int c; | |
532 try { | |
533 while( (c = System.in.read()) != -1 ) { | |
534 switch(c) { | |
535 case 's': | |
536 break; | |
537 default: | |
538 startSpeedCheck(); | |
539 break; | |
540 } | |
541 } | |
542 }catch(IOException e){ | |
543 System.out.println(e); | |
544 } | |
545 } | |
546 }; | |
547 | |
548 new Thread(stdin).start(); | |
549 } | |
550 | |
551 /** | |
552 * gzip byte arrays | |
553 * @param deflater | |
554 * @param inputs | |
555 * byte data[] | |
556 * @param inputIndex | |
557 * @param outputs | |
558 * byte data[] | |
559 * @return byte length in last byte array | |
560 * @throws IOException | |
561 */ | |
562 public int zip(Deflater deflater,LinkedList<ByteBuffer> inputs, int inputIndex, LinkedList<ByteBuffer> outputs) throws IOException { | |
563 int len = 0; | |
564 ByteBuffer c1= ByteBuffer.allocate(INFLATE_BUFSIZE); | |
565 while(inputIndex < inputs.size() ) { | |
566 ByteBuffer b1 = inputs.get(inputIndex++); | |
567 deflater.setInput(b1.array(),b1.position(),b1.remaining()); | |
69 | 568 /** |
569 * If we finish() stream and reset() it, Deflater start new gzip stream, this makes continuous zlib reader unhappy. | |
570 * if we remove finish(), Deflater.deflate() never flushes its output. The original zlib deflate has flush flag. I'm pretty | |
571 * sure this a kind of bug of Java library. | |
572 */ | |
573 if (inputIndex==inputs.size()) | |
574 deflater.finish(); | |
575 int len1 = 0; | |
60 | 576 do { |
69 | 577 len1 = deflater.deflate(c1.array(),c1.position(),c1.remaining()); |
60 | 578 if (len1>0) { |
579 len += len1; | |
580 c1.position(c1.position()+len1); | |
581 if (c1.remaining()==0) { | |
582 c1.flip(); outputs.addLast(c1); | |
583 c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); | |
584 } | |
585 } | |
69 | 586 } while (len1 >0 || !deflater.needsInput()); // &&!deflater.finished()); |
60 | 587 } |
588 if (c1.position()!=0) { | |
589 c1.flip(); outputs.addLast(c1); | |
590 } | |
591 deflater.reset(); | |
592 return len; | |
593 } | |
594 | |
595 /** | |
596 * gunzip byte arrays | |
597 * @param inflater | |
598 * @param inputs | |
599 * byte data[] | |
600 * @param outputs | |
601 * byte data[] | |
602 *@return number of total bytes | |
603 * @throws IOException | |
604 */ | |
69 | 605 public int unzip(Inflater inflater, LinkedList<ByteBuffer> inputs, int inputIndex, LinkedList<ByteBuffer> outputs,int bufSize) |
60 | 606 throws DataFormatException { |
607 int len=0; | |
69 | 608 ByteBuffer buf = ByteBuffer.allocate(bufSize); |
60 | 609 while (inputIndex < inputs.size()) { |
610 ByteBuffer input = inputs.get(inputIndex++); | |
611 inflater.setInput(input.array(),input.position(),input.limit()); | |
69 | 612 // if (inputIndex==inputs.size()) if inflater/deflater has symmetry, we need this |
613 // inflater.end(); but this won't work | |
60 | 614 do { |
615 int len0 = inflater.inflate(buf.array(),buf.position(),buf.remaining()); | |
616 if (len0>0) { | |
617 buf.position(buf.position()+len0); | |
618 len += len0; | |
619 if (buf.remaining()==0) { | |
620 buf.flip(); | |
621 outputs.addLast(buf); | |
69 | 622 buf = ByteBuffer.allocate(bufSize); |
60 | 623 } |
624 } | |
625 } while (!inflater.needsInput()); | |
626 } | |
627 if (buf.position()!=0) { | |
628 buf.flip(); | |
629 outputs.addLast(buf); | |
630 } | |
631 return len; | |
632 } | |
633 | |
69 | 634 /** |
635 * send data to clients | |
636 * @param dataLen | |
637 * @throws IOException | |
638 * @throws DataFormatException | |
639 * | |
640 * Zlibed packet is compressed in context dependent way, that is, it have to send from the beginning. But this is | |
641 * impossible. So we have to compress it again for each clients. Separate deflater for each clients is necessary. | |
642 * | |
643 * Java's deflater does not support flush. This means to get the result, we have to finish the compression. Reseting | |
644 * start new compression, but it is not accepted well in zlib continuous reading. So we need new Encoding ZRLEE | |
645 * which reset decoder for each packet. ZRLEE can be invisible from user, but it have to be implemented in the clients. | |
646 * ZRLEE compression is not context dependent, so no recompression is necessary. | |
647 */ | |
60 | 648 void readSendData(int dataLen) throws IOException, DataFormatException { |
649 LinkedList<ByteBuffer>bufs = new LinkedList<ByteBuffer>(); | |
650 ByteBuffer header = ByteBuffer.allocate(16); | |
651 readFully(header.array(),0,16); | |
652 header.limit(16); | |
653 if (header.get(0)==RfbProto.FramebufferUpdate) { | |
654 int encoding = header.getInt(12); | |
69 | 655 if (encoding==RfbProto.EncodingZRLE||encoding==RfbProto.EncodingZlib) { // ZRLEE is already recompressed |
60 | 656 ByteBuffer len = ByteBuffer.allocate(4); |
657 readFully(len.array(),0,4); len.limit(4); | |
658 ByteBuffer inputData = ByteBuffer.allocate(dataLen-20); | |
659 readFully(inputData.array(),0,inputData.capacity()); inputData.limit(dataLen-20); | |
660 LinkedList<ByteBuffer>inputs = new LinkedList<ByteBuffer>(); | |
661 inputs.add(inputData); | |
69 | 662 |
663 header.putInt(12, RfbProto.EncodingZRLEE); // means recompress every time | |
664 // using new Deflecter every time is incompatible with the protocol, clients have to be modified. | |
665 Deflater nDeflater = deflater; // new Deflater(); | |
666 LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>(); | |
667 unzip(inflater, inputs, 0 , out, INFLATE_BUFSIZE); | |
668 // dump32(inputs); | |
669 int len2 = zip(nDeflater, out, 0, bufs); | |
670 ByteBuffer blen = ByteBuffer.allocate(4); blen.putInt(len2); blen.flip(); | |
671 bufs.addFirst(blen); | |
672 | |
60 | 673 bufs.addFirst(header); |
674 multicastqueue.put(bufs); | |
675 is.reset(); | |
676 return ; | |
677 } | |
678 } | |
679 bufs.add(header); | |
680 if (dataLen>16) { | |
681 ByteBuffer b = ByteBuffer.allocate(dataLen-16); | |
682 readFully(b.array(),0,dataLen-16); b.limit(dataLen-16); | |
683 bufs.add(b); | |
684 } | |
685 multicastqueue.put(bufs); | |
686 is.reset(); | |
687 | |
688 // It may be compressed. We can inflate here to avoid repeating clients decompressing here, | |
689 // but it may generate too many large data. It is better to do it in each client. | |
690 // But we have do inflation for all input data, so we have to do it here. | |
691 } | |
45 | 692 |
54 | 693 void newClient(AcceptThread acceptThread, final Socket newCli, |
45 | 694 final OutputStream os, final InputStream is) throws IOException { |
695 // createBimgFlag = true; | |
696 // rfb.addSockTmp(newCli); | |
697 // addSock(newCli); | |
69 | 698 final int myId = clients; |
60 | 699 final Client <LinkedList<ByteBuffer>> c = multicastqueue.newClient(); |
69 | 700 final AtomicInteger writerRunning = new AtomicInteger(); |
701 writerRunning.set(1); | |
702 /** | |
703 * Timeout thread. If a client is suspended, it has top of queue indefinitely, which caused memory | |
704 * overflow. After the timeout, we poll the queue and discard it. Start long wait if writer is running. | |
705 */ | |
706 final Runnable timer = new Runnable() { | |
707 public void run() { | |
708 int count = 0; | |
709 for(;;) { | |
710 long timeout = 30000/8; | |
711 try { | |
712 synchronized(this) { | |
713 int state,flag; | |
714 writerRunning.set(0); | |
715 wait(timeout); | |
716 flag = 0; | |
717 while((state=writerRunning.get())==0) { | |
718 c.poll(); // discard, should be timeout | |
719 count++; | |
720 if (flag==0) { | |
721 System.out.println("Discarding "+myId + " count="+ count); flag = 1; | |
722 } | |
723 wait(10); // if this is too short, writer cannot take the poll, if this is too long, memory will overflow... | |
724 } | |
725 if (flag==1) System.out.println("Resuming "+myId + " count="+count); | |
726 if (state!=1) { | |
727 System.out.println("Client died "+myId); | |
728 break; | |
729 } | |
730 } | |
731 } catch (InterruptedException e) { | |
732 } | |
733 } | |
734 } | |
735 }; | |
736 new Thread(timer).start(); | |
737 /** | |
738 * discard all incoming from clients | |
739 */ | |
60 | 740 final Runnable reader = new Runnable() { |
45 | 741 public void run() { |
60 | 742 byte b[] = new byte[4096]; |
743 for(;;) { | |
744 try { | |
745 int c = is.read(b); | |
746 if (c<=0) throw new IOException(); | |
69 | 747 // System.out.println("client read "+c); |
60 | 748 } catch (IOException e) { |
749 try { | |
69 | 750 writerRunning.set(2); |
60 | 751 os.close(); |
752 is.close(); | |
753 } catch (IOException e1) { | |
754 } | |
755 return; | |
756 } | |
757 } | |
758 } | |
759 }; | |
69 | 760 /** |
761 * send packets to a client | |
762 */ | |
60 | 763 Runnable sender = new Runnable() { |
764 public void run() { | |
69 | 765 writerRunning.set(1); |
45 | 766 try { |
60 | 767 /** |
768 * initial connection of RFB protocol | |
769 */ | |
45 | 770 sendRfbVersion(os); |
771 readVersionMsg(is); | |
772 sendSecurityType(os); | |
773 readSecType(is); | |
774 sendSecResult(os); | |
775 readClientInit(is); | |
776 sendInitData(os); | |
69 | 777 new Thread(reader).start(); // discard incoming packet here after. |
45 | 778 for (;;) { |
60 | 779 LinkedList<ByteBuffer> bufs = c.poll(); |
780 int inputIndex = 0; | |
69 | 781 ByteBuffer header = bufs.get(inputIndex); |
60 | 782 if (header==null) continue; |
783 if (header.get(0)==RfbProto.FramebufferUpdate) { | |
69 | 784 // System.out.println("client "+ myId); |
60 | 785 } |
69 | 786 writeToClient(os, bufs, inputIndex); |
787 writerRunning.set(1); // yes my client is awaking. | |
45 | 788 } |
789 } catch (IOException e) { | |
60 | 790 try { |
69 | 791 writerRunning.set(2); |
60 | 792 os.close(); |
793 } catch (IOException e1) { | |
81 | 794 /* if socket closed cliList.remove(newCli); */ |
60 | 795 } |
45 | 796 } |
797 } | |
69 | 798 |
799 public void writeToClient(final OutputStream os, | |
800 LinkedList<ByteBuffer> bufs, int inputIndex) | |
801 throws IOException { | |
802 while(inputIndex < bufs.size()) { | |
803 ByteBuffer b = bufs.get(inputIndex++); | |
804 os.write(b.array(), b.position(), b.limit()); | |
805 } | |
806 os.flush(); | |
807 } | |
45 | 808 }; |
60 | 809 clients++; |
45 | 810 new Thread(sender).start(); |
811 | |
812 } | |
60 | 813 |
69 | 814 |
815 public void dump32(LinkedList<ByteBuffer>bufs) { | |
816 int len =0; | |
817 for(ByteBuffer b: bufs) len += b.remaining(); | |
818 ByteBuffer top = bufs.getFirst(); | |
819 ByteBuffer end = bufs.getLast(); | |
820 System.err.println("length: "+len); | |
821 System.err.print("head 0: "); | |
822 for(int i = 0; i<16 && i < top.remaining(); i++) { | |
823 System.err.print(" "+ top.get(i)); | |
824 } | |
825 System.err.print("tail 0: "); | |
826 for(int i = 0; i<16 && i < end.remaining(); i++) { | |
827 System.err.print(" "+end.get(i)); | |
828 } | |
829 System.err.println(); | |
830 } | |
831 | |
832 private Iterable<Byte> byteBufferIterator(final LinkedList<ByteBuffer> in) { | |
833 return new Iterable<Byte>() { | |
834 public Iterator<Byte> iterator() { | |
835 return new Iterator<Byte>() { | |
836 int bytes = 0; | |
837 int buffers = 0; | |
838 public boolean hasNext() { | |
839 for(;;) { | |
840 if (buffers>=in.size()) return false; | |
841 ByteBuffer b = in.get(buffers); | |
842 if (! (bytes<b.remaining())) { | |
843 buffers ++; bytes=0; | |
844 } else return true; | |
845 } | |
846 } | |
847 public Byte next() { | |
848 ByteBuffer bf =in.get(buffers); | |
849 byte b = bf.get(bytes++); | |
850 if (bf.remaining()<=bytes) { | |
851 buffers++; | |
852 bytes = 0; | |
853 } | |
854 // System.out.print(b); | |
855 return b; | |
856 } | |
857 public void remove() { | |
858 } | |
859 }; | |
860 } | |
861 }; | |
862 } | |
863 | |
45 | 864 } |
60 | 865 |
866 |