Skriba programaro kun la funkcieco de Vindozaj klient-servilaj iloj, parto 01

Salutojn.

Hodiaŭ mi ŝatus rigardi la procezon de verkado de klient-servilaj aplikaĵoj, kiuj plenumas la funkciojn de normaj Vindozaj iloj, kiel Telnet, TFTP, ktp, kaj tiel plu en pura Java. Estas klare, ke mi alportos nenion novan - ĉiuj ĉi tiuj utilecoj funkcias sukcese dum pli ol unu jaro, sed mi kredas, ke ne ĉiuj scias, kio okazas sub la kapuĉo.

Ĉi tio estas ĝuste kio estos diskutita sub la tranĉo.

En ĉi tiu artikolo, por ne treni ĝin, krom ĝeneralaj informoj, mi skribos nur pri la Telnet-servilo, sed nuntempe estas ankaŭ materialo pri aliaj utilecoj - ĝi estos en pliaj partoj de la serio.

Antaŭ ĉio, vi devas eltrovi, kio estas Telnet, por kio ĝi bezonas, kaj por kio ĝi estas uzata. Mi ne citos fontojn laŭvorte (se necese, mi aldonos ligilon al materialoj pri la temo fine de la artikolo), mi nur diros, ke Telnet disponigas foran aliron al la komandlinio de la aparato. Ĝenerale, ĉi tie finiĝas ĝia funkcieco (mi intence silentis pri aliro al la servila haveno; pli pri tio poste). Ĉi tio signifas, ke por efektivigi ĝin, ni devas akcepti linion sur la kliento, transdoni ĝin al la servilo, provi pasi ĝin al la komandlinio, legi la komandlinian respondon, se ekzistas unu, redonu ĝin al la kliento kaj montri ĝin sur la ekrano, aŭ, se eraroj, lasu la uzanton scii ke io estas malĝusta.

Por efektivigi ĉi-supran, laŭe, ni bezonas 2 laboristajn klasojn kaj iun testklason de kiu ni lanĉos la servilon kaj per kiu la kliento funkcios.
Sekve, nuntempe la aplika strukturo inkluzivas:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Ni trarigardu ĉiun el ili:

TelnetClient

Ĉio ĉi tiu klaso devus povi fari estas sendi ricevitajn komandojn kaj montri la ricevitajn respondojn. Krome, vi devas povi konekti al arbitra (kiel menciita supre) haveno de fora aparato kaj malkonekti de ĝi.

Por atingi tion, la sekvaj funkcioj estis efektivigitaj:

Funkcio kiu prenas ingan adreson kiel argumenton, malfermas konekton kaj komencas enigajn kaj eligajn fluojn (fluaj variabloj estas deklaritaj supre, plenaj fontoj estas ĉe la fino de la artikolo).

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

Troŝarĝante la saman funkcion, konektante al la defaŭlta haveno - por telnet ĉi tio estas 23


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

La funkcio legas signojn de la klavaro kaj sendas ilin al la eliga ingo - kio estas tipa, en linioreĝimo, ne signoreĝimo:


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

La funkcio ricevas datumojn de la ingo kaj montras ĝin sur la ekrano


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

La funkcio ĉesigas datumricevon kaj transdonon


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

TelnetServer

Ĉi tiu klaso devas havi la funkciecon ricevi komandon de ingo, sendante ĝin por ekzekuto, kaj sendi respondon de la komando reen al la ingo. La programo intence ne kontrolas la enigajn datumojn, ĉar unue, eĉ en "boksita telnet" eblas formati la servilan diskon, kaj due, la afero pri sekureco en ĉi tiu artikolo estas principe preterlasita, kaj tial ne ekzistas. vorton pri ĉifrado aŭ SSL.

Estas nur 2 funkcioj (unu el ili estas troŝarĝita), kaj ĝenerale tio ne estas tre bona praktiko, sed por la celoj de ĉi tiu tasko, ŝajnis al mi taŭga lasi ĉion kiel ĝi estas.

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

    }

La programo malfermas la servilan havenon, legas datumojn de ĝi ĝis ĝi renkontas komandan finon, pasas la komandon al nova procezo kaj redirektas la produktaĵon de la procezo al la ingo. Ĉio estas tiel simpla kiel kalashnikov-sturmpafilo.

Sekve, estas troŝarĝo por ĉi tiu funkcio kun defaŭlta haveno:

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

Nu, sekve, la funkcio kiu haltigas la servilon ankaŭ estas bagatela, ĝi interrompas la eternan buklon, malobservante ĝian kondiĉon.

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

Mi ne donos testklasojn ĉi tie, ili estas malsupre - ĉio, kion ili faras, estas kontroli la funkciojn de publikaj metodoj. Ĉio estas sur la git.

Por resumi, en kelkaj vesperoj vi povas kompreni la principojn de funkciado de la ĉefaj konzolaj utilecoj. Nun, kiam ni telenetas al fora komputilo, ni komprenas kio okazas - la magio malaperis)

Do, la ligiloj:
Ĉiuj fontoj estis, estas kaj estos ĉi tie
Pri Telnet
Pli pri Telnet

fonto: www.habr.com

Aldoni komenton