上一篇讲到局域网内的远程控制,这一篇讲利用云服务器,实现真正远程控制。

云服务器转发的话呢,就需要S控制端,C被控制端,W转发 思路就是,C将截图发送给W,W转发给S,S解析显示。S将操作发送给W,W转发给C,C解析模拟操作。

先来张整体结构图:

java使用jsch实现远程登录 java控制远程设备_System

不多说,直接贴代码:

按结构图来,先贴第一个类,全部是使用了大佬的代码,用于实现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,比如持续多次连接,再比如图片太大传输会卡需要压缩图片等等,希望看到这篇文章的大佬能不吝赐教,感谢。