Kuandika programu yenye utendaji wa huduma za seva ya mteja wa Windows, sehemu ya 02

Kuendeleza mfululizo unaoendelea wa makala yanayotolewa kwa ajili ya utekelezaji maalum wa huduma za dashibodi ya Windows, hatuwezi kujizuia kugusa TFTP (Itifaki Ndogo ya Kuhamisha Faili) - itifaki rahisi ya kuhamisha faili.

Kama mara ya mwisho, wacha tupitie nadharia kwa ufupi, tuone nambari inayotekelezea utendakazi sawa na ile inayohitajika, na tuichambue. Maelezo zaidi - chini ya kukata

Sitakili-kubandika habari ya kumbukumbu, viungo ambavyo vinaweza kupatikana kwa jadi mwishoni mwa kifungu, nitasema tu kwamba kwa msingi wake, TFTP ni tofauti iliyorahisishwa ya itifaki ya FTP, ambayo mpangilio wa udhibiti wa ufikiaji una. imeondolewa, na kwa kweli hakuna chochote hapa isipokuwa amri za kupokea na kuhamisha faili. Walakini, ili kufanya utekelezaji wetu kuwa wa kifahari zaidi na kuzoea kanuni za sasa za uandishi, syntax imebadilishwa kidogo - hii haibadilishi kanuni za operesheni, lakini interface, IMHO, inakuwa ya mantiki zaidi na. inachanganya vipengele vyema vya FTP na TFTP.

Hasa, inapozinduliwa, mteja huomba anwani ya IP ya seva na bandari ambayo TFTP maalum imefunguliwa (kwa sababu ya kutokubaliana na itifaki ya kawaida, niliona kuwa inafaa kumwacha mtumiaji uwezo wa kuchagua bandari), baada ya hapo a. uhusiano hutokea, kama matokeo ambayo mteja anaweza kutuma moja ya amri - kupata au kuweka, kupokea au kutuma faili kwa seva. Faili zote hutumwa katika hali ya binary ili kurahisisha mantiki.

Ili kutekeleza itifaki, jadi nilitumia madarasa 4:

  • TFTPClient
  • Seva ya TFTPS
  • TFTPClientTester
  • TFTPServerTester

Kwa sababu ya ukweli kwamba madarasa ya upimaji yanapatikana tu kwa kurekebisha zile kuu, sitazichambua, lakini nambari itakuwa kwenye hazina inaweza kupatikana mwishoni mwa kifungu. Sasa nitaangalia madarasa kuu.

TFTPClient

Kazi ya darasa hili ni kuunganishwa na seva ya mbali kwa kutumia ip na nambari ya bandari, soma amri kutoka kwa mkondo wa kuingiza (katika kesi hii, kibodi), uchanganue, uhamishe kwa seva, na, kulingana na kama wewe. haja ya kutuma au kupokea faili, kuhamisha au kupata.

Nambari ya kuzindua mteja kuunganishwa na seva na kungojea amri kutoka kwa mkondo wa kuingiza inaonekana kama hii. Idadi ya vigeu vya kimataifa vinavyotumika hapa vimeelezewa nje ya kifungu, katika maandishi kamili ya programu. Kwa sababu ya upuuzi wao, siwataji ili nisizidishe nakala hiyo.

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

Wacha tuchunguze njia zinazoitwa kwenye kizuizi hiki cha nambari:

Hapa faili inatumwa - kwa kutumia skana, tunawasilisha yaliyomo kwenye faili kama safu ya ka, ambayo tunaandika moja kwa moja kwenye tundu, baada ya hapo tunaifunga na kuifungua tena (sio suluhisho dhahiri zaidi, lakini. inahakikisha kutolewa kwa rasilimali), baada ya hapo tunaonyesha ujumbe kuhusu uhamisho uliofanikiwa.

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

Kipande hiki cha msimbo kinaelezea kurejesha data kutoka kwa seva. Kila kitu ni kidogo tena, kizuizi cha kwanza tu cha nambari ndicho cha kupendeza. Ili kuelewa kwa usahihi ni kapi nyingi zinahitajika kusoma kutoka kwenye tundu, unahitaji kujua ni kiasi gani faili iliyohamishwa ina uzito. Saizi ya faili kwenye seva inawakilishwa kama nambari kamili, kwa hivyo baiti 4 zinakubaliwa hapa, ambazo hubadilishwa kuwa nambari moja. Hii sio mbinu ya Java sana, ni sawa na SI, lakini inasuluhisha shida yake.

Kisha kila kitu ni kidogo - tunapokea idadi inayojulikana ya ka kutoka kwenye tundu na kuiandika kwenye faili, baada ya hapo tunaonyesha ujumbe wa mafanikio.

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

Ikiwa amri isipokuwa kupata au kuweka iliingizwa kwenye dirisha la mteja, kitendakazi cha showErrorMessage kitaitwa, ikionyesha kuwa ingizo lilikuwa sahihi. Kwa sababu ya ujinga, sitaitaja. Kinachovutia zaidi ni kazi ya kupokea na kugawanya kamba ya kuingiza. Tunapitisha skana ndani yake, ambayo tunatarajia kupokea mstari uliotenganishwa na nafasi mbili na iliyo na amri, anwani ya chanzo na anwani ya marudio.

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

Kutuma amri-hutuma amri iliyoingia kutoka kwa skana hadi kwenye tundu na kuilazimisha kutumwa

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

Kiteuzi ni chaguo la kukokotoa ambalo huamua vitendo vya programu kulingana na mfuatano ulioingizwa. Kila kitu hapa sio nzuri sana na hila iliyotumiwa sio bora zaidi kwa kuondoka kwa kulazimishwa nje ya kizuizi cha nambari, lakini sababu kuu ya hii ni kutokuwepo kwa Java kwa baadhi ya mambo, kama wajumbe katika C #, viashiria vya kazi kutoka C ++, au saa. angalau goto ya kutisha na ya kutisha, ambayo inakuwezesha kutekeleza hili kwa uzuri. Ikiwa unajua jinsi ya kufanya msimbo kuwa wa kifahari zaidi, ninakaribisha ukosoaji katika maoni. Inaonekana kwangu kuwa kamusi ya mjumbe wa String inahitajika hapa, lakini hakuna mjumbe ...

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

Seva ya TFTPS

Utendaji wa seva hutofautiana na utendaji wa mteja, kwa kiasi kikubwa, tu kwa kuwa amri huja kwake sio kutoka kwa kibodi, lakini kutoka kwa tundu. Baadhi ya njia kwa ujumla ni sawa, kwa hiyo sitawapa, nitagusa tu tofauti.

Kuanza, mbinu ya kukimbia hutumiwa, ambayo hupokea mlango kama ingizo na kuchakata data ya ingizo kutoka kwa tundu kwenye kitanzi cha milele.

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

Njia ya kuweka, ambayo hufunga njia ya kuandikaToFileFromSocket ambayo inafungua mkondo wa maandishi kwa faili na kuandika baiti zote za pembejeo kutoka kwa tundu, huonyesha ujumbe unaoonyesha kukamilika kwa uhamishaji wakati uandishi ukamilika.

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

Njia ya kupata inachukua faili ya seva. Kama ilivyotajwa tayari katika sehemu ya upande wa mteja wa programu, ili kuhamisha faili kwa mafanikio unahitaji kujua saizi yake, iliyohifadhiwa kwa nambari ndefu, kwa hivyo niliigawanya katika safu ya ka 4, nikizihamisha byte-by-byte. kwa tundu, na kisha, baada ya kupokea na kuwakusanya kwa mteja kwenye nambari nyuma, mimi huhamisha byte zote zinazounda faili, kusoma kutoka kwa mkondo wa pembejeo kutoka kwa faili.


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

Mbinu ya getAndParseInput ni sawa na katika mteja, tofauti pekee ni kwamba inasoma data kutoka kwenye soketi badala ya kutoka kwenye kibodi. Nambari iko kwenye hazina, kama vile kichaguzi.
Katika kesi hii, uanzishaji umewekwa kwenye kizuizi tofauti cha nambari, kwa sababu ndani ya utekelezaji huu, baada ya kukamilika kwa uhamisho, rasilimali hutolewa na kukaa tena - tena kutoa ulinzi dhidi ya uvujaji wa kumbukumbu.

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

Kwa muhtasari:

Tumeandika hivi tofauti tofauti kwenye itifaki rahisi ya uhamishaji data na kubaini jinsi inavyopaswa kufanya kazi. Kimsingi, sikugundua Amerika hapa na sikuandika vitu vipya, lakini hakukuwa na nakala kama hizo juu ya HabrΓ©, na kama sehemu ya kuandika safu kadhaa za huduma za cmd haikuwezekana kuigusa.

Marejeo:

Hazina ya msimbo wa chanzo
Kwa kifupi kuhusu TFTP
Kitu kimoja, lakini kwa Kirusi

Chanzo: mapenzi.com

Kuongeza maoni