說到屏幕監(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(
new
File(
"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)):