Actual Client Desktop
Remote Image of the Connected Client Desktop
Introduction
This program allows any computer to control other PCs remotely.
Program Features
- View remote desktop
- Mouse movement control
- Mouse buttons control
- Keyboard control
Background
One of the biggest challenges in this project is finding a way to move the mouse pointer, simulate key stroke and capture the screen. After spending some time I found a great class, Robot
class that does all that I need.
In addition, using serialization to send screenshots from client to server and to send server events like mouse move, key press, key release helped me a lot to write clean and simple code instead of sending images and events in raw data format over the network.
Robot Class Methods
mouseMove
- Moves the mouse pointer to a set of specified absolute screen coordinates given in pixels mousePress
- Presses one of the buttons on the mouse mouseRelease
- Releases one of the buttons on the mouse keyPress
- Presses a specified key on the keyboard keyRelease
- Releases specified key on the keyboard createScreenCapture
- Takes a screenshot
Program Parts
1. RemoteServer
This is the server part which waits for clients connections and per each connected client, a new frame appears showing the current client screen. When you move the mouse over the frame, this results in moving the mouse at the client side. The same happens when you right/left click mouse button or type a key while the frame is in focus.
2. RemoteClient
This the client side, its core function is sending a screen shot of the client's desktop every predefined amount of time. Also it receives server commands such as "move the mouse command", then executes the command at the client's PC.
Using the Code
In order to run the program, you need to download RemoteServer_source.zip and RemoteClient_source.zip on two different PCs, then extract them. In one of the PCs say PC1, run RemoteServer.jar which is located under dist folder using the following command:
>> java -jar RemoteServer.jar
You will be asked to enter port number for the server to listen at, enter any port number above 1024, for example 5000.
On the other PC say PC2, execute RemoteClient.jar using the following command:
>> java -jar RemoteClient.jar
You will be asked to enter server IP, enter IP address of PC1, then you will be asked to enter port number, enter the same port you entered above, e.g. 5000.
Now, in PC1 you have full control over PC2 including moving the mouse, clicking the mouse, keys stroking, viewing PC2 desktop, etc.
Coding Structure
RemoteServer
ServerInitiator Class
This is the entry class which listens to server port and wait for clients connections. Also, it creates an essential part of the program GUI.
ClientHandler Class
Per each connected client, there is an object of this class. It shows an InternalFrame
per client and it receives clients' screen dimension.
ClientScreenReciever Class
Receives captured screen from the client, then displays it.
ClientCommandsSender Class
It listens to the server commands, then sends them to the client. Server commands include mouse move, key stroke, mouse click, etc.
EnumCommands Class
Defines constants which are used to represent server commands.
RemoteClient
ClientInitiator Class
This is the entry class that starts the client instance. It establishes connection to the server and creates the client GUI.
ScreenSpyer Class
Captures screen periodically and sends them to the server.
ServerDelegate Class
Receives server commands and executes them in the client PC.
EnumCommands Class
Defines constants which are used to represent server commands.
Code Snippets
1) RemoteClient
Connect to Server
System.out.println("Connecting to server ..........");
socket = new Socket(ip, port);
System.out.println("Connection Established.");
Capture Desktop Screen then Send it to the Server Periodically
In ScreenSpyer
class, Screen
is captured using createScreenCapture
method in Robot
class and it accepts a Rectangle
object which carries screen dimension. If we try to send image object directly using serialization, it will fail because it does not implement Serializable
interface. That is why we have to wrap it using the ImageIcon
class as shown below:
while(continueLoop){
BufferedImage image = robot.createScreenCapture(rectangle);
ImageIcon imageIcon = new ImageIcon(image);
try {
System.out.println("before sending image");
oos.writeObject(imageIcon);
oos.reset();
System.out.println("New screenshot sent");
} catch (IOException ex) {
ex.printStackTrace();
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
}
Receive Server Events then call Robot Class Methods to Execute these Events
while(continueLoop){
System.out.println("Waiting for command");
int command = scanner.nextInt();
System.out.println("New command: " + command);
switch(command){
case -1:
robot.mousePress(scanner.nextInt());
break;
case -2:
robot.mouseRelease(scanner.nextInt());
break;
case -3:
robot.keyPress(scanner.nextInt());
break;
case -4:
robot.keyRelease(scanner.nextInt());
break;
case -5:
robot.mouseMove(scanner.nextInt(), scanner.nextInt());
break;
}
}
2) RemoteServer
Wait for Clients Connections
while(true){
Socket client = sc.accept();
System.out.println("New client Connected to the server");
new ClientHandler(client,desktop);
}
Receive Client Desktop Screenshots and Display them
while(continueLoop){
ImageIcon imageIcon = (ImageIcon) cObjectInputStream.readObject();
System.out.println("New image received");
Image image = imageIcon.getImage();
image = image.getScaledInstance
(cPanel.getWidth(),cPanel.getHeight(),Image.SCALE_FAST);
Graphics graphics = cPanel.getGraphics();
graphics.drawImage(image, 0, 0, cPanel.getWidth(),cPanel.getHeight(),cPanel);
}
Handle Mouse and Key Events then Send them to the Client Program to Simulate them
In ClientCommandsSender
class, when mouse is moved, x and y values are sent to the client but we have to take into consideration the size difference between clients' screen size and server's panel size, that is why we have to multiply by a certain factor as shown in the following code:
public void mouseMoved(MouseEvent e) {
double xScale = clientScreenDim.getWidth()/cPanel.getWidth();
System.out.println("xScale: " + xScale);
double yScale = clientScreenDim.getHeight()/cPanel.getHeight();
System.out.println("yScale: " + yScale);
System.out.println("Mouse Moved");
writer.println(EnumCommands.MOVE_MOUSE.getAbbrev());
writer.println((int)(e.getX() * xScale));
writer.println((int)(e.getY() * yScale));
writer.flush();
}
public void mousePressed(MouseEvent e) {
System.out.println("Mouse Pressed");
writer.println(EnumCommands.PRESS_MOUSE.getAbbrev());
int button = e.getButton();
int xButton = 16;
if (button == 3) {
xButton = 4;
}
writer.println(xButton);
writer.flush();
}
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse Released");
writer.println(EnumCommands.RELEASE_MOUSE.getAbbrev());
int button = e.getButton();
int xButton = 16;
if (button == 3) {
xButton = 4;
}
writer.println(xButton);
writer.flush();
}
public void keyPressed(KeyEvent e) {
System.out.println("Key Pressed");
writer.println(EnumCommands.PRESS_KEY.getAbbrev());
writer.println(e.getKeyCode());
writer.flush();
}
public void keyReleased(KeyEvent e) {
System.out.println("Mouse Released");
writer.println(EnumCommands.RELEASE_KEY.getAbbrev());
writer.println(e.getKeyCode());
writer.flush();
}