Softvér na písanie s funkciami obslužných programov klient-server Windows, časť 01

Pozdravy.

Dnes by som sa rád pozrel na proces písania klient-server aplikácií, ktoré vykonávajú funkcie štandardných Windows utilít, ako sú Telnet, TFTP, atď., a tak ďalej v čistej Jave. Je jasné, že neprinesiem nič nové – všetky tieto utility fungujú úspešne už viac ako rok, no verím, že nie každý vie, čo sa deje pod kapotou.

Presne o tom bude reč pod strihom.

V tomto článku, aby som to nenaťahoval, okrem všeobecných informácií napíšem iba o serveri Telnet, ale momentálne je tu aj materiál o iných nástrojoch - bude v ďalších častiach série.

Najprv musíte zistiť, čo je Telnet, na čo slúži a na čo sa používa. Zdroje nebudem citovať doslovne (v prípade potreby na konci článku pripojím odkaz na materiály k téme), poviem len, že Telnet poskytuje vzdialený prístup k príkazovému riadku zariadenia. Celkovo sa tu jeho funkčnosť končí (o prístupe k portu servera som zámerne mlčal; o tom neskôr). To znamená, že na jeho implementáciu musíme prijať riadok na klientovi, odovzdať ho serveru, pokúsiť sa ho odovzdať príkazovému riadku, prečítať odpoveď príkazového riadka, ak existuje, odovzdať ho späť klientovi a zobraziť na obrazovke alebo v prípade chýb upozornite používateľa, že niečo nie je v poriadku.

Na implementáciu vyššie uvedeného teda potrebujeme 2 pracovné triedy a nejakú testovaciu triedu, z ktorej spustíme server a cez ktorú bude pracovať klient.
Preto štruktúra aplikácie v súčasnosti zahŕňa:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Poďme si prejsť každý z nich:

TelnetClient

Všetko, čo by mala byť táto trieda schopná, je odosielať prijaté príkazy a zobrazovať prijaté odpovede. Okrem toho musíte mať možnosť pripojiť sa k ľubovoľnému (ako je uvedené vyššie) portu vzdialeného zariadenia a odpojiť sa od neho.

Na dosiahnutie tohto cieľa boli implementované nasledujúce funkcie:

