การเขียนซอฟต์แวร์ด้วยฟังก์ชันการทำงานของยูทิลิตี้ไคลเอ็นต์-เซิร์ฟเวอร์ Windows ตอนที่ 01

Приветствую

วันนี้ฉันอยากจะดูกระบวนการเขียนแอปพลิเคชันไคลเอนต์-เซิร์ฟเวอร์ที่ทำหน้าที่ของยูทิลิตี้ Windows มาตรฐาน เช่น Telnet, TFTP และอื่นๆ อีกมากมายใน Java ล้วนๆ เห็นได้ชัดว่าฉันจะไม่นำสิ่งใหม่ๆ มาใช้ - ยูทิลิตี้ทั้งหมดนี้ทำงานได้สำเร็จมานานกว่าหนึ่งปี แต่ฉันเชื่อว่าไม่ใช่ทุกคนที่รู้ว่าเกิดอะไรขึ้นภายใต้ประทุน

นี่คือสิ่งที่จะมีการหารือภายใต้การตัด

ในบทความนี้เพื่อไม่ให้ลากออกไปนอกเหนือจากข้อมูลทั่วไปฉันจะเขียนเกี่ยวกับเซิร์ฟเวอร์ Telnet เท่านั้น แต่ในขณะนี้ยังมีเนื้อหาเกี่ยวกับยูทิลิตี้อื่น ๆ ด้วย - มันจะอยู่ในส่วนต่อไปของซีรีส์

ก่อนอื่น คุณต้องเข้าใจว่า Telnet คืออะไร จำเป็นสำหรับอะไร และใช้ทำอะไร ฉันจะไม่อ้างอิงแหล่งที่มาทุกคำ (หากจำเป็นฉันจะแนบลิงก์ไปยังเนื้อหาในหัวข้อท้ายบทความ) ฉันจะบอกว่า Telnet ให้การเข้าถึงบรรทัดคำสั่งของอุปกรณ์จากระยะไกลเท่านั้น โดยทั่วไปแล้ว นี่คือจุดที่ฟังก์ชันการทำงานของมันสิ้นสุดลง (ฉันจงใจเงียบเกี่ยวกับการเข้าถึงพอร์ตเซิร์ฟเวอร์ และจะมีข้อมูลเพิ่มเติมในภายหลัง) ซึ่งหมายความว่าในการนำไปใช้งาน เราจำเป็นต้องยอมรับบรรทัดบนไคลเอ็นต์ ส่งต่อไปยังเซิร์ฟเวอร์ พยายามส่งต่อไปยังบรรทัดคำสั่ง อ่านการตอบสนองของบรรทัดคำสั่ง ถ้ามี ให้ส่งต่อไปยังไคลเอ็นต์ และ แสดงบนหน้าจอ หรือหากเกิดข้อผิดพลาด ให้แจ้งให้ผู้ใช้ทราบว่ามีบางอย่างผิดปกติ

เพื่อดำเนินการตามข้างต้น เราจำเป็นต้องมีคลาสการทำงาน 2 คลาสและคลาสทดสอบบางคลาสซึ่งเราจะเปิดใช้เซิร์ฟเวอร์และไคลเอ็นต์จะทำงานผ่านนั้น
ดังนั้น ในขณะนี้ โครงสร้างการสมัคร รวมถึง:

  • Telnetไคลเอนต์
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

มาดูแต่ละเรื่องกัน:

Telnetไคลเอนต์

คลาสทั้งหมดนี้ควรทำได้คือส่งคำสั่งที่ได้รับและแสดงการตอบกลับที่ได้รับ นอกจากนี้ คุณจะต้องสามารถเชื่อมต่อกับพอร์ตใดก็ได้ (ตามที่กล่าวไว้ข้างต้น) ของอุปกรณ์ระยะไกลและตัดการเชื่อมต่อจากอุปกรณ์นั้น

เพื่อให้บรรลุเป้าหมายนี้ จึงมีการนำฟังก์ชันต่อไปนี้ไปใช้:

ฟังก์ชันที่ใช้ที่อยู่ซ็อกเก็ตเป็นอาร์กิวเมนต์ เปิดการเชื่อมต่อและเริ่มสตรีมอินพุตและเอาต์พุต (ตัวแปรสตรีมได้รับการประกาศไว้ด้านบน แหล่งที่มาทั้งหมดอยู่ที่ส่วนท้ายของบทความ)

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

ฟังก์ชั่นเดียวกันมากเกินไปโดยเชื่อมต่อกับพอร์ตเริ่มต้น - สำหรับ telnet นี่คือ 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();
    }
}

TelnetServer

คลาสนี้ต้องมีฟังก์ชันการรับคำสั่งจากซ็อกเก็ต ส่งเพื่อดำเนินการ และส่งการตอบสนองจากคำสั่งกลับไปยังซ็อกเก็ต โปรแกรมจงใจไม่ตรวจสอบข้อมูลอินพุตเพราะประการแรกแม้ใน "เทลเน็ตแบบบรรจุกล่อง" ก็เป็นไปได้ที่จะฟอร์แมตดิสก์เซิร์ฟเวอร์และประการที่สองประเด็นด้านความปลอดภัยในบทความนี้ถูกละเว้นในหลักการและนั่นคือสาเหตุที่ไม่มี คำเกี่ยวกับการเข้ารหัสหรือ 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();

    }

โปรแกรมจะเปิดพอร์ตเซิร์ฟเวอร์ อ่านข้อมูลจนกระทั่งพบอักขระสิ้นสุดคำสั่ง ส่งคำสั่งไปยังกระบวนการใหม่ และเปลี่ยนเส้นทางเอาต์พุตจากกระบวนการไปยังซ็อกเก็ต ทุกอย่างง่ายดายเหมือนปืนไรเฟิลจู่โจม Kalashnikov

ดังนั้นจึงมีการโอเวอร์โหลดสำหรับฟังก์ชันนี้ด้วยพอร์ตเริ่มต้น:

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

ดังนั้นฟังก์ชั่นที่หยุดเซิร์ฟเวอร์จึงเป็นเรื่องเล็กน้อยเช่นกันมันขัดจังหวะการวนซ้ำชั่วนิรันดร์โดยละเมิดเงื่อนไขของมัน

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

ฉันจะไม่จัดชั้นเรียนทดสอบที่นี่ ชั้นเรียนอยู่ด้านล่าง - สิ่งที่พวกเขาทำคือตรวจสอบการทำงานของวิธีการสาธารณะ ทุกอย่างอยู่ที่คอมไพล์

โดยสรุปในช่วงเย็นคุณสามารถเข้าใจหลักการทำงานของยูทิลิตี้คอนโซลหลักได้ ตอนนี้เมื่อเราเทเลเน็ตไปยังคอมพิวเตอร์ระยะไกล เราเข้าใจสิ่งที่เกิดขึ้น - ความมหัศจรรย์ได้หายไป)

ดังนั้นลิงค์:
แหล่งที่มาทั้งหมดเป็นอยู่และจะอยู่ที่นี่
เกี่ยวกับเทลเน็ต
ข้อมูลเพิ่มเติมเกี่ยวกับเทลเน็ต

ที่มา: will.com

เพิ่มความคิดเห็น