Menulis perisian dengan kefungsian utiliti pelayan klien Windows, bahagian 01

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

Hari ini saya ingin melihat proses menulis aplikasi pelayan pelanggan yang melaksanakan fungsi utiliti Windows standard, seperti Telnet, TFTP, dan lain-lain, dan sebagainya dalam Java tulen. Sudah jelas bahawa saya tidak akan membawa apa-apa yang baru - semua utiliti ini telah berfungsi dengan jayanya selama lebih daripada satu tahun, tetapi saya percaya tidak semua orang tahu apa yang berlaku di bawah hud.

Inilah yang akan dibincangkan di bawah pemotongan.

Dalam artikel ini, untuk tidak menyeretnya keluar, sebagai tambahan kepada maklumat umum, saya hanya akan menulis tentang pelayan Telnet, tetapi pada masa ini terdapat juga bahan pada utiliti lain - ia akan berada di bahagian selanjutnya dalam siri ini.

Pertama sekali, anda perlu mengetahui apa itu Telnet, untuk apa ia diperlukan, dan untuk apa ia digunakan. Saya tidak akan memetik sumber secara verbatim (jika perlu, saya akan melampirkan pautan ke bahan mengenai topik pada akhir artikel), saya hanya akan mengatakan bahawa Telnet menyediakan akses jauh ke baris arahan peranti. Pada umumnya, di sinilah fungsinya berakhir (saya sengaja mendiamkan diri tentang mengakses port pelayan; lebih lanjut mengenainya kemudian). Ini bermakna untuk melaksanakannya, kita perlu menerima baris pada klien, hantar ke pelayan, cuba hantar ke baris arahan, baca respons baris arahan, jika ada, hantar semula kepada klien dan memaparkannya pada skrin, atau, jika ralat, beritahu pengguna bahawa ada sesuatu yang tidak kena.

Untuk melaksanakan perkara di atas, sewajarnya, kami memerlukan 2 kelas pekerja dan beberapa kelas ujian dari mana kami akan melancarkan pelayan dan melalui mana pelanggan akan berfungsi.
Sehubungan itu, pada masa ini struktur aplikasi termasuk:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Mari kita lihat setiap daripada mereka:

TelnetClient

Semua kelas ini sepatutnya boleh lakukan ialah menghantar arahan yang diterima dan menunjukkan respons yang diterima. Di samping itu, anda perlu dapat menyambung ke port sewenang-wenangnya (seperti yang dinyatakan di atas) peranti jauh dan memutuskan sambungan daripadanya.

Untuk mencapai matlamat ini, fungsi berikut telah dilaksanakan:

Fungsi yang mengambil alamat soket sebagai hujah, membuka sambungan dan memulakan aliran input dan output (pembolehubah strim diisytiharkan di atas, sumber penuh berada di penghujung 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());
        }
    }

Melebihkan fungsi yang sama, menyambung ke port lalai - untuk telnet ini adalah 23


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

Fungsi membaca aksara daripada papan kekunci dan menghantarnya ke soket output - yang biasa, dalam mod baris, bukan mod aksara:


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

Fungsi ini menerima data daripada soket dan memaparkannya pada skrin


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

Fungsi ini menghentikan penerimaan dan penghantaran data


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

TelnetServer

Kelas ini mesti mempunyai fungsi untuk menerima arahan daripada soket, menghantarnya untuk pelaksanaan dan menghantar respons daripada arahan itu kembali ke soket. Program ini sengaja tidak menyemak data input, kerana pertama, walaupun dalam "telnet berkotak" adalah mungkin untuk memformat cakera pelayan, dan kedua, isu keselamatan dalam artikel ini ditinggalkan pada dasarnya, dan itulah sebabnya tidak ada perkataan tentang penyulitan atau SSL.

Terdapat hanya 2 fungsi (salah satu daripadanya terlampau beban), dan secara amnya ini bukan amalan yang sangat baik, tetapi untuk tujuan tugas ini, nampaknya wajar saya meninggalkan segala-galanya sebagaimana adanya.

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

    }

Program ini membuka port pelayan, membaca data daripadanya sehingga ia menemui aksara akhir perintah, menghantar arahan kepada proses baharu dan mengubah hala keluaran daripada proses ke soket. Segala-galanya semudah senapang serangan Kalashnikov.

Oleh itu, terdapat lebihan untuk fungsi ini dengan port lalai:

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

Nah, oleh itu, fungsi yang menghentikan pelayan juga remeh, ia mengganggu gelung kekal, melanggar keadaannya.

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

Saya tidak akan memberikan kelas ujian di sini, mereka berada di bawah - yang mereka lakukan hanyalah menyemak kefungsian kaedah awam. Semuanya ada di git.

Untuk meringkaskan, dalam beberapa malam anda boleh memahami prinsip operasi utiliti konsol utama. Sekarang, apabila kita telenet ke komputer jauh, kita faham apa yang berlaku - keajaiban telah hilang)

Jadi, pautan:
Semua sumber telah, sedang dan akan ada di sini
Mengenai Telnet
Lebih lanjut mengenai Telnet

Sumber: www.habr.com

Tambah komen