上一篇讲到局域网内的远程控制,这一篇讲利用云服务器,实现真正远程控制。
云服务器转发的话呢,就需要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,比如持续多次连接,再比如图片太大传输会卡需要压缩图片等等,希望看到这篇文章的大佬能不吝赐教,感谢。