விண்டோஸ் கிளையன்ட்-சர்வர் பயன்பாடுகளின் செயல்பாட்டுடன் எழுதும் மென்பொருள், பகுதி 02

விண்டோஸ் கன்சோல் பயன்பாடுகளின் தனிப்பயன் செயலாக்கங்களுக்கு அர்ப்பணிக்கப்பட்ட தொடர் கட்டுரைகளைத் தொடர்ந்து, ஒரு எளிய கோப்பு பரிமாற்ற நெறிமுறையான TFTP (Trivial File Transfer Protocol) -ஐத் தொடுவதைத் தவிர்க்க முடியாது.

கடைசியாக, கோட்பாட்டை சுருக்கமாகப் பார்ப்போம், தேவையானதைப் போன்ற செயல்பாட்டைச் செயல்படுத்தும் குறியீட்டைப் பார்த்து, அதை பகுப்பாய்வு செய்வோம். மேலும் விவரங்கள் - வெட்டு கீழ்

நான் குறிப்புத் தகவலை நகலெடுத்து ஒட்ட மாட்டேன், கட்டுரையின் முடிவில் பாரம்பரியமாக காணக்கூடிய இணைப்புகள், TFTP என்பது FTP நெறிமுறையின் எளிமைப்படுத்தப்பட்ட மாறுபாடு என்று மட்டுமே கூறுவேன், இதில் அணுகல் கட்டுப்பாட்டு அமைப்பு உள்ளது. அகற்றப்பட்டது, உண்மையில் கோப்பைப் பெறுவதற்கும் மாற்றுவதற்கும் கட்டளைகளைத் தவிர வேறு எதுவும் இங்கு இல்லை . எவ்வாறாயினும், எங்கள் செயலாக்கத்தை இன்னும் கொஞ்சம் நேர்த்தியாகவும், தற்போதைய குறியீடு எழுதும் கொள்கைகளுக்கு ஏற்பவும் மாற்ற, தொடரியல் சிறிது மாற்றப்பட்டுள்ளது - இது செயல்பாட்டுக் கொள்கைகளை மாற்றாது, ஆனால் இடைமுகம், IMHO, இன்னும் கொஞ்சம் தர்க்கரீதியாக மாறும் மற்றும் FTP மற்றும் TFTP இன் நேர்மறையான அம்சங்களை ஒருங்கிணைக்கிறது.

குறிப்பாக, தொடங்கும் போது, ​​கிளையன்ட் சேவையகத்தின் ஐபி முகவரி மற்றும் தனிப்பயன் TFTP திறந்திருக்கும் போர்ட்டைக் கோருகிறது (நிலையான நெறிமுறையுடன் பொருந்தாததால், ஒரு போர்ட்டைத் தேர்ந்தெடுக்கும் திறனை பயனருக்கு விட்டுவிடுவது பொருத்தமானது என்று நான் கருதினேன்), அதன் பிறகு ஒரு இணைப்பு ஏற்படுகிறது, இதன் விளைவாக கிளையன்ட் கட்டளைகளில் ஒன்றை அனுப்ப முடியும் - பெறவும் அல்லது வைக்கவும், ஒரு கோப்பைப் பெற அல்லது சேவையகத்திற்கு அனுப்பவும். தர்க்கத்தை எளிதாக்க அனைத்து கோப்புகளும் பைனரி பயன்முறையில் அனுப்பப்படுகின்றன.

நெறிமுறையைச் செயல்படுத்த, நான் பாரம்பரியமாக 4 வகுப்புகளைப் பயன்படுத்தினேன்:

  • TFTP கிளையண்ட்
  • TFTPSசர்வர்
  • TFTPClientTester
  • TFTPServerTester

முக்கியவற்றை பிழைத்திருத்தத்திற்கு மட்டுமே சோதனை வகுப்புகள் இருப்பதால், நான் அவற்றை பகுப்பாய்வு செய்ய மாட்டேன், ஆனால் குறியீடு களஞ்சியத்தில் இருக்கும்; அதற்கான இணைப்பை கட்டுரையின் முடிவில் காணலாம். இப்போது நான் முக்கிய வகுப்புகளைப் பார்க்கிறேன்.

TFTP கிளையண்ட்

