የሶፍትዌር መፃፍ ከዊንዶውስ ደንበኛ-አገልጋይ መገልገያዎች ተግባር ጋር፣ ክፍል 02

ለዊንዶ መሥሪያ መገልገያዎች ብጁ አተገባበር የተሰጡ ቀጣይ ተከታታይ መጣጥፎችን በመቀጠል፣ TFTP (ትሪቪያል ፋይል ማስተላለፍ ፕሮቶኮልን) - ቀላል የፋይል ማስተላለፊያ ፕሮቶኮልን ከመንካት መውጣት አንችልም።

እንደባለፈው ጊዜ፣ ቲዎሪውን ባጭሩ እንቃኝ፣ ከሚፈለገው ጋር የሚመሳሰል ተግባርን የሚፈጽም ኮድ እንይ እና እንመርምረው። ተጨማሪ ዝርዝሮች - በቆራጩ ስር

የማጣቀሻ መረጃን አልቀዳም ፣ በአንቀጹ መጨረሻ ላይ በተለምዶ ሊገኙ የሚችሉ አገናኞች ፣ እኔ እላለሁ ፣ በዋናው ፣ TFTP ቀላል የኤፍቲፒ ፕሮቶኮል ልዩነት ነው ፣ እሱም የመዳረሻ መቆጣጠሪያ ቅንብሩ ያለው። ተወግዷል, እና እንዲያውም እዚህ ምንም ነገር የለም ፋይል ለመቀበል እና ለማስተላለፍ ከትእዛዞች በስተቀር. ሆኖም ግን, የእኛን ትግበራ ትንሽ ይበልጥ የሚያምር እና አሁን ካለው የአጻጻፍ ኮድ መርሆዎች ጋር እንዲጣጣም ለማድረግ, አገባቡ በትንሹ ተለውጧል - ይህ የአሠራር መርሆችን አይለውጥም, ግን በይነገጽ, IMHO, ትንሽ ምክንያታዊ ይሆናል እና የኤፍቲፒ እና TFTP አወንታዊ ገጽታዎችን ያጣምራል።

በተለይም ሲጀመር ደንበኛው የአገልጋዩን አይፒ አድራሻ እና ብጁ TFTP የሚከፈትበትን ወደብ ይጠይቃል (ከመደበኛ ፕሮቶኮል ጋር ተኳሃኝ ባለመሆኑ ምክንያት ተጠቃሚው ወደብ የመምረጥ ችሎታ መተው ተገቢ ነው ብዬ አስቤ ነበር) ከዚያ በኋላ ሀ ግንኙነት ይከሰታል, በዚህ ምክንያት ደንበኛው ከትእዛዛቱ ውስጥ አንዱን መላክ - ማግኘት ወይም ማስቀመጥ, ፋይልን ወደ አገልጋዩ ለመቀበል ወይም ለመላክ. አመክንዮውን ለማቃለል ሁሉም ፋይሎች በሁለትዮሽ ሁነታ ይላካሉ።

ፕሮቶኮሉን ለመተግበር በተለምዶ 4 ክፍሎችን እጠቀም ነበር፡

  • TFTPClient
  • TFTPS አገልጋይ
  • TFTPClient ሞካሪ
  • TFTPSserver ሞካሪ

የፈተና ክፍሎች ዋና ዋናዎቹን ለማረም ብቻ በመኖራቸው ምክንያት እነሱን አልመረምርም ፣ ግን ኮዱ በማጠራቀሚያው ውስጥ ይሆናል ፣ ከእሱ ጋር ያለው አገናኝ በአንቀጹ መጨረሻ ላይ ሊገኝ ይችላል። አሁን ዋናዎቹን ክፍሎች እመለከታለሁ.

TFTPClient

የዚህ ክፍል ተግባር ከርቀት አገልጋይ ጋር በአይፒ እና በፖርት ቁጥሩ መገናኘት ፣ ከግቤት ዥረቱ ላይ ያለውን ትእዛዝ ማንበብ (በዚህ ሁኔታ ፣ የቁልፍ ሰሌዳ) ፣ መተንተን ፣ ወደ አገልጋዩ ያስተላልፉ እና ፣ እንደ እርስዎ ወይም አለመሆኑን ይወስኑ ። ፋይል መላክ ወይም መቀበል፣ ማስተላለፍ ወይም ማግኘት ያስፈልጋል።

