Pisanje programske opreme s funkcionalnostjo pripomočkov Windows odjemalec-strežnik, 01. del

Pozdravljeni.

Danes bi rad pogledal postopek pisanja aplikacij odjemalec-strežnik, ki izvajajo funkcije standardnih pripomočkov Windows, kot so Telnet, TFTP itd., itd., v čisti Javi. Jasno je, da ne bom prinesel ničesar novega - vsi ti pripomočki uspešno delujejo že več kot eno leto, vendar verjamem, da vsi ne vedo, kaj se dogaja pod pokrovom.

Prav o tem bo govora pod rezom.

V tem članku, da ne bi zavlačeval, bom poleg splošnih informacij pisal le o strežniku Telnet, trenutno pa je na voljo tudi gradivo o drugih pripomočkih - to bo v nadaljnjih delih serije.

Najprej morate ugotoviti, kaj je Telnet, za kaj je potreben in za kaj se uporablja. Ne bom dobesedno citiral virov (če je potrebno, bom na koncu članka priložil povezavo do gradiva na to temo), rekel bom le, da Telnet omogoča oddaljeni dostop do ukazne vrstice naprave. Na splošno se tukaj njegova funkcionalnost konča (dostop do vrat strežnika sem namenoma zamolčal; o tem kasneje). To pomeni, da moramo za izvedbo sprejeti vrstico na odjemalcu, jo posredovati strežniku, jo poskusiti prenesti v ukazno vrstico, prebrati odgovor ukazne vrstice, če obstaja, jo posredovati nazaj odjemalcu in prikazati na zaslonu ali, če so napake, sporočiti uporabniku, da je nekaj narobe.

Za izvedbo zgoraj navedenega torej potrebujemo 2 delovna razreda in nekaj testnega razreda, iz katerega bomo zagnali strežnik in preko katerega bo delal odjemalec.
V skladu s tem trenutno struktura aplikacije vključuje:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Pojdimo skozi vsakega od njih:

TelnetClient

Vse, kar bi moral biti sposoben narediti ta razred, je pošiljanje prejetih ukazov in prikaz prejetih odgovorov. Poleg tega se morate imeti možnost povezati s poljubnimi (kot je navedeno zgoraj) vrati oddaljene naprave in prekiniti povezavo z njo.

Da bi to dosegli, so bile implementirane naslednje funkcije:

Funkcija, ki vzame naslov vtičnice kot argument, odpre povezavo in zažene vhodne in izhodne tokove (spremenljivke toka so deklarirane zgoraj, celotni viri so na koncu članka).

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

Preobremenitev iste funkcije, povezovanje s privzetimi vrati - za telnet je to 23


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

Funkcija prebere znake s tipkovnice in jih pošlje v izhodno vtičnico - kar je tipično v vrstičnem načinu, ne v znakovnem načinu:


    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 sprejema podatke iz vtičnice in jih prikazuje na zaslonu


    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 prekine sprejem in prenos podatkov


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

TelnetServer

Ta razred mora imeti funkcijo prejemanja ukaza iz vtičnice, pošiljanja v izvedbo in pošiljanja odgovora iz ukaza nazaj v vtičnico. Program namerno ne preverja vhodnih podatkov, ker je prvič tudi v “boxed telnetu” možno formatirati disk strežnika, drugič pa je vprašanje varnosti v tem članku načeloma izpuščeno in zato ni nekaj besed o šifriranju ali SSL.

Obstajata samo 2 funkciji (ena od njiju je preobremenjena) in na splošno to ni zelo dobra praksa, vendar se mi je za namene te naloge zdelo primerno, da pustim vse tako, kot je.

 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 odpre vrata strežnika, bere podatke iz njih, dokler ne naleti na znak za konec ukaza, posreduje ukaz novemu procesu in preusmeri izhod iz procesa v vtičnico. Vse je preprosto kot jurišna puška kalašnikov.

V skladu s tem obstaja preobremenitev za to funkcijo s privzetimi vrati:

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

No, v skladu s tem je tudi funkcija, ki ustavi strežnik, trivialna, prekine večno zanko in krši njen pogoj.

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

Tukaj ne bom dajal testnih razredov, spodaj so - vse, kar počnejo, je preverjanje funkcionalnosti javnih metod. Vse je na git.

Če povzamem, v nekaj večerih lahko razumete načela delovanja glavnih pripomočkov konzole. Zdaj, ko se po telenetu povežemo z oddaljenim računalnikom, razumemo, kaj se dogaja - čarovnija je izginila)

Torej, povezave:
Vsi viri so bili, so in bodo tukaj
O Telnetu
Več o Telnetu

Vir: www.habr.com

Dodaj komentar