Skriveprogramvare med funksjonaliteten til Windows klient-server-verktøy, del 01

Hilsener.

I dag vil jeg se på prosessen med å skrive klient-serverapplikasjoner som utfører funksjonene til standard Windows-verktøy, som Telnet, TFTP, et cetera, et cetera i ren Java. Det er klart at jeg ikke kommer med noe nytt - alle disse verktøyene har fungert med suksess i mer enn ett år, men jeg tror ikke alle vet hva som skjer under panseret.

Det er nettopp dette som vil bli diskutert under kuttet.

I denne artikkelen, for ikke å trekke det ut, i tillegg til generell informasjon, vil jeg bare skrive om Telnet-serveren, men for øyeblikket er det også materiale om andre verktøy - det vil være i andre deler av serien.

Først av alt må du finne ut hva Telnet er, hva det trengs til og hva det brukes til. Jeg vil ikke sitere kilder ordrett (om nødvendig vil jeg legge ved en lenke til materialer om emnet på slutten av artikkelen), jeg vil bare si at Telnet gir ekstern tilgang til kommandolinjen til enheten. I det store og hele er det her funksjonaliteten slutter (jeg holdt bevisst taus om tilgang til serverporten; mer om det senere). Dette betyr at for å implementere det, må vi godta en linje på klienten, sende den til serveren, prøve å sende den til kommandolinjen, lese kommandolinjesvaret, hvis det er en, sende den tilbake til klienten og vis det på skjermen, eller, hvis det oppstår feil, la brukeren vite at noe er galt.

For å implementere det ovennevnte trenger vi 2 arbeidsklasser og en testklasse som vi vil starte serveren fra og som klienten vil jobbe gjennom.
Følgelig inkluderer søknadsstrukturen for øyeblikket:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

La oss gå gjennom hver av dem:

TelnetClient

Alt denne klassen skal kunne gjøre er å sende mottatte kommandoer og vise de mottatte svarene. I tillegg må du kunne koble til en vilkårlig (som nevnt ovenfor) port på en ekstern enhet og koble fra den.

For å oppnå dette ble følgende funksjoner implementert:

En funksjon som tar en socket-adresse som et argument, åpner en forbindelse og starter inn- og utstrømmer (strømvariabler er deklarert ovenfor, fullstendige kilder er på slutten av artikkelen).

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

Overbelaster den samme funksjonen, kobler til standardporten - for telnet er dette 23


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

Funksjonen leser tegn fra tastaturet og sender dem til utgangskontakten - som er typisk i linjemodus, ikke tegnmodus:


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

Funksjonen mottar data fra stikkontakten og viser dem på skjermen


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

Funksjonen stopper datamottak og -overføring


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

TelnetServer

Denne klassen må ha funksjonaliteten til å motta en kommando fra en socket, sende den for utførelse og sende et svar fra kommandoen tilbake til socket. Programmet kontrollerer bevisst ikke inngangsdataene, fordi for det første, selv i "boxed telnet" er det mulig å formatere serverdisken, og for det andre er sikkerhetsproblemet i denne artikkelen utelatt i prinsippet, og det er derfor det ikke er et ord om kryptering eller SSL.

Det er bare 2 funksjoner (en av dem er overbelastet), og generelt er ikke dette en veldig god praksis, men for denne oppgavens formål virket det passende for meg å la alt være som det er.

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

    }

Programmet åpner serverporten, leser data fra den til den møter et kommandoslutttegn, sender kommandoen til en ny prosess og omdirigerer utdataene fra prosessen til sokkelen. Alt er så enkelt som en Kalashnikov-gevær.

Følgelig er det en overbelastning for denne funksjonen med en standardport:

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

Vel, følgelig er funksjonen som stopper serveren også triviell, den avbryter den evige sløyfen og bryter dens tilstand.

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

Jeg vil ikke gi testklasser her, de er nedenfor - alt de gjør er å sjekke funksjonaliteten til offentlige metoder. Alt er på git.

For å oppsummere, om et par kvelder kan du forstå prinsippene for drift av hovedkonsollverktøyene. Nå, når vi telenetter til en ekstern datamaskin, forstår vi hva som skjer - magien har forsvunnet)

Så, lenkene:
Alle kilder var, er og vil være her
Om Telnet
Mer om Telnet

Kilde: www.habr.com

Legg til en kommentar