Pagsusulat ng software na may functionality ng Windows client-server utilities, part 01

Pagbati.

Ngayon gusto kong tingnan ang proseso ng pagsulat ng mga application ng client-server na gumaganap ng mga function ng karaniwang mga utility ng Windows, tulad ng Telnet, TFTP, et cetera, et cetera sa purong Java. Malinaw na hindi ako magdadala ng bago - lahat ng mga utility na ito ay matagumpay na gumagana nang higit sa isang taon, ngunit naniniwala ako na hindi alam ng lahat kung ano ang nangyayari sa ilalim ng hood.

Ito mismo ang tatalakayin sa ilalim ng hiwa.

Sa artikulong ito, upang hindi i-drag ito, bilang karagdagan sa pangkalahatang impormasyon, magsusulat lamang ako tungkol sa server ng Telnet, ngunit sa ngayon ay mayroon ding materyal sa iba pang mga kagamitan - ito ay nasa karagdagang mga bahagi ng serye.

Una sa lahat, kailangan mong malaman kung ano ang Telnet, para saan ito kailangan, at para saan ito ginagamit. Hindi ako magsi-quote ng mga mapagkukunan ng verbatim (kung kinakailangan, mag-attach ako ng isang link sa mga materyales sa paksa sa dulo ng artikulo), sasabihin ko lang na ang Telnet ay nagbibigay ng malayuang pag-access sa command line ng device. Sa pangkalahatan, dito nagtatapos ang pag-andar nito (sinadya kong tumahimik tungkol sa pag-access sa port ng server; higit pa tungkol doon sa ibang pagkakataon). Nangangahulugan ito na upang maipatupad ito, kailangan nating tumanggap ng isang linya sa kliyente, ipasa ito sa server, subukang ipasa ito sa command line, basahin ang tugon ng command line, kung mayroon man, ipasa ito pabalik sa kliyente at ipakita ito sa screen, o, kung may mga error, ipaalam sa user na may mali.

Upang ipatupad ang nasa itaas, nang naaayon, kailangan namin ng 2 working classes at ilang test class kung saan namin ilulunsad ang server at kung saan gagana ang client.
Alinsunod dito, sa sandaling ang istraktura ng aplikasyon ay kinabibilangan ng:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Suriin natin ang bawat isa sa kanila:

TelnetClient

Ang tanging magagawa ng klase na ito ay magpadala ng mga natanggap na utos at ipakita ang mga natanggap na tugon. Bilang karagdagan, kailangan mong makakonekta sa isang arbitrary (tulad ng nabanggit sa itaas) na port ng isang malayuang aparato at idiskonekta mula dito.

Upang makamit ito, ipinatupad ang mga sumusunod na function:

Isang function na kumukuha ng socket address bilang argumento, nagbubukas ng koneksyon at nagsisimula ng input at output stream (ang mga stream variable ay idineklara sa itaas, ang buong source ay nasa dulo ng artikulo).

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

Overloading sa parehong function, pagkonekta sa default port - para sa telnet ito ay 23


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

Binabasa ng function ang mga character mula sa keyboard at ipinapadala ang mga ito sa output socket - na karaniwan, sa line mode, hindi character mode:


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

Ang function ay tumatanggap ng data mula sa socket at ipinapakita ito sa screen


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

Ang function ay huminto sa pagtanggap at paghahatid ng data


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

TelnetServer

Ang klase na ito ay dapat magkaroon ng functionality ng pagtanggap ng command mula sa isang socket, pagpapadala nito para sa execution, at pagpapadala ng tugon mula sa command pabalik sa socket. Ang programa ay sadyang hindi sinusuri ang input data, dahil una, kahit na sa "boxed telnet" posible na i-format ang server disk, at pangalawa, ang isyu ng seguridad sa artikulong ito ay tinanggal sa prinsipyo, at iyon ang dahilan kung bakit walang isang salita tungkol sa pag-encrypt o SSL.

Mayroon lamang 2 mga pag-andar (isa sa mga ito ay labis na karga), at sa pangkalahatan ito ay hindi isang napakahusay na kasanayan, ngunit para sa mga layunin ng gawaing ito, tila angkop sa akin na iwanan ang lahat nang ganito.

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

    }

Binubuksan ng program ang server port, binabasa ang data mula dito hanggang sa makatagpo ito ng command end character, ipasa ang command sa isang bagong proseso, at i-redirect ang output mula sa proseso patungo sa socket. Ang lahat ay kasing simple ng isang Kalashnikov assault rifle.

Alinsunod dito, mayroong labis na karga para sa function na ito na may default na port:

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

Kaya, nang naaayon, ang pag-andar na humihinto sa server ay walang halaga din, nakakaabala ito sa walang hanggang loop, lumalabag sa kondisyon nito.

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

Hindi ako magbibigay ng mga klase sa pagsubok dito, nasa ibaba sila - ang ginagawa lang nila ay suriin ang functionality ng mga pampublikong pamamaraan. Lahat ay nasa git.

Upang ibuod, sa loob ng ilang gabi mauunawaan mo ang mga prinsipyo ng pagpapatakbo ng mga pangunahing kagamitan sa console. Ngayon, kapag nag-telenet kami sa isang malayong computer, naiintindihan namin kung ano ang nangyayari - nawala ang magic)

Kaya, ang mga link:
Lahat ng mga pinagmumulan ay, naririto at narito
Tungkol sa Telnet
Higit pa tungkol sa Telnet

Pinagmulan: www.habr.com

Magdagdag ng komento