ደንበኛው ከአገልጋዩ ጋር ለመገናኘት እና ከግቤት ዥረቱ ትዕዛዝ ለመጠበቅ ደንበኛው የማስጀመር ኮድ ይህን ይመስላል። እዚህ ጥቅም ላይ የሚውሉ በርካታ ዓለም አቀፍ ተለዋዋጮች ከጽሑፉ ውጭ ተገልጸዋል፣ በፕሮግራሙ ሙሉ ጽሑፍ። በጥቃቅንነታቸው ምክንያት ጽሑፉን ከመጠን በላይ ላለመጫን አልጠቅሳቸውም።

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

በዚህ የብሎክ ኮድ ውስጥ ያሉትን ዘዴዎች እንመልከት፡-

እዚህ ፋይሉ ተልኳል - ስካነርን በመጠቀም የፋይሉን ይዘቶች እንደ ባይት ድርድር እናቀርባለን ፣ አንድ በአንድ ወደ ሶኬት እንጽፋለን ፣ ከዚያ በኋላ እንዘጋዋለን እና እንደገና እንከፍታለን (በጣም ግልፅ መፍትሄ አይደለም ፣ ግን ሀብቶችን ለመልቀቅ ዋስትና ይሰጣል), ከዚያ በኋላ ስለ ስኬታማ ማስተላለፍ መልእክት እናሳያለን.

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

ይህ የኮድ ቁራጭ ውሂብን ከአገልጋዩ ሰርስሮ ማውጣትን ይገልጻል። ሁሉም ነገር እንደገና ቀላል ነው, የመጀመሪያው የኮድ እገዳ ብቻ ፍላጎት አለው. ከሶኬት ውስጥ ምን ያህል ባይት ማንበብ እንዳለበት በትክክል ለመረዳት, የተላለፈው ፋይል ምን ያህል ክብደት እንዳለው ማወቅ ያስፈልግዎታል. በአገልጋዩ ላይ ያለው የፋይል መጠን እንደ ረጅም ኢንቲጀር ነው የሚወከለው፣ ስለዚህ 4 ባይት እዚህ ይቀበላሉ፣ እነሱም ወደ አንድ ቁጥር ይቀየራሉ። ይህ በጣም የጃቫ አካሄድ አይደለም፣ ለSI ይልቁንስ ተመሳሳይ ነው፣ ግን ችግሩን ይፈታል።

ከዚያ ሁሉም ነገር ቀላል ነው - የሚታወቅ የባይት ብዛት ከሶኬት እንቀበላለን እና ወደ ፋይል እንጽፋለን ፣ ከዚያ በኋላ የስኬት መልእክት እናሳያለን።

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

ከማግኘት ወይም ከማስቀመጥ ውጭ ሌላ ትዕዛዝ በደንበኛው መስኮት ውስጥ ከገባ፣የማሳየቱ ስህተት መልእክት ተግባር ይጠራል፣ይህም ግቤቱ የተሳሳተ መሆኑን ያሳያል። በጥቃቅን ነገር ምክንያት፣ አልጠቅሰውም። በተወሰነ ደረጃ የበለጠ ትኩረት የሚስበው የግቤት ሕብረቁምፊውን የመቀበል እና የመከፋፈል ተግባር ነው። ስካነሩን ወደ እሱ እናስተላልፋለን, ከእሱ ውስጥ በሁለት ቦታዎች የተከፈለ እና የትዕዛዝ, የምንጭ አድራሻ እና የመድረሻ አድራሻን የያዘ መስመር እንቀበላለን ብለን እንጠብቃለን.

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

ትዕዛዝ በመላክ ላይ - ከስካነር የገባውን ትዕዛዝ ወደ ሶኬት ያስተላልፋል እና እንዲላክ ያስገድዳል

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

መራጭ በገባው ሕብረቁምፊ ላይ በመመስረት የፕሮግራሙን ተግባራት የሚወስን ተግባር ነው። እዚህ ያለው ሁሉም ነገር በጣም ቆንጆ አይደለም እና ጥቅም ላይ የዋለው ብልሃት ከኮድ እገዳ ውጭ በግዳጅ መውጣት የተሻለ አይደለም, ነገር ግን ለዚህ ዋነኛው ምክንያት በጃቫ ውስጥ አንዳንድ ነገሮች አለመኖር ነው, ለምሳሌ በ C # ውስጥ ያሉ ልዑካን, የተግባር ጠቋሚዎች ከ C ++ ወይም በ. ይህንን በሚያምር ሁኔታ እንዲተገብሩ የሚፈቅድልዎ ቢያንስ አስፈሪ እና አስፈሪ ጎቶ. ኮዱን እንዴት ትንሽ ቆንጆ ማድረግ እንደሚችሉ ካወቁ በአስተያየቶቹ ውስጥ ትችቶችን በደስታ እቀበላለሁ። ለእኔ ይመስለኛል የ String-delegate መዝገበ-ቃላት እዚህ ያስፈልጋል፣ ግን ምንም ተወካይ የለም...

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

