Web Based Remote Desktop in Java

Ever wanted to access a computer on your network using just an iPhone web browser? Just to see the screen and perhaps use the mouse a bit? I did, so I wrote this simple web server, and it's turned out to be more useful than I thought it would be. It serves a screen capture as an image file, which you can then zoom in on and tap: the server implements an image map, so your tap on the image is turned into a mouse click. It then waits two seconds and sends you an updated copy of the screen.

Remote MacBook desktop as seen on iPhone
Remote MacBook desktop as seen on iPhone

After starting the jar file, access it using a web browser (any will work, not just an iPhone). By default it runs on port 6060, so for example you run it on a computer with IP address 192.168.1.42, access it at http://192.168.1.42:6060/

If you don't have Java, it's free, here.

An idea for improvement: Keyboard control - add a text field to the main page, so you can enter some text, and submit it to the server, which then simulates keystrokes. (I don't need this, so it's left as an exercise for the reader.)

Code

import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Date;

import javax.imageio.ImageIO;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class RemoteDesktop {

    static final int SERVER_PORT = 6060;
    static final int CLICK_REFRESH_DELAY_MS = 2000;
    static final Rectangle SCREEN_RECT = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
    static final String IMAGE_CODEC = "jpeg";
    static String HOST_NAME;

    static void logOutput(String string) {
        System.out.println(new Date() + " WEB: " + string);
    }

    static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
        }
    }

    static void send(HttpExchange exchange, byte content[]) throws IOException {
        exchange.sendResponseHeaders(200, content.length);
        exchange.getResponseBody().write(content);
        exchange.close();
    }

    static void send(HttpExchange exchange, String text) throws IOException {
        send(exchange, text.getBytes());
    }

    static void sendMainPage(HttpExchange exchange) throws IOException {
        send(exchange, "<html><head><title>" + HOST_NAME + " Desktop</title></head><body><a href=\"screen.map\"><img border=\"0\" ismap src=\"/screen\"></a></body></html>");
    }

    public static void main(String[] args) throws Exception {

        HOST_NAME = InetAddress.getLocalHost().getHostName();

        final Robot robot = new Robot();

        HttpServer server = HttpServer.create(new InetSocketAddress(SERVER_PORT), 0);
        server.createContext("/screen.map", new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) throws IOException {
                String path = exchange.getRequestURI().toASCIIString();
                int q = path.indexOf('?');
                int comma = path.indexOf(',', q);
                int x = Integer.parseInt(path.substring(q + 1, comma));
                int y = Integer.parseInt(path.substring(comma + 1));
                logOutput("CLICK! [" + x + "," + y + "]");
                robot.mouseMove(x, y);
                robot.mousePress(InputEvent.BUTTON1_MASK);
                robot.mouseRelease(InputEvent.BUTTON1_MASK);
                sleep(CLICK_REFRESH_DELAY_MS);
                sendMainPage(exchange);
            }
        });
        server.createContext("/screen", new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) throws IOException {
                BufferedImage im = robot.createScreenCapture(SCREEN_RECT);
                ByteArrayOutputStream output = new ByteArrayOutputStream(500000);
                ImageIO.write(im, IMAGE_CODEC, output);
                send(exchange, output.toByteArray());
            }
        });
        server.createContext("/", new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) throws IOException {
                sendMainPage(exchange);
            }
        });

        logOutput("Server started on: " + server.getAddress());
        server.start();
    }
}