Kirjoitusohjelmisto Windows-asiakas-palvelin-apuohjelmien toiminnallisuudella, osa 01

Tervehdys.

Tänään haluaisin tarkastella prosessia, jossa kirjoitetaan asiakas-palvelinsovelluksia, jotka suorittavat tavallisten Windows-apuohjelmien, kuten Telnet, TFTP, jne., toimintoja puhtaalla Javalla. On selvää, että en tuo mitään uutta - kaikki nämä apuohjelmat ovat toimineet menestyksekkäästi yli vuoden, mutta uskon, että kaikki eivät tiedä, mitä konepellin alla tapahtuu.

Juuri tästä leikkauksen alla keskustellaan.

Tässä artikkelissa kirjoitan yleistietojen lisäksi vain Telnet-palvelimesta, jotta se ei kestäisi, mutta tällä hetkellä on myös materiaalia muista apuohjelmista - se tulee olemaan sarjan muissa osissa.

Ensinnäkin sinun on selvitettävä, mikä Telnet on, mihin sitä tarvitaan ja mihin sitä käytetään. En lainaa lähteitä sanatarkasti (tarvittaessa liitän linkin aiheeseen liittyviin materiaaleihin artikkelin loppuun), sanon vain, että Telnet tarjoaa etäyhteyden laitteen komentoriville. Yleisesti ottaen sen toiminnallisuus päättyy tähän (Pidin tarkoituksella hiljaa palvelinportin käyttämisestä; siitä lisää myöhemmin). Tämä tarkoittaa, että sen toteuttamiseksi meidän on hyväksyttävä rivi asiakkaalla, välitettävä se palvelimelle, yritettävä välittää se komentoriville, luettava komentorivin vastaus, jos sellainen on, lähetettävä se takaisin asiakkaalle ja näytä se näytöllä tai, jos virheitä, kerro käyttäjälle, että jokin on vialla.

Yllä olevan toteuttamiseksi tarvitsemme vastaavasti 2 työluokkaa ja testiluokan, joista käynnistämme palvelimen ja jonka kautta asiakas toimii.
Vastaavasti tällä hetkellä sovellusrakenne sisältää:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Käydään läpi jokainen niistä:

TelnetClient

Tämän luokan pitäisi pystyä vain lähettämään vastaanotetut komennot ja näyttämään vastaanotetut vastaukset. Lisäksi sinun on voitava muodostaa yhteys mielivaltaiseen (kuten edellä mainittiin) etälaitteen porttiin ja katkaista yhteys siitä.

Tämän saavuttamiseksi toteutettiin seuraavat toiminnot:

Funktio, joka ottaa argumenttina socket-osoitteen, avaa yhteyden ja käynnistää tulo- ja lähtövirrat (virran muuttujat on ilmoitettu yllä, täydelliset lähteet artikkelin lopussa).

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

Saman toiminnon ylikuormitus, yhteyden muodostaminen oletusporttiin - telnetille tämä on 23


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

Toiminto lukee merkkejä näppäimistöltä ja lähettää ne lähtöliitäntään - mikä on tyypillistä rivitilassa, ei merkkitilassa:


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

Toiminto vastaanottaa tiedot pistorasiasta ja näyttää sen näytöllä


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

Toiminto pysäyttää tiedon vastaanoton ja lähetyksen


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

TelnetServer

Tällä luokalla on oltava toiminto, joka vastaanottaa komennon socketista, lähettää sen suoritettaviksi ja lähettää vastauksen komennosta takaisin pistorasiaan. Ohjelma ei tarkoituksella tarkista syöttötietoja, koska ensinnäkin jopa "laatikoidussa telnetissä" on mahdollista alustaa palvelinlevy, ja toiseksi tämän artikkelin turvallisuuskysymys on periaatteessa jätetty pois, ja siksi sitä ei ole muutama sana salauksesta tai SSL:stä.

Toimintoja on vain 2 (yksi niistä on ylikuormitettu), ja yleensä tämä ei ole kovin hyvä käytäntö, mutta tämän tehtävän kannalta minusta tuntui sopivalta jättää kaikki ennalleen.

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

    }

Ohjelma avaa palvelinportin, lukee siitä dataa, kunnes se kohtaa komennon lopetusmerkin, välittää komennon uudelle prosessille ja ohjaa ulostulon prosessista pistorasiaan. Kaikki on yhtä yksinkertaista kuin Kalashnikov-rynnäkkökivääri.

Vastaavasti tälle toiminnolle on ylikuormitus oletusportin kanssa:

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

No, vastaavasti toiminto, joka pysäyttää palvelimen, on myös triviaali, se katkaisee ikuisen silmukan rikkoen sen tilaa.

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

En anna testiluokkia täällä, ne ovat alla - he vain tarkistavat julkisten menetelmien toimivuuden. Kaikki on pihalla.

Yhteenvetona voidaan todeta, että parissa illassa voit ymmärtää pääkonsolin apuohjelmien toimintaperiaatteet. Nyt kun telenetimme etätietokoneeseen, ymmärrämme mitä tapahtuu - taika on kadonnut)

Eli linkit:
Kaikki lähteet olivat, ovat ja tulevat olemaan täällä
Tietoja Telnetistä
Lisätietoja Telnetistä

Lähde: will.com

Lisää kommentti