Menulis perangkat lunak dengan fungsionalitas utilitas server-klien Windows, bagian 01

Selamat Datang.

Hari ini saya ingin melihat proses penulisan aplikasi client-server yang menjalankan fungsi utilitas Windows standar, seperti Telnet, TFTP, dan lain-lain, dan lain-lain dalam Java murni. Jelas bahwa saya tidak akan membawa sesuatu yang baru - semua utilitas ini telah bekerja dengan sukses selama lebih dari satu tahun, tetapi saya yakin tidak semua orang tahu apa yang sedang terjadi.

Inilah tepatnya yang akan dibahas dalam pemotongan ini.

Pada artikel ini, agar tidak berlarut-larut, selain informasi umum, saya hanya akan menulis tentang server Telnet, tetapi saat ini ada juga materi tentang utilitas lain - itu akan ada di bagian selanjutnya dari seri ini.

Pertama-tama, Anda perlu memahami apa itu Telnet, apa kegunaannya, dan kegunaannya. Saya tidak akan mengutip sumber kata demi kata (jika perlu, saya akan melampirkan tautan ke materi tentang topik tersebut di akhir artikel), saya hanya akan mengatakan bahwa Telnet menyediakan akses jarak jauh ke baris perintah perangkat. Pada umumnya, di sinilah fungsinya berakhir (saya sengaja bungkam tentang akses ke port server; lebih lanjut tentang itu nanti). Artinya untuk mengimplementasikannya, kita perlu menerima sebuah baris di klien, meneruskannya ke server, mencoba meneruskannya ke baris perintah, membaca respons baris perintah, jika ada, meneruskannya kembali ke klien dan menampilkannya di layar, atau, jika terjadi kesalahan, beri tahu pengguna bahwa ada sesuatu yang salah.

Oleh karena itu, untuk mengimplementasikan hal di atas, kita memerlukan 2 kelas kerja dan beberapa kelas pengujian tempat kita akan meluncurkan server dan melaluinya klien akan bekerja.
Oleh karena itu, saat ini struktur aplikasi meliputi:

  • Klien Telnet
  • TelnetClientTester
  • Server Telnet
  • TelnetServerTester

Mari kita bahas masing-masingnya:

Klien Telnet

Yang harus dapat dilakukan oleh kelas ini hanyalah mengirimkan perintah yang diterima dan menunjukkan respons yang diterima. Selain itu, Anda harus dapat menyambung ke port sembarang (seperti yang disebutkan di atas) pada perangkat jarak jauh dan memutuskan sambungannya.

Untuk mencapai hal ini, fungsi-fungsi berikut dilaksanakan:

Sebuah fungsi yang menggunakan alamat soket sebagai argumen, membuka koneksi dan memulai aliran input dan output (variabel aliran dideklarasikan di atas, sumber lengkap ada di akhir 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());
        }
    }

Membebani fungsi yang sama, menghubungkan ke port default - untuk telnet ini adalah 23


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

Fungsi ini membaca karakter dari keyboard dan mengirimkannya ke soket keluaran - yang biasanya, dalam mode garis, bukan mode karakter:


    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 dari soket dan menampilkannya di layar


    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 transmisi data


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

Server Telnet

Kelas ini harus memiliki fungsi menerima perintah dari soket, mengirimkannya untuk dieksekusi, dan mengirimkan respons dari perintah kembali ke soket. Program ini sengaja tidak memeriksa data masukan, karena pertama, bahkan dalam "telnet kotak" dimungkinkan untuk memformat disk server, dan kedua, masalah keamanan dalam artikel ini pada prinsipnya dihilangkan, dan itulah mengapa tidak ada sepatah kata pun tentang enkripsi atau SSL.

Hanya ada 2 fungsi (salah satunya kelebihan beban), dan secara umum ini bukan praktik yang baik, tetapi untuk keperluan tugas ini, tampaknya tepat bagi saya untuk membiarkan semuanya apa 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 membuka port server, membaca data darinya hingga menemukan karakter akhir perintah, meneruskan perintah ke proses baru, dan mengarahkan output dari proses ke soket. Semuanya sesederhana senapan serbu Kalashnikov.

Oleh karena itu, ada kelebihan beban untuk fungsi ini dengan port default:

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

Oleh karena itu, fungsi menghentikan server juga sepele, memutus loop abadi, melanggar kondisinya.

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

Saya tidak akan memberikan kelas tes di sini, mereka ada di bawah - yang mereka lakukan hanyalah memeriksa fungsionalitas metode publik. Semuanya ada di git.

Untuk meringkas, dalam beberapa malam Anda dapat memahami prinsip-prinsip pengoperasian utilitas konsol utama. Sekarang, ketika kami melakukan telenet ke komputer jarak jauh, kami memahami apa yang terjadi - keajaiban telah hilang)

Jadi, tautannya:
Semua sumber telah, sedang, dan akan ada di sini
Tentang Telnet
Lebih lanjut tentang Telnet

Sumber: www.habr.com

Tambah komentar