Софтвер за пишување со функционалност на комуналните услуги за клиент-сервер на Windows, дел 01

Честитки.

Денес би сакал да го разгледам процесот на пишување апликации клиент-сервер кои ги извршуваат функциите на стандардните комунални услуги на Windows, како што се Telnet, TFTP, итн., итн во чиста Java. Јасно е дека нема да донесам ништо ново - сите овие комунални услуги успешно работат повеќе од една година, но верувам дека не секој знае што се случува под хаубата.

Ова е токму она што ќе се дискутира под сечењето.

Во оваа статија, за да не се одолговлекува, покрај општите информации, ќе пишувам само за серверот Телнет, но во моментов има и материјал за други комунални услуги - ќе го има во понатамошните делови од серијата.

Пред сè, треба да откриете што е Телнет, за што е потребен и за што се користи. Нема да цитирам извори дословно (доколку е потребно, ќе прикачам врска до материјали на темата на крајот од статијата), само ќе кажам дека Телнет обезбедува далечински пристап до командната линија на уредот. Во голема мера, тука завршува неговата функционалност (намерно молчев за пристапот до портата на серверот; повеќе за тоа подоцна). Ова значи дека за да го имплементираме, треба да прифатиме линија на клиентот, да ја предадеме на серверот, да се обидеме да ја пренесеме во командната линија, да го прочитаме одговорот на командната линија, ако има таков, да го пренесеме назад на клиентот и прикажете го на екранот или, доколку има грешки, известете го корисникот дека нешто не е во ред.

За да го имплементираме горенаведеното, соодветно, потребни ни се 2 работни класи и некоја тест класа од која ќе го стартуваме серверот и преку која ќе работи клиентот.
Според тоа, во моментот структурата на апликацијата вклучува:

  • TelnetClient
  • TelnetClientTester
  • ТелнетСервер
  • TelnetServerTester

Ајде да поминеме низ секоја од нив:

TelnetClient

Сè што оваа класа треба да може да направи е да испраќа примени команди и да ги прикаже добиените одговори. Покрај тоа, треба да бидете во можност да се поврзете со произволна (како што е споменато погоре) порта на далечински уред и да се исклучите од него.

За да се постигне ова, беа имплементирани следниве функции:

Функција која зема адреса на сокет како аргумент, отвора врска и започнува влезни и излезни текови (променливите за пренос се декларирани погоре, целосните извори се на крајот од статијата).

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

Преоптоварување на истата функција, поврзување со стандардната порта - за телнет ова е 23


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

Функцијата чита знаци од тастатурата и ги испраќа до излезниот приклучок - што е типично, во режим на линија, а не во режим на знаци:


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

Функцијата прима податоци од штекерот и ги прикажува на екранот


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

Функцијата го запира примањето и преносот на податоци


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

ТелнетСервер

Оваа класа мора да има функционалност да прима команда од сокет, да ја испраќа на извршување и да испраќа одговор од командата назад во сокетот. Програмата намерно не ги проверува влезните податоци, бидејќи прво, дури и во „бокс телнет“ е можно да се форматира серверскиот диск, и второ, прашањето за безбедноста во овој напис е во принцип испуштено, и затоа не постои збор за шифрирање или SSL.

Има само 2 функции (едната од нив е преоптоварена), и генерално ова не е многу добра практика, но за целите на оваа задача, ми се чинеше соодветно да оставам сè како што е.

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

    }

Програмата ја отвора портата на серверот, ги чита податоците од него додека не наиде на краен знак на команда, ја пренесува командата на нов процес и го пренасочува излезот од процесот во сокетот. Сè е едноставно како автоматска пушка калашников.

Според тоа, има преоптоварување за оваа функција со стандардна порта:

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

Па, соодветно, функцијата што го запира серверот е исто така тривијална, ја прекинува вечната јамка, нарушувајќи ја неговата состојба.

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

Нема да дадам тест класи овде, тие се подолу - сè што прават е да ја проверат функционалноста на јавните методи. Сè е на чекор.

Да резимираме, за неколку вечери можете да ги разберете принципите на работа на главните комунални услуги на конзолата. Сега, кога телефонираме на оддалечен компјутер, разбираме што се случува - магијата исчезна)

Значи, врските:
Сите извори беа, се и ќе бидат тука
За Телнет
Повеќе за Телнет

Извор: www.habr.com

Додадете коментар