Java. Десктопний застосунок для керування іншими комп’ютерами в локальній мережі за 15 хв

Частково клікбейт 🙂 — тільки для керування курсором миші.
Але розширивши функціонал, можна буде контролювати практично все.

Архітектура

Sender буде відправляти координати миші.
Receiver буде обробляти ці дані і застосувати дані координати у себе.
Таким чином, у нас буде симуляції контролю миші іншого пристрою.

Надсилаючи якісь дані, отримувач має знати як їх обробити.
Для цього існують протоколи - спосіб, за допомогою якого дві або більше сторони домовляються про формат обміну інформацією. Він визначає, які кроки слід виконувати, які повідомлення слід відправляти, як вони мають бути структуровані, і як системи повинні реагувати на отримані повідомлення.

Ми могли б використати вже існуючий протокол, наприклад, HTTP. Але давайте зробимо невеличкий велосипед.

Наш “протокол“ буде складатися з:
- Назви команди
- Символу нового рядка, щоб мати змогу розділити назву і дані
- Самих даних, необхідних для виконання цієї команди

Супер-пупер протокол
Супер-пупер протокол

[Sender]

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Sender extends Application {

    private CommandSender commandSender;

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(Sender.class.getResource("main.fxml"));
        Scene scene = new Scene(loader.load());
        commandSender = new CommandSender();
        applySendingListener(scene);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void applySendingListener(Scene scene) {
        scene.setOnMouseMoved(commandSender::sendMouseCommand);
    }
}
import javafx.scene.input.MouseEvent;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class CommandSender {

    public void sendMouseCommand(MouseEvent event) {
        int sceneX = (int) event.getSceneX();
        int sceneY = (int) event.getSceneY();
        String msg = String.format(Constants.MOUSE_CMD_TEMPLATE, sceneX, sceneY);
        sendMessage(msg);
    }

    private void sendMessage(String msg) {
       try(var socket = new Socket(Constants.IP, Constants.port);
           OutputStream outputStream = socket.getOutputStream();) {
           outputStream.write(msg.getBytes());
           outputStream.flush();
       } catch (IOException e) {
           throw new RuntimeException(e);
       }
    }
}
public interface Constants {
    String IP = "..local ip address";
    int port = ..desired port;

    String MOUSE_CMD_TEMPLATE = """
            CursorCommand
            %s,%s""";
}

Receiver

import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Receiver {

    private static Robot robot;

    public static void main(String[] args) throws Exception {
        robot = new Robot();
        ServerSocket socket = new ServerSocket(8766);
        while (true) {
            handleConnection(socket.accept());
        }
    }

    private static void handleConnection(Socket accept) throws IOException {
        try(accept;
            InputStream inputStream = accept.getInputStream()) {
            byte[] bytes = inputStream.readAllBytes();
            String unparsedCmd = new String(bytes);
            String[] cmdNameAndData = unparsedCmd.split("\n");
            String commandName = cmdNameAndData[0];
            String data = cmdNameAndData[1];
            System.out.println(data);
            handleCommand(commandName, data);
        }
    }

    private static void handleCommand(String commandName, String data) {
        // choose command by cmd name, but we have only one command
        String[] coordinates = data.split(",");
        int x = Integer.parseInt(coordinates[0]);
        int y = Integer.parseInt(coordinates[1]);
        robot.mouseMove(x, y);
    }
}

Результат (це прикріплене відео до мого незакінчего пет-проекту в гітхабі, який є аналогом цього коду, бо зараз немає вільної машини, щоб протестувати, точніше, тестував на локальній машині, але відос вийшов би не дуже ефектним)

https://user-images.githubusercontent.com/72043323/166488147-41cbd774-f5f0-4f12-8622-660a7a61e909.mp4


Тут гора і трішки речей які можна покращати:

  • У нас захардкоджений адрес локальної машини, на яку ми надсилаємо команди. Можна використовувати звичайний пінг в межах 192.168.*.*, щоб вияснити, які машини доступні в мережі

  • Ми беремо координати відносно сцени, розмір якої може бути наприклад 600×500. Тобто, в нас зміна координат буде відбуватися саме в цьому діапазоні. Тому нам потрібно знати як скейлити/трансформути ці координати відносно роздільної здатності під кожен пристрій.

    Нам потрібно буде з керуючої машини кидати GetInitialDataRequest, який би повертав дані про роздільну здатність.
    Ми б зберігали в пам’яті інформацію про роздільну здатність відносно кожного пристрою в локальній мережі.

  • Додати команду для кліку, управлінням клавіатури, etc.

  • Оскільки об’єкт події йде від ОС і дуже часто, виникає “дублікація“ координат і ми надсилаємо зайві дані, створюючи непотрібне навантаження

  • На кожну подію ми відкриваємо сокет. Ми можемо оптимізувати мережеву комунікацію, створюючи тільки один сокет, який буде відкритий

  • Рефакторинг коду


Цей довгочит — стиснута версія мого незакінченого пет-проекту.
Оскільки це лінк на гітхаб, там одразу можна взяти потикати код.

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Oleksandr Klymenko
Oleksandr Klymenko@overpathz

Java Software Engineer

4.4KПрочитань
1Автори
71Читачі
На Друкарні з 19 квітня

Більше від автора

  • Secure networking. Deep Dive

    Глибоке занурення в протоколи TLS/SSL та інфраструктуру відкритих ключів (PKI). Основні поняття, процес встановлення захищеного з'єднання, роль сертифікатів та ланцюжка довіри

    Теми цього довгочиту:

    Security
  • Поширені помилки у дизайні REST API

    У довгочиті розглядаються поширені помилки при проектуванні REST API та способи їх уникнення: версіонування, використання DTO, підхід CQRS, робота з мікросервісами, та інші практики для підвищення продуктивності, безпеки й зручності API

    Теми цього довгочиту:

    Java
  • Java. Короткий огляд еволюції багатопотоковості

    У перших версіях Java багатопоточність реалізовувалася за допомогою класу Thread, який дозволяв створювати нові потоки. Проте ця модель мала багато недоліків:

    Теми цього довгочиту:

    Java

Вам також сподобається

Коментарі (2)

Доволі цікаво, а яка практична цінність вашого ПЕТ проекту, рухатиметься в сторону тім в’ювера, хакерського софта чи це шоб просто щоб підтягнути власні навички ?

Вам також сподобається