說到屏幕監(jiān)控系統(tǒng),有教師斷和學(xué)生端,教師端就是Server端,學(xué)生端就做Client端。系統(tǒng)里比較有趣的一個(gè)地方應(yīng)該算是屏幕廣播與屏幕監(jiān)控吧,其余什么點(diǎn)名簽到,鎖屏,定時(shí)關(guān)機(jī)的,就相對(duì)來說簡(jiǎn)單點(diǎn)。
屏幕廣播,在功能實(shí)現(xiàn)上面,說白了,就是教師端的機(jī)器不斷截取屏幕信息,以圖片的形式發(fā)送到每一個(gè)學(xué)生端的電腦上面,由此學(xué)生能夠看見老師在電腦上的操作,這就是所謂的屏幕廣播。
這里面有個(gè)麻煩的地方,就是截取屏幕圖片的時(shí)候,是沒有鼠標(biāo)信息。不過有兩種解決辦法:
①在發(fā)送截圖信息時(shí),在圖片上繪制一個(gè)鼠標(biāo),這樣在學(xué)生端就會(huì)有兩個(gè)鼠標(biāo),學(xué)生端可以移動(dòng)自己電腦上的鼠標(biāo)。
②發(fā)送教師端的鼠標(biāo)坐標(biāo)到學(xué)生端上,學(xué)生端的電腦鼠標(biāo)根據(jù)坐標(biāo)信息實(shí)時(shí)移動(dòng),這里其實(shí)是涉及到控制的功能了,學(xué)生端就不能移動(dòng)鼠標(biāo)了。
屏幕監(jiān)控相對(duì)棘手點(diǎn),其實(shí)這是這包含倆功能:①教師監(jiān)控所有學(xué)生電腦屏幕的功能;②教師控制某一個(gè)學(xué)生的電腦; 因?yàn)樯婕暗讲l(fā),每個(gè)client都要實(shí)時(shí)的把屏幕信息發(fā)到教師端上,會(huì)有點(diǎn)麻煩,不過還是可以實(shí)現(xiàn)。
下面是不帶鼠標(biāo)的屏幕共享功能,比較簡(jiǎn)單,有待完善,不過可以作為一個(gè)工具類在后面集成使用。后面補(bǔ)充了把鼠標(biāo)畫上去的代碼,只需要3行。
首先是教師端Server:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
package Test;import java.awt.Dimension;import java.awt.Rectangle;import java.awt.Robot;import java.awt.Toolkit;import java.awt.image.BufferedImage;import java.io.DataOutputStream;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.zip.ZipEntry;import java.util.zip.ZipOutputStream;import javax.imageio.ImageIO;/* * ly 2014-11-20 * 該類實(shí)時(shí)發(fā)送截屏消失,多線程實(shí)現(xiàn),不包含鼠標(biāo)信息,且沒有做對(duì)每個(gè)Client做優(yōu)化處理 */public class SendScreenImg extends Thread{ public static int SERVERPORT=8000; private ServerSocket serverSocket; private Robot robot; public Dimension screen; public Rectangle rect ; private Socket socket; public static void main(String args[]) { new SendScreenImg(SERVERPORT).start(); } //構(gòu)造方法 開啟套接字連接 機(jī)器人robot 獲取屏幕大小 public SendScreenImg(int SERVERPORT) { try { serverSocket = new ServerSocket(SERVERPORT); serverSocket.setSoTimeout(864000000); robot = new Robot(); } catch (Exception e) { e.printStackTrace(); } screen = Toolkit.getDefaultToolkit().getScreenSize(); //獲取主屏幕的大小 rect = new Rectangle(screen); //構(gòu)造屏幕大小的矩形 } @Override public void run() { //實(shí)時(shí)等待接收截屏消息 while(true) { try{ socket = serverSocket.accept(); System.out.println("學(xué)生端口已經(jīng)連接"); ZipOutputStream zip = new ZipOutputStream(new DataOutputStream(socket.getOutputStream())); zip.setLevel(9); //設(shè)置壓縮級(jí)別 BufferedImage img = robot.createScreenCapture(rect); zip.putNextEntry(new ZipEntry("test.jpg")); ImageIO.write(img, "jpg", zip); if(zip!=null)zip.close(); System.out.println("Client正在實(shí)時(shí)連接"); } catch (IOException ioe) { System.out.println("連接斷開"); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) {e.printStackTrace();} } } } }} |
然后是學(xué)生端Client:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
package Test;import java.awt.Frame;import java.awt.Image;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.io.DataInputStream;import java.io.IOException;import java.net.Socket;import java.util.concurrent.TimeUnit;import java.util.zip.ZipInputStream;import javax.imageio.ImageIO;import javax.swing.ImageIcon;import javax.swing.JFrame;import javax.swing.JLabel;/* * ly 2014-11-20 * 該類用于接收教師端的屏幕信息,不包括鼠標(biāo),待優(yōu)化 */public class ReceiveImages extends Thread{ public BorderInit frame ; public Socket socket; public String IP; public static void main(String[] args){ new ReceiveImages(new BorderInit(), "127.0.0.1").start(); } public ReceiveImages(BorderInit frame,String IP) { this.frame = frame; this.IP=IP; } public void run() { while(frame.getFlag()){ try { socket = new Socket(IP,8000); DataInputStream ImgInput = new DataInputStream(socket.getInputStream()); ZipInputStream imgZip = new ZipInputStream(ImgInput); imgZip.getNextEntry(); //到Zip文件流的開始處 Image img = ImageIO.read(imgZip); //按照字節(jié)讀取Zip圖片流里面的圖片 frame.jlbImg.setIcon(new ImageIcon(img)); System.out.println("連接第"+(System.currentTimeMillis()/1000)%24%60+"秒"); frame.validate(); TimeUnit.MILLISECONDS.sleep(50);// 接收?qǐng)D片間隔時(shí)間 imgZip.close(); } catch (IOException | InterruptedException e) { System.out.println("連接斷開"); }finally{ try { socket.close(); } catch (IOException e) {} } } }}//Client端窗口輔助類,專門用來顯示從教師端收到的屏幕信息class BorderInit extends JFrame{ private static final long serialVersionUID = 1L; public JLabel jlbImg; private boolean flag; public boolean getFlag(){ return this.flag; } public BorderInit() { this.flag=true; this.jlbImg = new JLabel(); this.setTitle("遠(yuǎn)程監(jiān)控--IP:" + "--主題:" ); this.setSize(400, 400); //this.setUndecorated(true); //全屏顯示,測(cè)試時(shí)最好注釋掉 //this.setAlwaysOnTop(true); //顯示窗口始終在最前面 this.add(jlbImg); this.setLocationRelativeTo(null); this.setExtendedState(Frame.MAXIMIZED_BOTH); this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); this.setVisible(true); this.validate(); //窗口關(guān)閉事件 this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { flag=false; BorderInit.this.dispose(); System.out.println("窗體關(guān)閉"); System.gc(); //垃圾回收 } }); }} |
很晚了,從未成品中抽取了這么個(gè)小功能,因?yàn)榇饝?yīng)某位童鞋給鏈接的,距離成品還有很多要寫,后續(xù)補(bǔ)上吧。
后續(xù)補(bǔ)充:
把鼠標(biāo)畫到屏幕截圖的方法,在SendScreenImg類中 BufferedImage img = robot.createScreenCapture(rect);后面,添加下面三行代碼:
1
2
3BufferedImage cursor= ImageIO.read(newFile("img/cursor.png"));//把鼠標(biāo)加載到緩存中Point p= MouseInfo.getPointerInfo().getLocation();//獲取鼠標(biāo)坐標(biāo)img.createGraphics().drawImage(cursor, p.x, p.y,null);//在圖片畫上鼠標(biāo)嗯,記得在工程下面建立一個(gè)img文件夾,在文件夾中放置名為cursor.png的鼠標(biāo)圖片,一定要png格式的,要求底色透明,可以去下載,也可以自己p圖。
給張運(yùn)行時(shí)候的截圖(按理說應(yīng)該是有3個(gè)鼠標(biāo)的,但是QQ截圖也是沒有的鼠標(biāo)): 