இந்த வகுப்பின் பணியானது ரிமோட் சர்வருடன் அதன் ஐபி மற்றும் போர்ட் எண் மூலம் இணைப்பது, உள்ளீட்டு ஸ்ட்ரீமில் இருந்து ஒரு கட்டளையைப் படிப்பது (இந்த விஷயத்தில், விசைப்பலகை), அதை அலசுவது, சேவையகத்திற்கு மாற்றுவது மற்றும் நீங்கள் உள்ளதா என்பதைப் பொறுத்து ஒரு கோப்பை அனுப்ப அல்லது பெற, அதை மாற்ற அல்லது பெற வேண்டும்.

சேவையகத்துடன் இணைக்க கிளையண்டைத் தொடங்குவதற்கான குறியீடு மற்றும் உள்ளீட்டு ஸ்ட்ரீமில் இருந்து ஒரு கட்டளைக்காக காத்திருக்கவும். இங்கே பயன்படுத்தப்படும் பல உலகளாவிய மாறிகள் கட்டுரைக்கு வெளியே, நிரலின் முழு உரையில் விவரிக்கப்பட்டுள்ளன. அவற்றின் அற்பத்தன்மையின் காரணமாக, கட்டுரையை ஓவர்லோட் செய்யாமல் இருக்க நான் அவற்றை மேற்கோள் காட்டவில்லை.

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

கிளையன்ட் விண்டோவில் கெட் அல்லது புட் தவிர வேறு கட்டளை உள்ளிடப்பட்டால், உள்ளீடு தவறானது என்பதைக் குறிக்கும் வகையில் showErrorMessage செயல்பாடு அழைக்கப்படும். அற்பத்தன்மை காரணமாக, நான் அதை மேற்கோள் காட்ட மாட்டேன். உள்ளீட்டு சரத்தைப் பெறுதல் மற்றும் பிரித்தல் செயல்பாடு சற்று சுவாரஸ்யமானது. நாங்கள் ஸ்கேனரை அதில் அனுப்புகிறோம், அதில் இருந்து இரண்டு இடைவெளிகளால் பிரிக்கப்பட்ட மற்றும் கட்டளை, மூல முகவரி மற்றும் இலக்கு முகவரி ஆகியவற்றைக் கொண்ட ஒரு வரியைப் பெற எதிர்பார்க்கிறோம்.

    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++ இலிருந்து செயல்பாடு சுட்டிகள் போன்ற சில விஷயங்கள் ஜாவாவில் இல்லாததே ஆகும். குறைந்தபட்சம் பயங்கரமான மற்றும் பயங்கரமான கோட்டோ, இதை அழகாக செயல்படுத்த உங்களை அனுமதிக்கிறது. குறியீட்டை இன்னும் கொஞ்சம் நேர்த்தியாக மாற்றுவது எப்படி என்று உங்களுக்குத் தெரிந்தால், கருத்துகளில் விமர்சனங்களை வரவேற்கிறேன். இங்கே சரம்-பிரதிநிதி அகராதி தேவை என்று எனக்குத் தோன்றுகிறது, ஆனால் பிரதிநிதி இல்லை...

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

சுருக்க:

ஒரு எளிய தரவு பரிமாற்ற நெறிமுறையில் எங்கள் சொந்த மாறுபாட்டை நாங்கள் எழுதி, அது எவ்வாறு செயல்பட வேண்டும் என்பதைக் கண்டுபிடித்தோம். கொள்கையளவில், நான் இங்கு அமெரிக்காவைக் கண்டுபிடிக்கவில்லை, புதிய விஷயங்களை எழுதவில்லை, ஆனால் ஹப்ரேயில் இதே போன்ற கட்டுரைகள் எதுவும் இல்லை, மேலும் cmd பயன்பாடுகளைப் பற்றிய தொடர் கட்டுரைகளை எழுதுவதன் ஒரு பகுதியாக அதைத் தொடாமல் இருக்க முடியாது.

மேற்கோள்கள்:

மூல குறியீடு களஞ்சியம்
TFTP பற்றி சுருக்கமாக
அதே விஷயம், ஆனால் ரஷ்ய மொழியில்

ஆதாரம்: www.habr.com

கருத்தைச் சேர்