Funkcia, ktorá berie ako argument adresu soketu, otvára spojenie a spúšťa vstupné a výstupné toky (premenné toku sú deklarované vyššie, úplné zdroje sú na konci článku).

 public void run(String ip, int port)
    {
        try {
            Socket socket = new Socket(ip, port);
            InputStream sin = socket.getInputStream();
            OutputStream sout = socket.getOutputStream();
            Scanner keyboard = new Scanner(System.in);
            reader = new Thread(()->read(keyboard, sout));
            writer = new Thread(()->write(sin));
            reader.start();
            writer.start();
        }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

Preťaženie rovnakej funkcie, pripojenie k predvolenému portu - pre telnet je to 23


    public void run(String ip)
    {
        run(ip, 23);
    }

Funkcia číta znaky z klávesnice a odosiela ich do výstupnej zásuvky – čo je typické v riadkovom režime, nie v znakovom režime:


    private void read(Scanner keyboard, OutputStream sout)
    {
        try {
            String input = new String();
            while (true) {
                input = keyboard.nextLine();
                for (char i : (input + " n").toCharArray())
                    sout.write(i);
            }
        }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

Funkcia prijíma dáta zo zásuvky a zobrazuje ich na obrazovke


    private void write(InputStream sin)
    {
        try {
            int tmp;
            while (true){
                tmp = sin.read();
                System.out.print((char)tmp);
            }
        }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

Funkcia zastaví príjem a prenos dát


    public void stop()
    {
        reader.stop();
        writer.stop();
    }
}

TelnetServer

Táto trieda musí mať funkciu prijatia príkazu zo soketu, jeho odoslania na vykonanie a odoslania odpovede z príkazu späť do soketu. Program schválne nekontroluje vstupné údaje, pretože po prvé aj v „krabicovom telnete“ je možné naformátovať disk servera a po druhé, otázka bezpečnosti v tomto článku je v zásade vynechaná, a preto tu nie je pár slov o šifrovaní alebo SSL.

Existujú len 2 funkcie (jedna z nich je preťažená) a vo všeobecnosti to nie je veľmi dobrý postup, ale pre účely tejto úlohy sa mi zdalo vhodné nechať všetko tak.

 boolean isRunning = true;
    public void run(int port)    {

        (new Thread(()->{ try {
            ServerSocket ss = new ServerSocket(port); // создаем сокет сервера и привязываем его к вышеуказанному порту
            System.out.println("Port "+port+" is waiting for connections");

            Socket socket = ss.accept();
            System.out.println("Connected");
            System.out.println();

            // Берем входной и выходной потоки сокета, теперь можем получать и отсылать данные клиенту.
            InputStream sin = socket.getInputStream();
            OutputStream sout = socket.getOutputStream();

            Map<String, String> env = System.getenv();
            String wayToTemp = env.get("TEMP") + "tmp.txt";
            for (int i :("Connectednnr".toCharArray()))
                sout.write(i);
            sout.flush();

            String buffer = new String();
            while (isRunning) {

                int intReader = 0;
                while ((char) intReader != 'n') {
                    intReader = sin.read();
                    buffer += (char) intReader;
                }


                final String inputToSubThread = "cmd /c " + buffer.substring(0, buffer.length()-2) + " 2>&1";


                new Thread(()-> {
                    try {

                        Process p = Runtime.getRuntime().exec(inputToSubThread);
                        InputStream out = p.getInputStream();
                        Scanner fromProcess = new Scanner(out);
                        try {

                            while (fromProcess.hasNextLine()) {
                                String temp = fromProcess.nextLine();
                                System.out.println(temp);
                                for (char i : temp.toCharArray())
                                    sout.write(i);
                                sout.write('n');
                                sout.write('r');
                            }
                        }
                        catch (Exception e) {
                            String output = "Something gets wrong... Err code: "+ e.getStackTrace();
                            System.out.println(output);
                            for (char i : output.toCharArray())
                                sout.write(i);
                            sout.write('n');
                            sout.write('r');
                        }

                        p.getErrorStream().close();
                        p.getOutputStream().close();
                        p.getInputStream().close();
                        sout.flush();

                    }
                    catch (Exception e) {
                        System.out.println("Error: " + e.getMessage());
                    }
                }).start();
                System.out.println(buffer);
                buffer = "";

            }
        }
        catch(Exception x) {
            System.out.println(x.getMessage());
        }})).start();

    }

Program otvorí port servera, načítava z neho dáta, kým nenarazí na znak konca príkazu, odovzdá príkaz novému procesu a presmeruje výstup z procesu do soketu. Všetko je také jednoduché ako útočná puška Kalašnikov.

V dôsledku toho existuje preťaženie tejto funkcie s predvoleným portom:

 public void run()
    {
        run(23);
    }

V súlade s tým je funkcia, ktorá zastaví server, tiež triviálna, preruší večnú slučku a poruší jej stav.

    public void stop()
    {
        System.out.println("Server was stopped");
        this.isRunning = false;
    }

Nebudem tu dávať testovacie triedy, sú nižšie - všetko, čo robia, je kontrola funkčnosti verejných metód. Všetko je na git.

Aby som to zhrnul, za pár večerov môžete pochopiť princípy fungovania hlavných nástrojov konzoly. Teraz, keď sme telenet na vzdialený počítač, chápeme, čo sa deje - kúzlo zmizlo)

Takže odkazy:
Všetky zdroje tu boli, sú a budú
O Telnete
Viac o Telnete

Zdroj: hab.com

Pridať komentár