Softueri i shkrimit me funksionalitetin e shërbimeve të klient-server Windows, pjesa 01

Përshëndetje.

Sot do të doja të shikoja procesin e shkrimit të aplikacioneve klient-server që kryejnë funksionet e shërbimeve standarde të Windows, të tilla si Telnet, TFTP, etj., etj. në Java të pastër. Është e qartë se nuk do të sjell asgjë të re - të gjitha këto shërbime kanë funksionuar me sukses për më shumë se një vit, por besoj se jo të gjithë e dinë se çfarë po ndodh nën kapuç.

Kjo është pikërisht ajo që do të diskutohet nën prerje.

Në këtë artikull, për të mos e zvarritur, përveç informacionit të përgjithshëm, do të shkruaj vetëm për serverin Telnet, por për momentin ka edhe materiale për shërbime të tjera - do të jetë në pjesë të mëtejshme të serisë.

Para së gjithash, ju duhet të kuptoni se çfarë është Telnet, për çfarë shërben dhe për çfarë përdoret. Unë nuk do t'i citoj burimet fjalë për fjalë (nëse është e nevojshme, do të bashkëngjit një lidhje me materialet mbi temën në fund të artikullit), do të them vetëm se Telnet siguron qasje në distancë në linjën e komandës së pajisjes. Në përgjithësi, këtu përfundon funksionaliteti i tij (qëllimisht kam heshtur për hyrjen në portin e serverit; më shumë për këtë më vonë). Kjo do të thotë që për ta zbatuar atë, duhet të pranojmë një linjë në klient, ta kalojmë atë te serveri, të përpiqemi ta kalojmë në vijën e komandës, të lexojmë përgjigjen e linjës së komandës, nëse ka një të tillë, t'ia kalojmë përsëri klientit dhe shfaqeni atë në ekran ose, nëse ka gabime, njoftoni përdoruesin se diçka nuk është në rregull.

Për të zbatuar sa më sipër, në përputhje me rrethanat, na duhen 2 klasa pune dhe një klasë testimi nga e cila do të lëshojmë serverin dhe përmes së cilës do të punojë klienti.
Prandaj, për momentin struktura e aplikimit përfshin:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Le të kalojmë nëpër secilën prej tyre:

TelnetClient

E gjithë kjo klasë duhet të jetë në gjendje të bëjë është të dërgojë komandat e marra dhe të tregojë përgjigjet e marra. Përveç kësaj, duhet të jeni në gjendje të lidheni me një portë arbitrare (siç u përmend më lart) të një pajisjeje të largët dhe të shkëputeni prej saj.

Për të arritur këtë, funksionet e mëposhtme u zbatuan:

Një funksion që merr një adresë prizë si argument, hap një lidhje dhe nis rrymat hyrëse dhe dalëse (ndryshoret e transmetimit janë deklaruar më sipër, burimet e plota janë në fund të artikullit).

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

Mbingarkimi i të njëjtit funksion, lidhja me portin e paracaktuar - për telnet kjo është 23


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

Funksioni lexon karaktere nga tastiera dhe i dërgon në prizën e daljes - që është tipike, në modalitetin e linjës, jo në modalitetin e karaktereve:


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

Funksioni merr të dhëna nga priza dhe i shfaq ato në ekran


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

Funksioni ndalon marrjen dhe transmetimin e të dhënave


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

TelnetServer

Kjo klasë duhet të ketë funksionalitetin e marrjes së një komande nga një prizë, dërgimit të saj për ekzekutim dhe dërgimit të një përgjigje nga komanda përsëri në prizë. Programi nuk i kontrollon qëllimisht të dhënat hyrëse, sepse së pari, edhe në "telnet me kuti" është e mundur të formatohet disku i serverit, dhe së dyti, çështja e sigurisë në këtë artikull është lënë në parim, dhe kjo është arsyeja pse nuk ka disa fjalë për enkriptimin ose SSL.

Janë vetëm 2 funksione (njëri prej tyre është i mbingarkuar), dhe në përgjithësi kjo nuk është një praktikë shumë e mirë, por për qëllimet e kësaj detyre, m'u duk e përshtatshme të lija gjithçka ashtu siç është.

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

    }

Programi hap portin e serverit, lexon të dhënat prej tij derisa të ndeshet me një karakter të fundit të komandës, e kalon komandën në një proces të ri dhe e ridrejton daljen nga procesi në prizë. Gjithçka është aq e thjeshtë sa një pushkë kallashnikov.

Prandaj, ka një mbingarkesë për këtë funksion me një port të paracaktuar:

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

Epo, në përputhje me rrethanat, funksioni që ndalon serverin është gjithashtu i parëndësishëm, ai ndërpret lakin e përjetshëm, duke shkelur gjendjen e tij.

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

Unë nuk do të jap klasa testimi këtu, ato janë më poshtë - gjithçka që bëjnë është të kontrollojnë funksionalitetin e metodave publike. Gjithçka është në gatishmëri.

Për ta përmbledhur, në disa mbrëmje mund të kuptoni parimet e funksionimit të shërbimeve kryesore të tastierës. Tani, kur ne telenet në një kompjuter të largët, kuptojmë se çfarë po ndodh - magjia është zhdukur)

Pra, lidhjet:
Të gjitha burimet ishin, janë dhe do të jenë këtu
Rreth Telnet
Më shumë rreth Telnet

Burimi: www.habr.com

Shto një koment