Skryfsagteware met die funksionaliteit van Windows-kliënt-bedienerhulpprogramme, deel 01

Groete.

Vandag wil ek kyk na die proses om kliënt-bedienertoepassings te skryf wat die funksies van standaard Windows-nutsprogramme verrig, soos Telnet, TFTP, ensovoorts, ensovoorts in suiwer Java. Dit is duidelik dat ek niks nuuts sal bring nie - al hierdie nutsprogramme werk al meer as een jaar suksesvol, maar ek glo nie almal weet wat onder die enjinkap aangaan nie.

Dit is presies wat onder die snit bespreek sal word.

In hierdie artikel, om dit nie uit te sleep nie, sal ek benewens algemene inligting net oor die Telnet-bediener skryf, maar op die oomblik is daar ook materiaal oor ander nutsprogramme - dit sal in verdere dele van die reeks wees.

Eerstens moet u uitvind wat Telnet is, waarvoor dit nodig is en waarvoor dit gebruik word. Ek sal nie bronne woordeliks aanhaal nie (indien nodig sal ek 'n skakel na materiaal oor die onderwerp aan die einde van die artikel heg), ek sal net sê dat Telnet afstandtoegang bied tot die opdragreël van die toestel. Oor die algemeen is dit waar die funksionaliteit daarvan eindig (ek het doelbewus geswyg oor toegang tot die bedienerpoort; later meer daaroor). Dit beteken dat om dit te implementeer, ons 'n reël op die kliënt moet aanvaar, dit na die bediener moet deurgee, probeer om dit na die opdragreël deur te gee, die opdragreëlreaksie lees, as daar een is, dit teruggee aan die kliënt en vertoon dit op die skerm, of, indien foute, laat die gebruiker weet dat iets verkeerd is.

Om bogenoemde te implementeer, benodig ons dienooreenkomstig 2 werksklasse en een of ander toetsklas waaruit ons die bediener sal begin en waardeur die kliënt sal werk.
Gevolglik sluit die aansoekstruktuur op die oomblik in:

  • Telnet-kliënt
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Kom ons gaan deur elkeen van hulle:

Telnet-kliënt

Al wat hierdie klas behoort te kan doen, is om ontvangde opdragte te stuur en die ontvangde antwoorde te wys. Daarbenewens moet jy in staat wees om aan 'n arbitrêre (soos hierbo genoem) poort van 'n afgeleë toestel te koppel en daarvan te ontkoppel.

Om dit te bereik, is die volgende funksies geïmplementeer:

'n Funksie wat 'n sokadres as 'n argument neem, 'n verbinding oopmaak en toevoer- en uitsetstrome begin (stroomveranderlikes word hierbo verklaar, volledige bronne is aan die einde van die artikel).

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

Oorlaai dieselfde funksie, koppel aan die verstekpoort - vir telnet is dit 23


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

Die funksie lees karakters vanaf die sleutelbord en stuur dit na die uitsetsok - wat tipies is, in lynmodus, nie karaktermodus nie:


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

Die funksie ontvang data vanaf die sok en wys dit op die skerm


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

Die funksie stop data-ontvangs en -versending


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

TelnetServer

Hierdie klas moet die funksionaliteit hê om 'n opdrag vanaf 'n sok te ontvang, dit vir uitvoering te stuur en 'n antwoord van die opdrag terug na die sok te stuur. Die program kontroleer doelbewus nie die invoerdata nie, want eerstens, selfs in "boxed telnet" is dit moontlik om die bedienerskyf te formateer, en tweedens word die kwessie van sekuriteit in hierdie artikel in beginsel weggelaat, en daarom is daar nie 'n woord oor enkripsie of SSL.

Daar is net 2 funksies (een daarvan is oorlaai), en oor die algemeen is dit nie 'n baie goeie praktyk nie, maar vir die doeleindes van hierdie taak het dit vir my gepas gelyk om alles te laat soos dit is.

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

    }

Die program maak die bedienerpoort oop, lees data daaruit totdat dit 'n opdrag-eindkarakter teëkom, gee die opdrag na 'n nuwe proses deur en herlei die uitset van die proses na die sok. Alles is so eenvoudig soos 'n Kalashnikov-aanvalsgeweer.

Gevolglik is daar 'n oorlading vir hierdie funksie met 'n verstekpoort:

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

Wel, dienooreenkomstig is die funksie wat die bediener stop ook triviaal, dit onderbreek die ewige lus, wat die toestand daarvan oortree.

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

Ek sal nie toetsklasse hier gee nie, hulle is hieronder - al wat hulle doen is om die funksionaliteit van openbare metodes na te gaan. Alles is op die punt.

Om op te som, oor 'n paar aande kan u die beginsels van die werking van die hoofkonsole-nutsprogramme verstaan. Nou, wanneer ons na 'n afgeleë rekenaar telenet, verstaan ​​ons wat gebeur - die magie het verdwyn)

Dus, die skakels:
Alle bronne was, is en sal hier wees
Oor Telnet
Meer oor Telnet

Bron: will.com

Voeg 'n opmerking