Programvaruutveckling med funktionalitet för klient-serververktyg Windows, del 01

Hej.

I dagens avsnitt skulle jag vilja diskutera processen för att skriva klient-serverapplikationer som utför funktioner för standardverktyg i Windows, sĂ„som Telnet, TFTP, etc. Det Ă€r klart att jag inte kommer att bidra med nĂ„got nytt – alla dessa verktyg har fungerat framgĂ„ngsrikt i flera Ă„r, men jag tror att inte alla vet vad som hĂ€nder under huven.

Det Àr precis vad det hÀr handlar om nedan.

I den hĂ€r artikeln, för att inte dra ut pĂ„ det, kommer jag att skriva bara om Telnet-servern, men för tillfĂ€llet finns det ytterligare material om andra verktyg – det kommer i kommande delar av serien.

Först och frĂ€mst Ă€r det viktigt att förstĂ„ vad Telnet Ă€r, varför det behövs och vad man anvĂ€nder det till. Jag kommer inte att citera kĂ€llor ordagrant (om det behövs – jag kan bifoga lĂ€nkar till material i slutet av artikeln), jag kommer bara att sĂ€ga att Telnet möjliggör fjĂ€rrĂ„tkomst till kommandoraden pĂ„ en enhet. I stort sett slutar dess funktionalitet dĂ€r (jag har medvetet utelĂ€mnat nĂ€mna hur man ser pĂ„ serverporten, om detta senare). SĂ„, för att implementera detta, behöver vi ta emot en strĂ€ng pĂ„ klienten, skicka den till servern, försöka bearbeta den i kommandoraden, lĂ€sa svar frĂ„n kommandoraden, om det finns nĂ„got, vidarebefordra det tillbaka till klienten och visa det pĂ„ skĂ€rmen, eller om ett fel uppstĂ„r, lĂ„ta anvĂ€ndaren förstĂ„ att nĂ„got Ă€r fel.

För att genomföra det ovanstÄende, behövs dÀrför 2 arbetande klasser och en testklass, frÄn vilken vi kommer att starta servern och genom vilken klienten kommer att fungera.
För nÀrvarande inkluderar applikationens struktur:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

LÄt oss gÄ igenom var och en av dem:

TelnetClient

Allt som den hÀr klassen ska kunna göra Àr att skicka mottagna kommandon och visa de mottagna svaren. Dessutom mÄste den kunna ansluta till en godtycklig (som nÀmnts ovan) port pÄ den fjÀrrstyrda enheten och koppla ifrÄn den.

För detta har följande funktioner implementerats:

Funktionen som tar emot en socket-adress som argument, öppnar en anslutning och startar in- och utströmningar (variablerna för strömmen har deklarerats ovan, fullstÀndiga kÀllkoder finns i slutet av artikeln).

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

Överlagring av samma funktion som kopplar upp mot standardporten – för telnet Ă€r det 23


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

Funktionen lĂ€ser tecken frĂ„n tangentbordet och skickar dem till utgĂ„ende socket – vilket Ă€r karaktĂ€ristiskt att göra i radlĂ€ge, inte teckenlĂ€ge:


    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 tar emot data frÄn socket och visar 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 stoppar mottagning och överföring av data


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

TelnetServer

Denna klass bör ha funktionalitet för att ta emot kommandon frÄn socket, skicka dem för utförande och skicka svaret tillbaka till socket. Programmet har medvetet ingen inmatningskontroll, eftersom för det första finns det i "lÄdkontakt" möjlighet att formatera serverns disk och för det andra har sÀkerhetsfrÄgan utelÀmnats i denna artikel, och dÀrför finns det inget ord om kryptering eller SSL.

Det finns totalt 2 funktioner (en av dem Àr överlagrad), och generellt Àr det inte en sÀrskilt bra praxis, men inom ramen för denna uppgift tyckte jag att det var lÀmpligt att lÄta allt vara som det Àr.

 boolean isRunning = true;
    public void run(int port)    {

        (new Thread(() -> { try {
            ServerSocket ss = new ServerSocket(port); // Skapar en server socket och binder den till angiven port
            System.out.println("Port " + port + " vÀntar pÄ anslutningar");

            Socket socket = ss.accept();
            System.out.println("Ansluten");
            System.out.println();

            // HÀmtar in- och utströmmen för socketen, sÄ nu kan vi ta emot och skicka data till klienten.
            InputStream sin = socket.getInputStream();
            OutputStream sout = socket.getOutputStream();

            Map env = System.getenv();
            String wayToTemp = env.get("TEMP") + "tmp.txt";
            for (int i : ("Connected

".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 = "NÄgot gick fel... Felkod: " + 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("Fel: " + e.getMessage());
                    }
                }).start();
                System.out.println(buffer);
                buffer = "";

            }
        }
        catch(Exception x) {
            System.out.println(x.getMessage());
        }})).start();

    }

Programmet öppnar en serverport, lÀser data frÄn den tills det stöter pÄ kommandoslutsymbolen, skickar kommandot till en ny process, och utdata frÄn processen omdirigeras till socketen. SÄ enkelt som en Kalashnikov.

DÀrför finns det ocksÄ en överlagring för den hÀr funktionen med en standardport:

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

Och sĂ„ finns det ocksĂ„ en funktion som stoppar servern — den Ă€r ocksĂ„ ganska trivial, den bryter den eviga cykeln genom att bryta dess villkor.

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

Jag kommer inte att gĂ„ igenom testklasser hĂ€r, de finns lĂ€ngre ner — allt de gör Ă€r att kontrollera att de publika metoderna fungerar. Allt finns pĂ„ Git.

Sammanfattningsvis kan du förstĂ„ principerna för de grundlĂ€ggande konsolverktygen pĂ„ ett par kvĂ€llar. Nu nĂ€r vi telnetar till den fjĂ€rrdatorn förstĂ„r vi vad som hĂ€nder — magin har försvunnit.)

SÄ, lÀnkar:
Alla kÀllkoder har funnits, finns nu och kommer alltid finnas hÀr
Om Telnet
Mer om Telnet

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster