Ritun hugbúnaðar með virkni Windows biðlara-miðlara tólum, hluti 02

Í framhaldi af áframhaldandi greinaröð sem helguð er sérsniðnum útfærslum á Windows stjórnborðsforritum, getum við ekki annað en snert TFTP (Trivial File Transfer Protocol) - einföld skráaflutningssamskiptareglur.

Eins og síðast, skulum við fara stuttlega yfir kenninguna, sjá kóðann sem útfærir virkni svipaða þeirri sem krafist er og greina hana. Nánari upplýsingar - undir klippingu

Ég mun ekki afrita og líma tilvísunarupplýsingar, sem venjulega er hægt að finna tengla á í lok greinarinnar, ég mun aðeins segja að í grunninn er TFTP einfölduð afbrigði af FTP-samskiptareglunum, þar sem aðgangsstýringarstillingin hefur verið fjarlægð, og í raun er ekkert hér nema skipanir til að taka á móti og flytja skrá . Hins vegar, til þess að gera útfærslu okkar aðeins glæsilegri og aðlagaðar að núverandi meginreglum um að skrifa kóða, hefur setningafræðinni verið breytt lítillega - þetta breytir ekki meginreglum rekstrarins, en viðmótið, IMHO, verður aðeins rökréttara og sameinar jákvæða þætti FTP og TFTP.

Sérstaklega, þegar ræst er, biður viðskiptavinurinn um IP-tölu netþjónsins og gáttina sem sérsniðið TFTP er opið á (vegna ósamrýmanleika við staðlaða samskiptareglur taldi ég rétt að láta notandanum geta valið tengi), eftir það tenging á sér stað, þar af leiðandi getur viðskiptavinurinn sent eina af skipunum - fá eða setja, til að taka á móti eða senda skrá á netþjóninn. Allar skrár eru sendar í tvíundarstillingu til að einfalda rökfræðina.

Til að innleiða siðareglur notaði ég venjulega 4 flokka:

  • TFTPClient
  • TFTPServer
  • TFTPClientTester
  • TFTPServerTester

Vegna þess að prófunarflokkar eru aðeins til til að kemba þá helstu mun ég ekki greina þá, en kóðinn verður í geymslunni; tengil á hann er að finna í lok greinarinnar. Nú mun ég skoða helstu flokka.

TFTPClient

Verkefni þessa flokks er að tengjast ytri netþjóni með ip og gáttarnúmeri hans, lesa skipun úr inntaksstraumnum (í þessu tilfelli lyklaborðinu), flokka hann, flytja hann yfir á netþjóninn og eftir því hvort þú þarf að senda eða taka á móti skrá, flytja hana eða fá.

Kóðinn til að ræsa biðlarann ​​til að tengjast þjóninum og bíða eftir skipun frá inntaksstraumnum lítur svona út. Nokkrar alþjóðlegar breytur sem notaðar eru hér eru lýst utan greinarinnar, í heildartexta forritsins. Vegna léttvægis þeirra vitna ég ekki í þær til að ofhlaða ekki greininni.

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

Við skulum fara yfir aðferðirnar sem kallast í þessum kóðablokk:

Hér er skráin send - með því að nota skanna, kynnum við innihald skráarinnar sem fylki af bætum, sem við skrifum eitt af öðru í falsið, eftir það lokum við henni og opnum aftur (ekki augljósasta lausnin, en það tryggir losun auðlinda), eftir það birtum við skilaboð um árangursríkan flutning.

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

Þetta kóðabrot lýsir því að sækja gögn frá þjóninum. Allt er aftur léttvægt, aðeins fyrsti kóðablokkinn er áhugaverður. Til þess að skilja nákvæmlega hversu mörg bæti þarf að lesa úr falsinu þarftu að vita hversu mikið flutt skráin vegur. Skráarstærðin á þjóninum er sýnd sem löng heiltala, svo 4 bæti eru samþykkt hér, sem síðan er breytt í eina tölu. Þetta er ekki mjög Java nálgun, hún er frekar svipuð fyrir SI, en hún leysir vandamál þess.

Þá er allt léttvægt - við fáum þekktan fjölda bæta úr falsinu og skrifum þau í skrá, eftir það birtum við árangursskilaboð.

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

Ef önnur skipun en get eða put var færð inn í biðlaragluggann verður showErrorMessage aðgerðin kölluð, sem gefur til kynna að inntakið hafi verið rangt. Vegna léttvægis mun ég ekki vitna í það. Nokkuð áhugaverðara er virkni þess að taka á móti og skipta inntaksstrengnum. Við sendum skannann inn í hann, þaðan sem við gerum ráð fyrir að fá línu sem er aðskilin með tveimur bilum og inniheldur skipunina, uppruna heimilisfang og áfangastað.

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

Sendir skipun — sendir skipunina sem slegin er inn úr skannanum yfir í innstunguna og þvingar hana til sendingar

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

Valur er aðgerð sem ákvarðar aðgerðir forritsins eftir innslátnum streng. Allt hér er ekki mjög fallegt og bragðið sem notað er er ekki það besta með þvinguðum útgangi fyrir utan kóðablokkina, en aðalástæðan fyrir því er skortur á sumum hlutum í Java, eins og fulltrúa í C#, fallbendingar frá C++, eða kl. allavega hið hræðilega og hræðilega goto, sem gerir þér kleift að útfæra þetta fallega. Ef þú veist hvernig á að gera kóðann aðeins glæsilegri þá fagna ég gagnrýni í athugasemdunum. Mér sýnist að hér vanti String-delega-orðabók, en það er enginn fulltrúi...

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

TFTPServer

Virkni þjónsins er frábrugðin virkni viðskiptavinarins, að mestu leyti, aðeins að því leyti að skipanir koma ekki til hans frá lyklaborðinu, heldur frá falsinu. Sumar aðferðirnar eru almennt þær sömu, svo ég mun ekki vitna í þær, ég mun aðeins koma inn á muninn.

Til að byrja með er keyrsluaðferðin notuð sem tekur við porti sem inntak og vinnur inntaksgögnin úr falsinu í eilífri lykkju.

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

Put aðferðin, sem umlykur writeToFileFromSocket aðferðina sem opnar skrifstraum í skrá og skrifar öll inntaksbæti úr falsinu, sýnir skilaboð sem gefa til kynna að flutningnum sé lokið þegar rituninni er lokið.

    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 aðferðin sækir netþjónsskrána. Eins og áður hefur komið fram í hlutanum um biðlarahlið forritsins, til að flytja skrá með góðum árangri þarftu að vita stærð hennar, geymd í langri heiltölu, svo ég skipti henni í 4 bæti fylki, flytti þau bæti fyrir bæti í innstunguna og síðan, eftir að hafa tekið við og sett þau saman á biðlarann ​​í númer til baka, flyt ég öll bæti sem mynda skrána, lesin úr inntaksstraumnum úr skránni.


 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 aðferðin er sú sama og í biðlaranum, eini munurinn er sá að hún les gögn úr falsinu frekar en frá lyklaborðinu. Kóðinn er í geymslunni, alveg eins og selector.
Í þessu tilviki er frumstillingin sett í sérstakan kóðablokk, vegna þess að innan þessarar útfærslu, eftir að flutningi er lokið, eru tilföng losuð og tekin aftur - aftur til að veita vernd gegn minnisleka.

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

Til að draga saman:

Við höfum nýlega skrifað okkar eigið afbrigði af einfaldri gagnaflutningssamskiptareglu og fundið út hvernig það ætti að virka. Í grundvallaratriðum uppgötvaði ég Ameríku ekki hér og skrifaði ekki mikið nýtt, en það voru engar svipaðar greinar um Habré, og sem hluti af því að skrifa röð greina um cmd tól var ómögulegt annað en að snerta það.

Tilvísanir:

Geymsla frumkóða
Stuttlega um TFTP
Það sama, en á rússnesku

Heimild: www.habr.com

Bæta við athugasemd