<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

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

    Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視

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

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

    將當(dāng)前屏幕顯示內(nèi)容捕捉為圖片

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

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

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

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

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

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

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

    將生成的字節(jié)數(shù)組進行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();

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

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

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

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


    遠(yuǎn)程控制主機接收消息
     1//先要判斷消息的類型
     2DataInputStream reader = new DataInputStream(socket.getInputStream());
     3int msgSymbol = reader.read();
     4//還記得這個SEND_IMAGE_SYMBOL嗎
     5if (msgSymbol == SEND_IMAGE_SYMBOL)
     6{
     7    //哦,是要發(fā)送圖片啊。讓我看看你的圖片有多大
     8    int msgSize = reader.readInt();
     9
    10    //暈,你網(wǎng)速好也不用發(fā)這么大吧,我一次接收不完的,不過幸好我有準(zhǔn)備
    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    //這是非常關(guān)鍵的,圖片太大時一次性是讀不完的,一定要使用緩沖重復(fù)讀取。
    28
    29    //人家給我發(fā)送的消息是圖片,怎么把字節(jié)數(shù)組還原成圖片呢?
    30    ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
    31    ZipInputStream zis = new ZipInputStream(bis);
    32    //讀取壓縮的數(shù)據(jù)內(nèi)容。
    33    ZipEntry ze = zis.getNextEntry();
    34    BufferedImage bi = ImageIO.read(zis);
    35    //或者BufferedImage bi = JPEGCodec.createJPEGDecoder(zis).decodeAsBufferedImage();
    36    //上面兩行代碼的差別已經(jīng)說過了
    37    //另外,如果在發(fā)送的時候你沒有進行壓縮,把上面的zis換成bis就行。
    38
    39
    40    //有了BufferedImage對象,剩下的就是把它顯示出來了進行遠(yuǎn)程"偷窺",任何一個支持圖片顯示的swing組件你都以用,我在實驗中用的是JPanel,一個簡單又支持雙緩沖的組件。
    41}


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

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


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


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

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

    評論

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視[未登錄]  回復(fù)  更多評論   

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

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

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

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

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

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

    以前嘗試使用MINA寫一個服務(wù)器端程序,但是沒有想到如何區(qū)分不同的用戶發(fā)來的消息,超時的狀態(tài)和權(quán)限
    2010-02-23 14:31 | lordz

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

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

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

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

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

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

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

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

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

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

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

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

    # re: Java實現(xiàn)遠(yuǎn)程屏幕監(jiān)視  回復(fù)  更多評論   

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

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

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲日韩AV一区二区三区四区| 黄色永久免费网站| 亚洲色大情网站www| 久久亚洲精品中文字幕无码| 免费萌白酱国产一区二区| 国产精品免费观看| 青青草原1769久久免费播放| 国产免费牲交视频免费播放| 99亚洲乱人伦aⅴ精品| 亚洲色www永久网站| 亚洲精品免费在线视频| 亚洲情综合五月天| 久久久亚洲精品蜜桃臀| 亚洲成AV人在线观看网址| 日韩成人免费在线| 成人无遮挡裸免费视频在线观看 | 亚洲国产主播精品极品网红| 成人人免费夜夜视频观看| 精品女同一区二区三区免费站| 免费在线观影网站| 成全视成人免费观看在线看| 九九全国免费视频| 人禽伦免费交视频播放| 免费看美女午夜大片| 国产亚洲精品仙踪林在线播放| 亚洲国产精品美女久久久久| 亚洲欧美成aⅴ人在线观看| 亚洲综合偷自成人网第页色| 亚洲日本乱码一区二区在线二产线 | 亚洲精品视频在线| 亚洲成a人片77777老司机| 久久亚洲综合色一区二区三区 | 午夜网站在线观看免费完整高清观看| 抽搐一进一出gif免费视频| 一区二区三区免费精品视频| 黄床大片免费30分钟国产精品| 九九九国产精品成人免费视频| 九九久久国产精品免费热6| 亚洲第一视频在线观看免费| 国产福利免费视频 | 亚洲成A人片77777国产|