Rašymo programinė įranga su Windows kliento-serverio paslaugų funkcijomis, 01 dalis

Sveikinimai.

Šiandien norėčiau pažvelgti į kliento-serverio programų, kurios atlieka standartinių „Windows“ paslaugų, tokių kaip Telnet, TFTP ir tt, ir kt., funkcijas gryna Java, rašymo procesą. Aišku, kad nieko naujo nepateiksiu - visos šios komunalinės paslaugos sėkmingai veikia jau ne vienerius metus, bet tikiu, kad ne visi žino, kas vyksta po gaubtu.

Būtent apie tai ir bus kalbama po pjūviu.

Šiame straipsnyje, kad nebūtų vilkinamas, be bendros informacijos, parašysiu tik apie Telnet serverį, tačiau šiuo metu yra ir medžiagos apie kitas komunalines paslaugas - tai bus kitose serijos dalyse.

Pirmiausia reikia išsiaiškinti, kas yra Telnet, kam jis reikalingas ir kam jis naudojamas. Necituosiu šaltinių pažodžiui (jei reikės, straipsnio pabaigoje pridėsiu nuorodą į medžiagą šia tema), pasakysiu tik tiek, kad Telnet suteikia nuotolinę prieigą prie įrenginio komandinės eilutės. Apskritai, čia baigiasi jo funkcionalumas (aš sąmoningai tylėjau apie prieigą prie serverio prievado; daugiau apie tai vėliau). Tai reiškia, kad norėdami jį įgyvendinti, turime priimti kliento eilutę, perduoti ją serveriui, pabandyti perduoti komandų eilutei, perskaityti komandinės eilutės atsakymą, jei toks yra, grąžinti klientui ir rodyti jį ekrane arba, jei yra klaidų, pranešti vartotojui, kad kažkas negerai.

Norint įgyvendinti tai, kas išdėstyta aukščiau, atitinkamai mums reikia 2 darbo klasių ir tam tikros bandomosios klasės, iš kurios paleisime serverį ir per kurią dirbs klientas.
Atitinkamai, šiuo metu paraiškos struktūra apima:

  • TelnetClient
  • TelnetClientTester
  • TelnetServeris
  • TelnetServerTester

Panagrinėkime kiekvieną iš jų:

TelnetClient

Visa ši klasė turėtų sugebėti siųsti gautas komandas ir parodyti gautus atsakymus. Be to, turite turėti galimybę prisijungti prie savavališko (kaip minėta aukščiau) nuotolinio įrenginio prievado ir nuo jo atsijungti.

Tam buvo įgyvendintos šios funkcijos:

Funkcija, kuri kaip argumentą priima lizdo adresą, atidaro ryšį ir pradeda įvesties bei išvesties srautus (srauto kintamieji nurodyti aukščiau, pilni šaltiniai yra straipsnio pabaigoje).

 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());
        }
    }

Perkraunant tą pačią funkciją, prisijungiant prie numatytojo prievado - telnet tai yra 23


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

Funkcija nuskaito simbolius iš klaviatūros ir siunčia juos į išvesties lizdą – tai įprasta eilutės režimu, o ne simbolių režimu:


    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());
        }
    }

Funkcija gauna duomenis iš lizdo ir parodo juos ekrane


    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());
        }
    }

Funkcija sustabdo duomenų priėmimą ir perdavimą


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

TelnetServeris

Ši klasė turi turėti galimybę gauti komandą iš lizdo, išsiųsti ją vykdyti ir siųsti atsakymą iš komandos atgal į lizdą. Programa sąmoningai netikrina įvesties duomenų, nes, pirma, net „telnete“ galima suformatuoti serverio diską, antra, saugumo klausimas šiame straipsnyje iš esmės praleistas, todėl nėra Keletas žodžių apie šifravimą arba SSL.

Yra tik 2 funkcijos (viena iš jų perkrauta), ir apskritai tai nėra labai gera praktika, tačiau šios užduoties tikslais man atrodė tikslinga viską palikti taip, kaip yra.

 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();

    }

Programa atidaro serverio prievadą, nuskaito duomenis iš jo, kol susiduria su komandos pabaigos simboliu, perduoda komandą naujam procesui ir nukreipia išvestį iš proceso į lizdą. Viskas taip paprasta, kaip Kalašnikovo automatas.

Atitinkamai yra šios funkcijos perkrova naudojant numatytąjį prievadą:

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

Na, atitinkamai, funkcija, kuri sustabdo serverį, taip pat yra nereikšminga, ji nutraukia amžiną kilpą, pažeisdama jo būklę.

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

Bandymo klasių čia neduosiu, jos yra žemiau – viskas, ką jie daro, yra patikrinti viešųjų metodų funkcionalumą. Viskas yra ant galvos.

Apibendrinant, per porą vakarų galite suprasti pagrindinių konsolės komunalinių paslaugų veikimo principus. Dabar, kai prisijungiame prie nuotolinio kompiuterio, suprantame, kas vyksta - magija dingo)

Taigi, nuorodos:
Visi šaltiniai buvo, yra ir bus čia
Apie Telnet
Daugiau apie Telnet

Šaltinis: www.habr.com

Добавить комментарий