隨筆 - 4, 文章 - 1, 評論 - 22, 引用 - 0
          數據加載中……

          Java實現遠程屏幕監視

          遠程屏幕監視使得控制方可以在遠程主機上監視其它一臺機器,其主要實現原理就是將被控制機器的屏幕作為圖片傳送給監視方,在Java中要實現遠程屏幕監視,主要解決以下幾個問題即可:
          1:將當前屏幕的顯示內容捕捉為圖片
          2:將捕捉的圖片發送到遠程控制主機
          3:遠程控制主機接收到在本地顯示
          4:利用多線程重復上面三步達到實時更新

          說起來怎么這么簡單啊,今天試著做了一下遠程屏幕監視的實驗,發現還真不是這么簡單的,把我的心得總結出來共享一下,希望對你有用。

          將當前屏幕顯示內容捕捉為圖片

          1Robot robot = new Robot();
          2//要捕捉的屏幕顯示范圍,下面以全屏示例說明
          3Rectangle rect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
          4BufferedImage bm = robot.createScreenCapture(rect);

          通過上面幾行代碼就把屏幕的當前顯示內容保存為內存中的BufferedImage對象,這個確實簡單.

          將捕捉的圖片發送到遠程控制主機
          要達到實時監控,怎么保證發送效率,在網絡狀況不良好時怎么保證發送時使用帶寬
          因為要不停地往控制機上發送圖片,所以傳送的圖片不能太大,否則會影響實時性,當網絡狀況不好時,占用帶寬過多則更加會給實時性帶來嚴峻的考驗,解決的方法有兩個:
          1:使用jpg格式的圖片進行傳輸。
          jpg是一種支持高度壓縮技術的圖片格式,它所存儲的信息不包含透明度,同等質量的情形下相對來說比png,gif等格式的圖片要小很多,當然,文件大小是以圖像質量為代價的,如果你一味地追求壓縮后的大小,圖像質量就會受損了。我在實驗中使用大小為28394字節的png圖片經過jpg壓縮后大小僅剩5815字節(不是PS,整個過程全部使用Java完成)。
          2:將用圖片生成的字節數據先壓縮再傳送。
          這一步是仁者見仁,智者見智了,有人說沒有必要,jgp格式的圖片再壓縮也小不了多少。確實是這樣的,我在實驗中把5815字節大小的jpg經過zip壓縮后為大小變為5702,有點小作用,實際應用中壓縮與否就看你了(壓縮其它格式的圖片效果可能會明顯一點,我在實驗中把一個大小為883078字節的bmp圖片壓縮后大小僅為16849字節,很可觀,達到了52:1)。

          我能想到的就是這兩點了,歡迎各位仁智雙全的人補充。下面就是這兩點用到的Java技術,Java高手就直接跳過吧。

          使用Java將圖片處理成jpg格式
          1//outputstream就是要寫入處理后的jpg圖片的輸出流,要保存到文件的話就用FileOutputStream
          2JPEGCodec.createJPEGEncoder(outputstream).encode(bm);
          3ImageIO.write(bm, "jpg", outputstream);

          這兩種方法有什么差別呢?別的我不知道,就平均效率來說,第二種是第一種的2倍,我實驗中轉換了10次,使用的時間分別是125和250(單位是百分之一毫秒,機子有點慢的說).

          把圖片數據轉換為字節數組
          1ByteArrayOutputStream bos = new ByteArrayOutputStream();
          2JPEGCodec.createJPEGEncoder(bos).encode(bm);
          3// 上句也可以用 ImageIO.write(bm, "jpg", bos)實現
          4bos.flush();
          5byte[] data = bos.toByteArray();

          將生成的字節數組進行zip壓縮
          1ZipOutputStream zos = new ZipOutputStream(bos);
          2zos.setLevel(Deflater.BEST_COMPRESSION);
          3//下面我以ScreenCapture.jpg說明
          4zos.putNextEntry(new ZipEntry("ScreenCapture.jpg"));
          5zos.write(data);
          6zos.closeEntry();

          好了,這個時候就可以把字節數組發往監控機器了,如果你發了,你就知道,問題又來了(不會吧!)。
          1:既然是采用多線程發送多張圖片,那么對于一張圖片,接收方怎么知道你發完了呢?
          2:如果要發其它的數據,比如鼠標點擊等,接收方又怎么區分什么時候發的是圖片,什么時候發的是其它的……

          對于這兩點問題,最直接的解決方法是當數據發送完成后關閉發送字節的輸出流,第二次發送時重新建立連接(網上確實有人這樣肆無忌憚地做),這種方法采用不采用就看良心了(汗)。我采用的解決方法是,每次發送數據前都告訴接收方要發什么東西(解決問題2),同時告訴它我發了多少字節(解決問題1),接收方只要接收了這么多字節數,就表示本次發送完成,最后再發送真正要發送的內容(圖片等),說簡單點就是,發送的消息結構如下:
          【標識位 大小 消息】
          標識位:采用一個整型,其實是一個byte,占一個字節
          大小:一個整型,占四個字節
          消息:實際要發送的字節數組,長度就是字節數組的長度
          這樣接收方每次都是先讀取一個整數,判斷發送方是要發送什么消息,然后再判斷消息的大小,然后再接收指定大小的消息,最后完成本次發送轉入下一次接收工作。

          采用Socket的方式進行消息的發送
          1DataOutputStream dos = new DataOutputStream(client.getOutputStream());
          2//SEND_IMAGE_SYMBOL是一個標識位,你隨便定義,只要保證能與其它標識位區分就行
          3dos.write(SEND_IMAGE_SYMBOL);
          4
          5dos.writeInt(data.length);
          6dos.write(data);
          7dos.flush();

          啊,真不容易,終于發送出去了!不知道那邊接收到了沒有?那現在就去追蹤報道吧。


          遠程控制主機接收消息
           1//先要判斷消息的類型
           2DataInputStream reader = new DataInputStream(socket.getInputStream());
           3int msgSymbol = reader.read();
           4//還記得這個SEND_IMAGE_SYMBOL嗎
           5if (msgSymbol == SEND_IMAGE_SYMBOL)
           6{
           7    //哦,是要發送圖片啊。讓我看看你的圖片有多大
           8    int msgSize = reader.readInt();
           9
          10    //暈,你網速好也不用發這么大吧,我一次接收不完的,不過幸好我有準備
          11    byte[] buffer = new byte[msgSize];
          12    int length = 0;
          13
          14    while (length < msgSize)
          15    {
          16        int readSize = reader.read(buffer, length, msgSize - length);
          17
          18        if (readSize > 0)
          19        {
          20            length = length + readSize;
          21        }

          22        else
          23        {
          24            break;
          25        }

          26    }

          27    //這是非常關鍵的,圖片太大時一次性是讀不完的,一定要使用緩沖重復讀取。
          28
          29    //人家給我發送的消息是圖片,怎么把字節數組還原成圖片呢?
          30    ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
          31    ZipInputStream zis = new ZipInputStream(bis);
          32    //讀取壓縮的數據內容。
          33    ZipEntry ze = zis.getNextEntry();
          34    BufferedImage bi = ImageIO.read(zis);
          35    //或者BufferedImage bi = JPEGCodec.createJPEGDecoder(zis).decodeAsBufferedImage();
          36    //上面兩行代碼的差別已經說過了
          37    //另外,如果在發送的時候你沒有進行壓縮,把上面的zis換成bis就行。
          38
          39
          40    //有了BufferedImage對象,剩下的就是把它顯示出來了進行遠程"偷窺",任何一個支持圖片顯示的swing組件你都以用,我在實驗中用的是JPanel,一個簡單又支持雙緩沖的組件。
          41}


          最后,就是使用多線程重復上面的步驟進行實時監控了。有什么問題歡迎指正。

          本次實驗進行的還算成功,成功偷窺了自己的桌面。謝謝你堅持看完我這么爛的文章,向你致敬!


          下面是本次實驗的運行圖:


          本文所使用的源代碼:  源代碼下載

          posted on 2010-02-10 10:05 凱子 閱讀(3196) 評論(10)  編輯  收藏 所屬分類: Java

          評論

          # re: Java實現遠程屏幕監視[未登錄]  回復  更多評論   

          怎么把多線程的部分省略了?
          2010-02-10 13:50 | lazy

          # re: Java實現遠程屏幕監視  回復  更多評論   

          @lazy
          你只要把文章里面所說的發送方和接收方的行為都放到多線程里就可以了,下載源代碼看吧,呵呵。
          2010-02-10 14:35 | 凱子

          # re: Java實現遠程屏幕監視  回復  更多評論   

          不錯!
          2010-02-11 16:41 | 龍屠日

          # re: Java實現遠程屏幕監視  回復  更多評論   

          以前嘗試使用MINA寫一個服務器端程序,但是沒有想到如何區分不同的用戶發來的消息,超時的狀態和權限
          2010-02-23 14:31 | lordz

          # re: Java實現遠程屏幕監視  回復  更多評論   

          發送截圖可能在你本機上還能是“實時”
          拿到局域網或者不同網段的兩天機器上恐怕就到不到“實時”的效果了
          要想做這個,建議你看看VNC的java實現
          2010-02-24 12:26 | RogerX

          # re: Java實現遠程屏幕監視  回復  更多評論   

          再提一句
          ImageIO性能很差
          2010-02-24 12:27 | RogerX

          # re: Java實現遠程屏幕監視  回復  更多評論   

          @RogerX
          其實本機也達不到很好的“實時”效果,有時間好好研究一下VNC的源碼,謝謝。

          我也沒有打算做遠程控制,只是做個實驗,還是有不少收獲的。
          2010-02-25 14:39 | 凱子

          # re: Java實現遠程屏幕監視  回復  更多評論   

          @RogerX
          ImageIO的性能確實很差,不知道有沒有JPEGCodec.createJPEGEncoder性能更好的解決方案呢?

          兄弟你應該是個高手了,哈哈,以后多指教了!
          2010-02-25 14:41 | 凱子

          # re: Java實現遠程屏幕監視  回復  更多評論   

          大哥,箭頭形狀的鼠標 看不到呀。怎么辦?
          2010-02-25 17:06 | 飛熊

          # re: Java實現遠程屏幕監視  回復  更多評論   

          @飛熊
          把捕捉到的bufferedimage處理,在其上面繪制出鼠標再發給遠程機器,先找一張鼠標的圖片,當然,要先得到鼠標的位置,方法如下:
          PointerInfo pi = MouseInfo.getPointerInfo();
          Point point = pi.getLocation();

          這是我的解決方法,僅供參考,呵呵 !
          2010-02-28 23:21 | 凱子

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 洛扎县| 克东县| 榆中县| 临邑县| 柞水县| 维西| 金华市| 龙游县| 扎兰屯市| 阿坝| 霍邱县| 彭山县| 汽车| 鞍山市| 元谋县| 卫辉市| 赫章县| 深泽县| 中宁县| 滦南县| 板桥市| 仪征市| 竹山县| 宜昌市| 西乌珠穆沁旗| 浠水县| 云和县| 新乐市| 清原| 金川县| 静海县| 武陟县| 西盟| 通州市| 江城| 偃师市| 平度市| 大宁县| 苍梧县| 于田县| 沧州市|