Software schrijven met de functionaliteit van Windows client-server-hulpprogramma's, deel 01

ΠŸΡ€ΠΈΠ²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽ.

Vandaag zou ik willen kijken naar het proces van het schrijven van client-serverapplicaties die de functies van standaard Windows-hulpprogramma's uitvoeren, zoals Telnet, TFTP, enzovoort, enzovoort, in puur Java. Het is duidelijk dat ik niets nieuws zal brengen - al deze hulpprogramma's werken al meer dan een jaar met succes, maar ik geloof dat niet iedereen weet wat er onder de motorkap gebeurt.

Dit is precies wat onder de bezuiniging zal worden besproken.

Om het niet te slepen, zal ik in dit artikel, naast algemene informatie, alleen over de Telnet-server schrijven, maar op dit moment is er ook materiaal over andere hulpprogramma's - het zal in verdere delen van de serie voorkomen.

Allereerst moet je uitzoeken wat Telnet is, waarvoor het nodig is en waarvoor het wordt gebruikt. Ik zal de bronnen niet woordelijk citeren (indien nodig zal ik aan het einde van het artikel een link naar materiaal over het onderwerp bijvoegen), ik zal alleen zeggen dat Telnet externe toegang biedt tot de opdrachtregel van het apparaat. Over het algemeen eindigt hier de functionaliteit (ik zweeg bewust over de toegang tot de serverpoort; daarover later meer). Dit betekent dat we, om het te implementeren, een regel op de client moeten accepteren, deze aan de server moeten doorgeven, proberen deze door te geven aan de opdrachtregel, het antwoord op de opdrachtregel moeten lezen, als die er is, deze terug moeten sturen naar de client en weer te geven op het scherm, of, als er fouten optreden, de gebruiker te laten weten dat er iets mis is.

Om het bovenstaande te implementeren, hebben we dienovereenkomstig 2 werkklassen en een testklasse nodig van waaruit we de server zullen starten en waarmee de client zal werken.
Dienovereenkomstig omvat de applicatiestructuur op dit moment:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Laten we ze allemaal doornemen:

TelnetClient

Het enige dat deze klasse zou moeten kunnen doen, is ontvangen opdrachten verzenden en de ontvangen antwoorden weergeven. Bovendien moet u verbinding kunnen maken met een willekeurige (zoals hierboven vermeld) poort van een extern apparaat en de verbinding hiermee kunnen verbreken.

Om dit te bereiken zijn de volgende functies geΓ―mplementeerd:

Een functie die een socketadres als argument neemt, een verbinding opent en invoer- en uitvoerstromen start (stroomvariabelen worden hierboven gedeclareerd, volledige bronnen staan ​​aan het einde van het 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());
        }
    }

Overbelasting van dezelfde functie, verbinding maken met de standaardpoort - voor telnet is dit 23


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

De functie leest tekens van het toetsenbord en stuurt deze naar de uitvoeraansluiting - wat typisch is in de lijnmodus, niet in de tekenmodus:


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

De functie ontvangt gegevens van het stopcontact en geeft deze weer op het scherm


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

De functie stopt de ontvangst en verzending van gegevens


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

TelnetServer

Deze klasse moet de functionaliteit hebben om een ​​commando van een socket te ontvangen, het ter uitvoering te verzenden en een antwoord van het commando terug naar de socket te sturen. Het programma controleert opzettelijk de invoergegevens niet, omdat het ten eerste zelfs in "boxed telnet" mogelijk is om de serverschijf te formatteren, en ten tweede wordt de kwestie van beveiliging in dit artikel in principe weggelaten, en daarom is er geen een woord over encryptie of SSL.

Er zijn slechts 2 functies (een ervan is overbelast), en over het algemeen is dit geen erg goede gewoonte, maar voor de doeleinden van deze taak leek het mij gepast om alles te laten zoals het 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();

    }

Het programma opent de serverpoort, leest er gegevens uit totdat het een opdrachteindteken tegenkomt, geeft de opdracht door aan een nieuw proces en stuurt de uitvoer van het proces door naar de socket. Alles is zo simpel als een Kalashnikov-aanvalsgeweer.

Dienovereenkomstig is er een overbelasting voor deze functie met een standaardpoort:

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

Welnu, dienovereenkomstig is de functie die de server stopt ook triviaal, hij onderbreekt de eeuwige lus en schendt de voorwaarde ervan.

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

Ik zal hier geen testlessen geven, ze staan ​​hieronder - het enige wat ze doen is de functionaliteit van openbare methoden controleren. Alles staat op de git.

Samenvattend kunt u in een paar avonden de werkingsprincipes van de hulpprogramma's van de hoofdconsole begrijpen. Als we nu teleneten naar een externe computer, begrijpen we wat er gebeurt - de magie is verdwenen)

Dus de links:
Alle bronnen waren, zijn en zullen hier zijn
Over Telnet
Meer over Telnet

Bron: www.habr.com

Voeg een reactie