Windows mijoz-server yordam dasturlari bilan dasturiy ta'minotni yozish, 02-qism

Windows konsoli yordamchi dasturlarini maxsus tatbiq etishga bag'ishlangan davom etayotgan maqolalar seriyasini davom ettirar ekanmiz, biz TFTP (Trivial File Transfer Protocol) - oddiy fayl uzatish protokoliga tegmasdan ilojimiz yo'q.

Oxirgi marta bo'lgani kabi, keling, nazariyani qisqacha ko'rib chiqamiz, talab qilinadigan funksiyaga o'xshash funksiyalarni amalga oshiradigan kodni ko'rib chiqamiz va uni tahlil qilamiz. Batafsil ma'lumot - kesish ostida

Men an'anaviy ravishda maqolaning oxirida havolalarni topish mumkin bo'lgan ma'lumotnoma ma'lumotlarini nusxa ko'chirmayman, faqat shuni aytamanki, TFTP FTP protokolining soddalashtirilgan o'zgarishi bo'lib, unda kirishni boshqarish sozlamalari mavjud. o'chirildi va aslida bu erda faylni qabul qilish va uzatish buyruqlaridan boshqa hech narsa yo'q. Biroq, bizning amalga oshirishimizni biroz oqlangan va kod yozishning joriy tamoyillariga moslashtirish uchun sintaksis biroz o'zgartirildi - bu ishlash tamoyillarini o'zgartirmaydi, lekin interfeys, IMHO, biroz mantiqiyroq bo'ladi va FTP va TFTP ning ijobiy tomonlarini birlashtiradi.

Xususan, ishga tushirilganda, mijoz serverning IP-manzilini va maxsus TFTP ochiq bo'lgan portni so'raydi (standart protokolga mos kelmasligi sababli men foydalanuvchiga portni tanlash imkoniyatini qoldirishni to'g'ri deb hisobladim), shundan so'ng a ulanish sodir bo'ladi, buning natijasida mijoz buyruqlardan birini yuborishi mumkin - olish yoki qo'yish, faylni serverga qabul qilish yoki yuborish. Mantiqni soddalashtirish uchun barcha fayllar ikkilik rejimda yuboriladi.

Protokolni amalga oshirish uchun men an'anaviy ravishda 4 ta sinfdan foydalanardim:

  • TFTPClient
  • TFTPS serveri
  • TFTPClientTester
  • TFTPS Server Tester

Sinov sinflari faqat asosiylarini tuzatish uchun mavjud bo'lganligi sababli, men ularni tahlil qilmayman, lekin kod omborda bo'ladi, unga havolani maqolaning oxirida topish mumkin. Endi men asosiy darslarni ko'rib chiqaman.

TFTPClient

Bu sinfning vazifasi masofaviy serverga uning IP va port raqami orqali ulanish, kirish oqimidan buyruqni o'qish (bu holda klaviatura), uni tahlil qilish, serverga o'tkazish va sizga bog'liq. faylni yuborish yoki qabul qilish, uni uzatish yoki olish kerak.

