Skrivesoftware med funktionaliteten fra Windows-klient-server-værktøjer, del 01

Vær hilset.

I dag vil jeg gerne se på processen med at skrive klient-server-applikationer, der udfører funktionerne i standard Windows-værktøjer, såsom Telnet, TFTP, et cetera, etc. i ren Java. Det er klart, at jeg ikke vil bringe noget nyt - alle disse hjælpeprogrammer har fungeret med succes i mere end et år, men jeg tror, ​​at ikke alle ved, hvad der foregår under motorhjelmen.

Det er præcis, hvad der vil blive diskuteret under nedskæringen.

I denne artikel, for ikke at trække det ud, vil jeg ud over generel information kun skrive om Telnet-serveren, men i øjeblikket er der også materiale om andre hjælpeprogrammer - det vil være i andre dele af serien.

Først og fremmest skal du finde ud af, hvad Telnet er, hvad det er nødvendigt til, og hvad det bruges til. Jeg vil ikke citere kilder ordret (hvis det er nødvendigt, vil jeg vedhæfte et link til materialer om emnet i slutningen af ​​artiklen), jeg vil kun sige, at Telnet giver fjernadgang til enhedens kommandolinje. I det store og hele er det her, dens funktionalitet slutter (jeg forholdt mig bevidst tavs om at få adgang til serverporten; mere om det senere). Det betyder, at for at implementere det, skal vi acceptere en linje på klienten, sende den til serveren, prøve at sende den til kommandolinjen, læse kommandolinjesvaret, hvis der er en, sende den tilbage til klienten og vise det på skærmen, eller, hvis der opstår fejl, lad brugeren vide, at der er noget galt.

For at implementere ovenstående har vi derfor brug for 2 arbejdsklasser og en testklasse, hvorfra vi vil starte serveren, og gennem hvilken klienten vil arbejde.
Derfor omfatter ansøgningsstrukturen i øjeblikket:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Lad os gennemgå hver af dem:

TelnetClient

Alt, hvad denne klasse skal kunne gøre, er at sende modtagne kommandoer og vise de modtagne svar. Derudover skal du være i stand til at oprette forbindelse til en vilkårlig (som nævnt ovenfor) port på en fjernenhed og afbryde forbindelsen fra den.

For at opnå dette blev følgende funktioner implementeret:

En funktion, der tager en socket-adresse som et argument, åbner en forbindelse og starter input- og outputstrømme (streamvariabler er erklæret ovenfor, fulde kilder er i slutningen af ​​artiklen).

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

Overbelastning af den samme funktion, tilslutning til standardporten - for telnet er dette 23


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

Funktionen læser tegn fra tastaturet og sender dem til udgangsstikket - hvilket er typisk i linjetilstand, ikke tegntilstand:


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

Funktionen modtager data fra stikkontakten og viser dem på skærmen


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

Funktionen stopper datamodtagelse og -transmission


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

TelnetServer

Denne klasse skal have funktionaliteten til at modtage en kommando fra en socket, sende den til udførelse og sende et svar fra kommandoen tilbage til socket. Programmet kontrollerer bevidst ikke inputdataene, fordi for det første, selv i "boxed telnet" er det muligt at formatere serverdisken, og for det andet er spørgsmålet om sikkerhed i denne artikel i princippet udeladt, og det er derfor, der ikke er et ord om kryptering eller SSL.

Der er kun 2 funktioner (en af ​​dem er overbelastet), og generelt er dette ikke en særlig god praksis, men i forbindelse med denne opgave forekom det mig passende at lade 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 åbner serverporten, læser data fra den, indtil det støder på et kommandosluttegn, sender kommandoen til en ny proces og omdirigerer output fra processen til socket. Alt er så simpelt som en Kalashnikov-gevær.

Derfor er der en overbelastning for denne funktion med en standardport:

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

Nå, derfor er funktionen, der stopper serveren, også triviel, den afbryder den evige løkke og krænker dens tilstand.

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

Jeg vil ikke give testklasser her, de er nedenfor - alt, de gør, er at kontrollere funktionaliteten af ​​offentlige metoder. Alt er på git.

For at opsummere kan du om et par aftener forstå principperne for driften af ​​de vigtigste konsolværktøjer. Nu, når vi telenet til en fjerncomputer, forstår vi, hvad der sker - magien er forsvundet)

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

Kilde: www.habr.com

Tilføj en kommentar