上一篇讲到局域网内的远程控制,这一篇讲利用云服务器,实现真正远程控制。
云服务器转发的话呢,就需要S控制端,C被控制端,W转发 思路就是,C将截图发送给W,W转发给S,S解析显示。S将操作发送给W,W转发给C,C解析模拟操作。
先来张整体结构图:
不多说,直接贴代码: 按结构图来,先贴第一个类,全部是使用了大佬的代码,用于实现S端接收C传来的截图显示:
package com.wfg.imag import java.awt.Dimension; import java.awt.Image; import java.awt.Insets; import java.awt.Toolkit; import java.io.ObjectOutputStream import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.UIManager public class MyJframe extends JFrame{ private ObjectOutputStream ous; private static JLabel img_jLabel=new JLabel(); public MyJframe(String title,ObjectOutputStream ous){ super(title); this.ous=ous; // 获取屏幕的边界 Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration()); // 获取底部任务栏高度 int taskBarHeight = screenInsets.bottom; Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); double ratio = screenDimension.getWidth()/screenDimension.getHeight(); setSize((int)(screenDimension.getWidth()-taskBarHeight*ratio), (int)(screenDimension.getHeight()-taskBarHeight)); //设置打开窗口为屏幕大小 setVisible(true); setAlwaysOnTop(true); setDefaultCloseOperation(3); try { //显示方式 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { // } add(img_jLabel); } /* * type 1 原图显示 * 2 按照比例缩放 * 3 滚动显示 */ public static void setImgLabel(ImageIcon icon,int type) { switch (type) { case 1: break; case 2: showImagByRatio(icon); break; case 3: break; default: break; } } private static void showImagByRatio(ImageIcon icon) { int imgWidth=icon.getIconWidth();//获得图片宽度 int imgHeight=icon.getIconHeight();//获得图片高度 int conWidth=img_jLabel.getWidth();//得到组件宽度 int conHeight=img_jLabel.getHeight();//得到组件高度 int reImgWidth;//保存图片更改宽度后的值 int reImgHeight;//保存图片更改高度后的值 if(imgWidth/imgHeight>=conWidth/conHeight){ if(imgWidth>conWidth){ reImgWidth=conWidth; reImgHeight=imgHeight*reImgWidth/imgWidth; }else{ reImgWidth=imgWidth; reImgHeight=imgHeight; } }else{ if(imgWidth>conWidth){ reImgHeight=conHeight; reImgWidth=imgWidth*reImgHeight/imgHeight; }else{ reImgWidth=imgWidth; reImgHeight=imgHeight; } } icon=new ImageIcon(icon.getImage().getScaledInstance(reImgWidth,reImgHeight, Image.SCALE_DEFAULT)); img_jLabel.setIcon(icon); img_jLabel.repaint();//销掉以前画的背景 } }第二个类,用于实现键盘按键事件发送的实体类
package eventSeri; import java.io.Serializable; public class serEvent implements Serializable{ static final long serialVersionUID = 1L; int keyCode; int id; public int getId() { return id; } public void setId(int id) { this.id = id; } public serEvent() { } public serEvent(int id, int keyCode) { super(); this.keyCode = keyCode; this.id = id; } public int getKeyCode() { return keyCode; } public void setKeyCode(int keyCode) { this.keyCode = keyCode; } }第三个类,发送图片的线程,也全部是使用了大佬的代码
package yu.client; import java.awt.AWTException; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Robot; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.Serializable; import javax.imageio.ImageIO; /** * 发送图片的线程 * @author wfg */ public class CaptureThread extends Thread implements Serializable{ private DataOutputStream dataOutputStream; private Toolkit tk ; private Dimension dm ; private Rectangle rec; private Robot robot ; public CaptureThread(DataOutputStream dataOutputStream) throws AWTException { this.dataOutputStream = dataOutputStream; tk = Toolkit.getDefaultToolkit(); dm = tk.getScreenSize(); //根据屏幕设定图片的大小 rec = new Rectangle(0, 0, (int)dm.getWidth(), (int)dm.getHeight()); robot = new Robot(); } @Override public void run() { while(true){ byte[] data = createCature(); try { dataOutputStream.writeInt(data.length); dataOutputStream.write(data); dataOutputStream.flush(); try { Thread.sleep(1000/20); } catch (InterruptedException e) { } } catch (IOException e) { e.printStackTrace(); System.out.println("网络有问题,截屏失败"); } } } private byte[] createCature() { //获得一个屏幕的截图 BufferedImage bimage = robot.createScreenCapture(rec); 创建一段内存流 ByteArrayOutputStream byout = new ByteArrayOutputStream(); try { //将图片数据写入内存流中 ImageIO.write(bimage, "jpg", byout); } catch (IOException e) { e.printStackTrace(); System.out.println("截屏图片写入内存流中出现异常"); } return byout.toByteArray(); } }第四个类,C端模拟回放S端传来的鼠标键盘操作
package yu.client; import java.awt.AWTException; import java.awt.Robot; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import eventSeri.serEvent; public class EventReadThread extends Thread implements Serializable{ private ObjectInputStream objins; public EventReadThread(ObjectInputStream objins) { this.objins = objins; } @Override public void run() { while(true){ try { Object eventobj = objins.readObject(); try { InputEvent e = (InputEvent) eventobj; actionEvent(e); }catch (Exception e) { //说明是按键事件! serEvent e1 =(serEvent) eventobj; actionKeyEvent(e1); } } catch (IOException e) { } catch (ClassNotFoundException e) { } } } //回放鼠标事件的方法 private void actionEvent(InputEvent e){ Robot robot =null; try { robot = new Robot(); } catch (AWTException e1) { } if(e instanceof MouseEvent){ MouseEvent me = (MouseEvent)e; int type = me.getID(); if(type==MouseEvent.MOUSE_PRESSED){ //按下 robot.mousePress(getMouseClick(me.getButton())); } if(type==MouseEvent.MOUSE_RELEASED){ //放开 robot.mouseRelease(getMouseClick(me.getButton())); } if(type==MouseEvent.MOUSE_MOVED) { //移动 robot.mouseMove(me.getX(), me.getY()); } if(type==MouseEvent.MOUSE_DRAGGED) { //拖动 robot.mouseMove(me.getX(), me.getY()); } if(type==MouseEvent.MOUSE_WHEEL) { //滑轮滚动 robot.mouseWheel(getMouseClick(me.getButton())); } } } //回放键盘事件的方法 private void actionKeyEvent(serEvent e){ Robot robot =null; try { robot = new Robot(); } catch (AWTException e1) { } if(e.getId()==KeyEvent.KEY_PRESSED){ robot.keyPress(e.getKeyCode()); } if(e.getId()==KeyEvent.KEY_RELEASED){ robot.keyRelease(e.getKeyCode()); } } //根据发送事的Mouse事件对象,转变为通用的Mouse按键代码 private int getMouseClick(int button){ if(button==MouseEvent.BUTTON1){ return InputEvent.BUTTON1_MASK; } if(button==MouseEvent.BUTTON2){ return InputEvent.BUTTON2_MASK; } if(button==MouseEvent.BUTTON3){ return InputEvent.BUTTON3_MASK; } return -1; } }第五个:本地被控制的主方法
package yu.client; import java.awt.AWTException; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.io.Serializable; import java.net.Socket; import java.net.UnknownHostException; /** * 本地是被控制的 不停的发送图片和接收操作 * @author yjz * */ public class localbeikong implements Serializable { private ObjectInputStream objectInputStream; private OutputStream ous private void startbeikong() throws UnknownHostException, IOException, AWTException { Socket socket =new Socket("123.123.123.65", 9527); System.out.println("服务器连接成功!"); InputStream ins = socket.getInputStream(); //对象输入流 读取操作事件对象 objectInputStream= new ObjectInputStream(ins); ous=socket.getOutputStream(); //数据输出流,用以发送图片数据 1个int图片数据长度 图片的字节 DataOutputStream dous=new DataOutputStream(ous); //事件处理 EventReadThread eventReadThread = new EventReadThread(objectInputStream); eventReadThread.start(); //发送图片 CaptureThread captureThread = new CaptureThread(dous); captureThread.start(); } public static void main(String[] args) throws IOException, AWTException{ new localbeikong().startbeikong(); } }接下来的是控制端的代码:
package yu.client2; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.DataInputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.Socket; import javax.swing.ImageIcon; import com.wfg.imag.MyJframe; import eventSeri.serEvent; /** * 服务器来进行操作控制的 发送操作,接收图片并显示在页面上 * @author Administrator * */ public class serverControl extends Thread implements Serializable{ private ObjectOutputStream ous; private DataInputStream ins; private void showui(){ MyJframe jf = new MyJframe("dddd",ous); addListener(jf); //添加监听 jf.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent arg0) { System.out.println(arg0.getNewValue()); } }); } private void startKongzhi() throws IOException { Socket sc =new Socket("123.123.123.65", 9528);//去连接服务器 不停的发送图片和接收操作 System.out.println("服务器连接成功!"); //得到输入流,读取图片数据 ins=new DataInputStream(sc.getInputStream()); //得到输出流,发送事件对象 ous=new ObjectOutputStream(sc.getOutputStream()); //处理这两个流 } @Override public void run() { try{ //读取图片数据 while(true){ int len=ins.readInt();//图片长度 byte[] data=new byte[len]; ins.readFully(data); //将读到的数据生成为一个图标对象 ImageIcon ic=new ImageIcon(data); //放到界面上.加到标签上 MyJframe.setImgLabel( ic,2); } }catch(Exception ef){ ef.printStackTrace(); } } private void addListener(MyJframe jf ){ jf.addMouseListener(new MouseListener() { @Override public void mouseReleased(MouseEvent e) { // System.out.println("鼠标事件!"); sentEvent(e); } @Override public void mousePressed(MouseEvent e) { // System.out.println("鼠标事件!"); sentEvent(e); } @Override public void mouseExited(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseClicked(MouseEvent e) { } }); //鼠标移动事件 jf.addMouseMotionListener(new MouseMotionListener() { @Override public void mouseMoved(MouseEvent arg0) { // System.out.println("鼠标移动事件!"); sentEvent(arg0); } @Override public void mouseDragged(MouseEvent arg0) { // System.out.println("鼠标移动事件!"); sentEvent(arg0); } }); //鼠标滑轮滑动事件 jf.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseWheelMoved(MouseWheelEvent arg0) { // System.out.println("滑动事件!"); sentEvent(arg0); } }); //键盘事件 jf.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent arg0) { // System.out.println("发送键盘事件1:"); serEvent e1=new serEvent(arg0.getID(),arg0.getKeyCode()); try { ous.writeObject(e1); ous.flush(); } catch (IOException e) { e.printStackTrace(); } } @Override public void keyReleased(KeyEvent arg0) { serEvent e1=new serEvent(arg0.getID(),arg0.getKeyCode()); try { ous.writeObject(e1); ous.flush(); } catch (IOException e) { e.printStackTrace(); System.out.println("发送事件对象出现异常"); } } @Override public void keyPressed(KeyEvent arg0) { serEvent e1=new serEvent(arg0.getID(),arg0.getKeyCode()); try { ous.writeObject(e1); ous.flush(); } catch (IOException e) { e.printStackTrace(); } } }); } private void sentEvent(InputEvent e){ try { ous.writeObject(e); ous.flush(); } catch (IOException e1) { e1.printStackTrace(); System.out.println("发送事件对象出现异常"); } } public static void main(String[] args) throws IOException { serverControl serverControl1=new serverControl(); serverControl1.startKongzhi(); System.out.println("控制端连接成功!"); serverControl1.showui(); serverControl1.start(); } }接下来就是转发端W的代码,事件转发的线程,从S端接收,转发给C端:
package zhongjian; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * 事件转发 * @author yjz * */ public class EventThread extends Thread implements Serializable{ private ObjectInputStream ins; private ObjectOutputStream ous; public EventThread(ObjectInputStream objins,ObjectOutputStream ous) { this.ins = objins; this.ous = ous; } @Override public void run() { while(true){ try { Object eventobj = ins.readObject(); ous.writeObject(eventobj); ous.flush(); } catch (IOException e) { } catch (ClassNotFoundException e) { } } } }图片转发线程:
package zhongjian; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.Serializable; /** * 转发图片的线程 * @author yjz * */ public class picThread extends Thread implements Serializable{ private DataOutputStream douts; private DataInputStream dins; public picThread(DataOutputStream dataOutputStream,DataInputStream dins){ this.douts = dataOutputStream; this.dins=dins; } @Override public void run() { try{ //读取图片数据 while(true){ int len=dins.readInt();//图片长度 byte[] data=new byte[len]; dins.readFully(data); douts.writeInt(data.length); douts.write(data); douts.flush(); } }catch(Exception ef){ ef.printStackTrace(); } } }最后一个,W端启动的主线程
package zhongjian; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.ServerSocket; import java.net.Socket; /** * 转发的! * @author yjz * */ public class zhuanfa implements Serializable{ private static ObjectOutputStream ous; private static ObjectInputStream ins; private static DataInputStream dins; private static DataOutputStream douts; private static ServerSocket serverSocket; private static ServerSocket serverSocket1; private void startSend() throws IOException { serverSocket=new ServerSocket(9527);//开通9527端口等待客户端来连接 对接被控制的! serverSocket1=new ServerSocket(9528);//开通9528对接控制人等待客户端来连接 System.out.println("服务器端口已打开,等待客户端连接!"); Socket sc1 = serverSocket1.accept(); Socket sc = serverSocket.accept(); //得到输入流,读取图片数据 dins=new DataInputStream(sc.getInputStream());//图片的数据 //转发给9528 douts=new DataOutputStream(sc1.getOutputStream()); //得到9528的事件流,转发给9527 ins=new ObjectInputStream(sc1.getInputStream()); ous=new ObjectOutputStream(sc.getOutputStream()); } public static void main(String[] args) throws IOException { zhuanfa zhuanfa=new zhuanfa(); //获取连接 zhuanfa.startSend(); System.out.println("中转服务器连接成功!"); new picThread(douts, dins).start();; System.out.println("转发图片线程启动成功!"); // zhuanfa.start();//启动图片转发线程 //启动事件转发线程 new EventThread(ins, ous).start(); System.out.println("转发事件线程启动成功!"); } }基本实现了远程控制,但是依然存在一些BUG,比如持续多次连接,再比如图片太大传输会卡需要压缩图片等等,希望看到这篇文章的大佬能不吝赐教,感谢。