TFTPS አገልጋይ

የአገልጋዩ ተግባራዊነት ከደንበኛው ተግባር, በአጠቃላይ, ከቁልፍ ሰሌዳው ሳይሆን ከሶኬት ወደ እሱ የሚመጡት ትዕዛዞች ብቻ ናቸው. አንዳንዶቹ ዘዴዎች በአጠቃላይ ተመሳሳይ ናቸው, ስለዚህ እነሱን አልጠቅስም, ልዩነቶቹን ብቻ እነካለሁ.

ለመጀመር፣ የሩጫ ዘዴ ጥቅም ላይ ይውላል፣ እሱም ወደብ እንደ ግብአት የሚቀበል እና የግብአት ውሂቡን ከሶኬት በዘለአለማዊ ዑደት ውስጥ ያስኬዳል።

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

በፋይል ላይ የመፃፍ ዥረት የሚከፍተውን እና ሁሉንም የግቤት ባይት ከሶኬት የሚፅፈው የፃፍ ቶፋይሌፍሮም ሶኬት ዘዴን ያጠቃለለ ፣ፅሁፉ ሲጠናቀቅ ዝውውሩን በተሳካ ሁኔታ ማጠናቀቁን የሚያመለክት መልእክት ያሳያል።

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

የማግኛ ዘዴው የአገልጋይ ፋይሉን ሰርስሮ ያወጣል። ቀደም ሲል በፕሮግራሙ የደንበኛ ክፍል ላይ ባለው ክፍል ላይ እንደተገለፀው አንድን ፋይል በተሳካ ሁኔታ ለማስተላለፍ በረጅም ኢንቲጀር ውስጥ የተከማቸበትን መጠን ማወቅ ያስፈልግዎታል ፣ ስለሆነም በ 4 ባይት ድርድር እከፍላለሁ ፣ ባይት-ባይት አስተላልፋቸዋለሁ ። ወደ ሶኬት ፣ እና ከዚያ በኋላ ፣ በደንበኛው ላይ በመቀበል እና በማሰባሰብ ወደ ኋላ ቁጥር ፣ ፋይሉን የሚሠሩትን ሁሉንም ባይቶች አስተላልፋለሁ ፣ ከፋይሉ የግብዓት ዥረት አንብቤያለሁ ።


 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 ዘዴ ከደንበኛው ጋር ተመሳሳይ ነው, ልዩነቱ ከቁልፍ ሰሌዳው ላይ ሳይሆን ከሶኬት ላይ ያለውን መረጃ ማንበብ ብቻ ነው. ኮዱ ልክ እንደ መራጭ በማከማቻው ውስጥ አለ።
በዚህ ሁኔታ, አጀማመር በተለየ የኮድ እገዳ ውስጥ ተቀምጧል, ምክንያቱም በዚህ ትግበራ ውስጥ ፣ ዝውውሩ ከተጠናቀቀ በኋላ ፣ ሀብቶች ተለቀቁ እና እንደገና ተይዘዋል - እንደገና ከማስታወሻ ፍሳሾች ለመከላከል።

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

ለማሳጠር:

የራሳችንን ልዩነት በቀላል የውሂብ ማስተላለፍ ፕሮቶኮል ላይ ጽፈናል እና እንዴት እንደሚሰራ አውቀናል. በመርህ ደረጃ, አሜሪካን እዚህ አላገኘሁም እና ብዙ አዳዲስ ነገሮችን አልጻፍኩም, ነገር ግን በ Habré ላይ ተመሳሳይ መጣጥፎች አልነበሩም, እና ስለ cmd መገልገያዎች ተከታታይ መጣጥፎችን እንደመጻፍ, እሱን መንካት አይቻልም.

ማጣቀሻዎች

የምንጭ ኮድ ማከማቻ
ስለ TFTP በአጭሩ
ተመሳሳይ ነገር, ግን በሩሲያኛ

ምንጭ: hab.com

አስተያየት ያክሉ