java自帶的java.net.和java.awt.robot. 的混合可以用于實現(xiàn)通過網(wǎng)絡對另一臺計算機的遠程控制,其中包括控制遠程計算機鼠標的動作和鍵盤的輸入,以及實時獲得遠程計算機屏幕的圖像。本文將用簡潔的語言和由淺入深的邏輯,教大家如何掌握這個技術(shù)。
首先先看一下效果圖:
遠程端計算機界面:
控制端計算機界面:
控制端輸入:
遠程端輸入:
一下開始詳細介紹遠程控制的技術(shù)思路。
首先兩臺計算機通過java.net的Socket來進行連接。
一端先打開一個ServerSocket,然后另外一端用socket進行連接。
服務器端
應該設(shè)置一個ServerSocket,并且初始化需要用到的輸入輸出流:
1
2
3
4
5
6
7
8
|
public static void OpenServer() throws IOException, ClassNotFoundException{ System.out.println( "ServerStart....." ); ServerSocket server = new ServerSocket( 7777 ); socket = server.accept(); System.out.println( "連接上...\n" +socket); OIS = new ObjectInputStream(socket.getInputStream()); OOS= new ObjectOutputStream(socket.getOutputStream()); } |
客戶機端
應該用socket去連接服務器,并且初始化輸入輸出流:
1
2
3
4
5
6
7
8
9
10
|
public static void StartConnection(String IP, int port) throws UnknownHostException, IOException, AWTException{ socket = new Socket( "192.168.0.106" , 7777 ); if (socket.isConnected()){ System.out.println( "socket connected..." +socket); } OOS = new ObjectOutputStream(socket.getOutputStream()); OIS = new ObjectInputStream(socket.getInputStream()); } |
這樣兩臺計算機就鏈接在一起并且可以通過流(InputStream和OutputStream)來交換數(shù)據(jù)了
接下來大家可以想一想,要實現(xiàn)遠程控制的兩臺計算機需要交換什么信息呢?首先被控制端需要不斷向控制端提供截取的屏幕圖像(這個我們將會用java.awt.robot來實現(xiàn)),然后鼠標和鍵盤根據(jù)控制端傳來的事件(inputEvent)來做出相同的操作(用robot來實現(xiàn))。然后控制端當然首先要接收被控制端傳來的圖像并且反映到一個面板上(pane),然后監(jiān)聽本機上鍵盤鼠標的動作再傳給被控制端的主機(我們通過在面板pane上設(shè)置一個監(jiān)聽器listener來實現(xiàn))
這里遇到的一個問題就是用于傳送的圖片無論是用image還是用bufferedImage都是不可串行化的。所以不能用I/OStream進行傳送,所以為了解決這個問題,我們需要把圖像數(shù)據(jù)封裝在一個類里面并implements Serializable接口
圖像類如下:
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
|
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Serializable; public class Message implements Serializable { private static final long serialVersionUID = 1L; private String fileName; // 文件名稱 private long fileLength; // 文件長度 private byte [] fileContent; // 文件內(nèi)容 public Message(){ } public Message(String filePath) throws IOException{ File file = new File(filePath); this .fileLength=file.length(); this .fileName=file.getName(); FileInputStream FIS = new FileInputStream(filePath); byte [] bytes = new byte [( int )fileLength]; FIS.read(bytes, 0 ,( int )fileLength); this .fileContent=bytes; } public String getFileName() { return fileName;} public void setFileName(String fileName) { this .fileName = fileName;} public long getFileLength() { return fileLength; } public void setFileLength( long fileLength) { this .fileLength = fileLength;} public byte [] getFileContent() { return fileContent;} public void setFileContent( byte [] fileContent) { this .fileContent = fileContent;} } |
這樣就可以實現(xiàn)圖像通過ObjectInputStream和ObjectOutputStream的串行化傳播了
了解了以上基礎(chǔ)之后首先我們要完成控制端的UI界面設(shè)置,圖片接收,和鍵盤鼠標動作監(jiān)聽:
首先是設(shè)置接收圖片:
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
|
public static void reveivePic() throws ClassNotFoundException, IOException{ Message g = (Message)OIS.readObject(); FileOutputStream FOS = new FileOutputStream( "D:\\OUT\\" +g.getFileName()); FOS.write(g.getFileContent(), 0 ,( int )g.getFileLength()); FOS.flush(); FileInputStream FIS= new FileInputStream( "D:\\OUT\\" +g.getFileName()); BufferedImage BI = ImageIO.read(FIS); IIC= new ImageIcon(BI); Image img = IIC.getImage(); Toolkit tk = Toolkit.getDefaultToolkit() ; Dimension d =tk.getScreenSize(); int w = d.width; int h =d.height; BufferedImage bi = resize(img, 800 , 600 ); imag_lab.setIcon( new ImageIcon(bi)); imag_lab.repaint(); //銷掉以前畫的背景 } private static BufferedImage resize(Image img, int newW, int newH) { int w = img.getWidth( null ); int h = img.getHeight( null ); BufferedImage dimg = new BufferedImage(newW, newH,BufferedImage.TYPE_INT_BGR); Graphics2D g = dimg.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(img, 0 , 0 , newW, newH, 0 , 0 , w, h, null ); g.dispose(); return dimg; } |
這樣接收了來自O(shè)bjectInputStream的Message類之后就可以把圖片重新設(shè)置到面板pane的大小然后展示出來
下一步就是設(shè)置面板屬性和監(jiān)聽器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public static void showUI(){ //控制臺標題 JFrame jf = new JFrame( "控制臺" );setListener(jf); //控制臺大小 jf.setSize( 500 , 400 ); //imag_lab用于存放畫面 imag_lab = new JLabel(); jf.add(imag_lab); //設(shè)置控制臺可見 jf.setVisible( true ); //控制臺置頂 jf.setAlwaysOnTop( true ); jf.setResizable( true ); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } |
監(jiān)聽器:
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
|
public static void setListener( JFrame frame){ //panel設(shè)置監(jiān)聽器 frame.addKeyListener( new KeyAdapter(){ public void keyPressed(KeyEvent e) { sendEventObject(e); } @Override public void keyReleased(KeyEvent e) { sendEventObject(e); } @Override public void keyTyped(KeyEvent e) { } }); frame.addMouseWheelListener( new MouseWheelListener(){ public void mouseWheelMoved(MouseWheelEvent e) { sendEventObject(e); } }); frame.addMouseMotionListener( new MouseMotionListener(){ public void mouseDragged(MouseEvent e) { sendEventObject(e); } public void mouseMoved(MouseEvent e) { sendEventObject(e); } }); frame.addMouseListener( new MouseListener(){ public void mouseClicked(MouseEvent e) { sendEventObject(e); } public void mouseEntered(MouseEvent e) { sendEventObject(e); } public void mouseExited(MouseEvent e) { sendEventObject(e); } public void mousePressed(MouseEvent e) { sendEventObject(e); } public void mouseReleased(MouseEvent e) { sendEventObject(e); } }); } private static void sendEventObject(InputEvent event){ try { System.out.println( "send" ); OOS.writeObject(event); OOS.flush(); } catch (Exception ef){ ef.printStackTrace(); } |
以上就完成了控制端。
接下來我們將構(gòu)建被控制端:
被控制端需要使用robot來截圖并發(fā)送,而且需要寫一個方法來對接收到的InputEvent進行反應
首先是截圖和發(fā)送:
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
|
public static void CapturePic() throws AWTException, IOException{ robot= new Robot(); Message msg = null ; Toolkit tk = java.awt.Toolkit.getDefaultToolkit(); java.awt.Dimension dm =tk.getScreenSize(); java.awt.Robot robot = new java.awt.Robot(); for ( int i = 0 ; i < 50 ; i++) { //截取指定大小的屏幕區(qū)域 Rectangle rec = new Rectangle( 0 , 0 , ( int ) dm.getWidth(), ( int ) dm .getHeight()); BufferedImage bimage = robot.createScreenCapture(rec); //將圖片保存到文件中 String filePath = "D:\\OUT\\screenshot" +i+ ".jpeg" ; FileOutputStream fops = new FileOutputStream(filePath); javax.imageio.ImageIO.write(bimage, "jpeg" , fops); fops.flush(); fops.close(); msg = new Message(filePath); System.out.println(msg.getFileName()); System.out.println( "send" ); OOS.writeObject(msg); OOS.flush(); } } |
注意到這段代碼中使用了D:\OUT\目錄作為臨時文件的存放地方,讀者使用這個代碼的時候需要自己設(shè)置臨時文檔的存放
然后實現(xiàn)robot對于接收到的InputEvent指令進行操作:
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
|
public void action() throws AWTException, ClassNotFoundException, IOException{ Robot robot= new Robot(); while ( true ){ InputEvent e =(InputEvent)OIS.readObject(); if (e!= null ){ handleEvents(robot,e);} } } public static void handleEvents(Robot action,InputEvent event){ MouseEvent mevent = null ; //鼠標事件 MouseWheelEvent mwevent = null ; //鼠標滾動事件 KeyEvent kevent = null ; //鍵盤事件 int mousebuttonmask = - 100 ; //鼠標按鍵 switch (event.getID()){ case MouseEvent.MOUSE_MOVED : //鼠標移動 mevent = ( MouseEvent )event ; action.mouseMove( mevent.getX() , mevent.getY() ); break ; case MouseEvent.MOUSE_PRESSED : //鼠標鍵按下 mevent = ( MouseEvent ) event; action.mouseMove( mevent.getX() , mevent.getY() ); mousebuttonmask = getMouseClick(mevent.getButton() ); if (mousebuttonmask != - 100 ) action.mousePress(mousebuttonmask); break ; case MouseEvent.MOUSE_RELEASED : //鼠標鍵松開 mevent = ( MouseEvent ) event; action.mouseMove( mevent.getX() , mevent.getY() ); mousebuttonmask = getMouseClick( mevent.getButton() ); //取得鼠標按鍵 if (mousebuttonmask != - 100 ) action.mouseRelease( mousebuttonmask ); break ; case MouseEvent.MOUSE_WHEEL : //鼠標滾動 mwevent = ( MouseWheelEvent ) event ; action.mouseWheel(mwevent.getWheelRotation()); break ; case MouseEvent.MOUSE_DRAGGED : //鼠標拖拽 mevent = ( MouseEvent ) event ; action.mouseMove( mevent.getX(), mevent.getY() ); break ; case KeyEvent.KEY_PRESSED : //按鍵 kevent = ( KeyEvent ) event; action.keyPress( kevent.getKeyCode() ); break ; case KeyEvent.KEY_RELEASED : //松鍵 kevent= ( KeyEvent ) event ; action.keyRelease( kevent.getKeyCode() ); break ; default : break ; } } private static int getMouseClick( int button) { //取得鼠標按鍵 if (button == MouseEvent.BUTTON1) //左鍵 ,中間鍵為BUTTON2 return InputEvent.BUTTON1_MASK; if (button == MouseEvent.BUTTON3) //右鍵 return InputEvent.BUTTON3_MASK; return - 100 ; } |
整個程序到這里就可以結(jié)束了。上面的程序并沒有實現(xiàn)對機器人類線程的封裝。完整可以用的代碼可以在以下站內(nèi)資源處下載我的資源:Java-RomoteControl.rar
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。