Serverga ulanish va kirish oqimidan buyruqni kutish uchun mijozni ishga tushirish kodi shunday ko'rinadi. Bu erda qo'llaniladigan bir qator global o'zgaruvchilar maqoladan tashqarida, dasturning to'liq matnida tasvirlangan. Ularning ahamiyatsizligi tufayli maqolani ortiqcha yuklamaslik uchun ularni keltirmayman.

 public void run(String ip, int port)
    {
        this.ip = ip;
        this.port = port;
        try {
            inicialization();
            Scanner keyboard = new Scanner(System.in);
            while (isRunning) {
                getAndParseInput(keyboard);
                sendCommand();
                selector();
                }
            }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

Keling, ushbu kod blokida chaqirilgan usullarni ko'rib chiqaylik:

Bu erda fayl yuboriladi - skaner yordamida biz fayl tarkibini baytlar qatori sifatida taqdim etamiz, ularni rozetkaga birma-bir yozamiz, shundan so'ng uni yopamiz va yana ochamiz (eng aniq yechim emas, lekin u resurslarni chiqarishni kafolatlaydi), shundan so'ng biz muvaffaqiyatli uzatish haqida xabarni ko'rsatamiz.

private  void put(String sourcePath, String destPath)
    {

        File src = new File(sourcePath);
        try {

            InputStream scanner = new FileInputStream(src);
            byte[] bytes = scanner.readAllBytes();
            for (byte b : bytes)
                sout.write(b);
            sout.close();
            inicialization();
            System.out.println("nDonen");
            }

        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

Ushbu kod fragmenti serverdan ma'lumotlarni olishni tavsiflaydi. Hamma narsa yana ahamiyatsiz, faqat birinchi kod bloki qiziqish uyg'otadi. Rozetkadan qancha baytni o'qish kerakligini aniq tushunish uchun siz uzatilgan faylning og'irligini bilishingiz kerak. Serverdagi fayl hajmi uzun butun son sifatida ifodalanadi, shuning uchun bu erda 4 bayt qabul qilinadi, ular keyinchalik bitta raqamga aylanadi. Bu juda Java yondashuvi emas, u SI uchun juda o'xshash, lekin u o'z muammosini hal qiladi.

Keyin hamma narsa ahamiyatsiz - biz rozetkadan ma'lum miqdordagi baytlarni olamiz va ularni faylga yozamiz, shundan so'ng biz muvaffaqiyatli xabarni ko'rsatamiz.

   private void get(String sourcePath, String destPath){
        long sizeOfFile = 0;
        try {


            byte[] sizeBytes = new byte[Long.SIZE];
           for (int i =0; i< Long.SIZE/Byte.SIZE; i++)
           {
               sizeBytes[i] = (byte)sin.read();
               sizeOfFile*=256;
               sizeOfFile+=sizeBytes[i];
           }

           FileOutputStream writer = new FileOutputStream(new File(destPath));
           for (int i =0; i < sizeOfFile; i++)
           {
               writer.write(sin.read());
           }
           writer.close();
           System.out.println("nDONEn");
       }
       catch (Exception e){
            System.out.println(e.getMessage());
       }
    }

Mijoz oynasiga get yoki put buyrug'idan boshqa buyruq kiritilgan bo'lsa, showErrorMessage funksiyasi chaqiriladi, bu esa kiritish noto'g'ri ekanligini ko'rsatadi. Arzimaganligi uchun uni keltirmayman. Kirish satrini qabul qilish va ajratish funktsiyasi biroz qiziqroq. Biz unga skanerni o'tkazamiz, undan ikkita bo'sh joy bilan ajratilgan va buyruq, manba manzili va maqsad manzilini o'z ichiga olgan qatorni olishni kutamiz.

    private void getAndParseInput(Scanner scanner)
    {
        try {

            input = scanner.nextLine().split(" ");
            typeOfCommand = input[0];
            sourcePath = input[1];
            destPath = input[2];
        }
        catch (Exception e) {
            System.out.println("Bad input");
        }
    }

Buyruq yuborish - skanerdan kiritilgan buyruqni rozetkaga uzatadi va uni yuborishga majbur qiladi

    private void sendCommand()
    {
        try {

            for (String str : input) {
                for (char ch : str.toCharArray()) {
                    sout.write(ch);
                }
                sout.write(' ');
            }
            sout.write('n');
        }
        catch (Exception e) {
            System.out.print(e.getMessage());
        }
    }

Selektor - kiritilgan qatorga qarab dastur harakatlarini aniqlaydigan funksiya. Bu erda hamma narsa juda chiroyli emas va ishlatilgan hiyla kod blokidan tashqariga majburan chiqish uchun eng yaxshisi emas, lekin buning asosiy sababi Java-da ba'zi narsalarning yo'qligi, masalan C# da delegatlar, C++ funksiyasi ko'rsatkichlari yoki kamida dahshatli va dahshatli goto, bu sizni chiroyli tarzda amalga oshirishga imkon beradi. Agar siz kodni qanday qilib biroz oqlangan qilishni bilsangiz, sharhlarda tanqidni mamnuniyat bilan qabul qilaman. Menimcha, bu erda String-delegate lug'ati kerak, ammo delegat yo'q ...

    private void selector()
    {
        do{
            if (typeOfCommand.equals("get")){
                get(sourcePath, destPath);
                break;
            }
            if (typeOfCommand.equals("put")){
                put(sourcePath, destPath);
                break;
            }
            showErrorMessage();
        }
        while (false);
    }
}

TFTPS serveri

Serverning funksionalligi mijozning funksionalligidan katta farq qiladi, faqat buyruqlar unga klaviaturadan emas, balki rozetkadan kelishi bilan farqlanadi. Ba'zi usullar odatda bir xil, shuning uchun men ularni keltirmayman, faqat farqlarga to'xtalaman.

Boshlash uchun portni kirish sifatida qabul qiladigan va rozetkadan kirish ma'lumotlarini abadiy tsiklda qayta ishlovchi ishga tushirish usuli qo'llaniladi.

    public void run(int port) {
            this.port = port;
            incialization();
            while (true) {
                getAndParseInput();
                selector();
            }
    }

Faylga yozish oqimini ochadigan va barcha kirish baytlarini rozetkadan yozadigan writeToFileFromSocket usulini o'rab turgan put usuli yozish tugagach, uzatish muvaffaqiyatli yakunlanganligini bildiruvchi xabarni ko'rsatadi.

    private  void put(String source, String dest){
            writeToFileFromSocket();
            System.out.print("nDonen");
    };
    private void writeToFileFromSocket()
    {
        try {
            FileOutputStream writer = new FileOutputStream(new File(destPath));
            byte[] bytes = sin.readAllBytes();
            for (byte b : bytes) {
                writer.write(b);
            }
            writer.close();
        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }
    }

Get usuli server faylini oladi. Dasturning mijoz tomonidagi bo'limda aytib o'tilganidek, faylni muvaffaqiyatli uzatish uchun siz uning hajmini bilishingiz kerak, uzun butun sonda saqlanadi, shuning uchun uni 4 bayt massivga ajrataman, ularni bayt-baytga o'tkazaman. rozetkaga, so'ngra ularni qabul qilib, mijozga o'rnatgandan so'ng, men faylni tashkil etuvchi barcha baytlarni fayldan kirish oqimidan o'qiyman.


 private  void get(String source, String dest){
        File sending = new File(source);
        try {
            FileInputStream readFromFile = new FileInputStream(sending);
            byte[] arr = readFromFile.readAllBytes();
            byte[] bytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(sending.length()).array();
            for (int i = 0; i<Long.SIZE / Byte.SIZE; i++)
                sout.write(bytes[i]);
            sout.flush();
            for (byte b : arr)
                sout.write(b);
        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }
    };

getAndParseInput usuli mijoz bilan bir xil, yagona farqi shundaki, u ma'lumotlarni klaviaturadan emas, balki rozetkadan o'qiydi. Kod xuddi selektor kabi omborda.
Bunday holda, ishga tushirish kodning alohida blokiga joylashtiriladi, chunki ushbu amalga oshirish doirasida, uzatish tugallangandan so'ng, resurslar bo'shatiladi va qayta ishg'ol qilinadi - yana xotira oqishidan himoya qilish uchun.

    private void  incialization()
    {
        try {
            serverSocket = new ServerSocket(port);
            socket = serverSocket.accept();
            sin = socket.getInputStream();
            sout = socket.getOutputStream();
        }
        catch (Exception e) {
            System.out.print(e.getMessage());
        }
    }

Xulosa qilish uchun:

Biz oddiy ma'lumotlarni uzatish protokoli bo'yicha o'z variantimizni yozdik va u qanday ishlashi kerakligini aniqladik. Aslida, men bu erda Amerikani kashf qilmadim va ko'p yangi narsalarni yozmadim, lekin HabrΓ©-da shunga o'xshash maqolalar yo'q edi va cmd yordam dasturlari haqida bir qator maqolalar yozishning bir qismi sifatida unga tegmaslik mumkin emas edi.

Manbalar:

Manba kodlari ombori
TFTP haqida qisqacha
Xuddi shu narsa, lekin rus tilida

Manba: www.habr.com

a Izoh qo'shish