Nermalava nivîsandinê bi fonksiyona karûbarên xerîdar-servera Windows-ê, beş 02

Berdewamkirina rêze gotarên domdar ên ku ji bo pêkanînên xwerû yên karûbarên konsolê yên Windows-ê hatine veqetandin, em nekarin dest bavêjin TFTP (Protokola Veguheztina Pelê Trivial) - protokolek veguheztina pelê ya hêsan.

Weke cara paşîn, em bi kurtasî li ser teoriyê bigerin, koda ku fonksiyonên mîna ya pêwîst bicîh tîne bibînin û wê analîz bikin. Zêdetir hûrgulî - di bin qutbûnê de

Ez ê agahdariya referansê kopî-paste nekim, lînkên ku bi kevneşopî di dawiya gotarê de têne dîtin, ez ê tenê bibêjim ku di bingeha xwe de, TFTP guhertoyek sadekirî ya protokola FTP ye, ku tê de mîhenga kontrolkirina gihîştinê ye. hatiye rakirin, û bi rastî li vir ji bilî fermanên ji bo wergirtin û veguheztina pelê tiştek tune. Lêbelê, ji bo ku em pêkanîna me hinekî xweşiktir û bi prensîbên heyî yên koda nivîsandinê re were adaptekirin, hevoksazî hinekî hate guheztin - ev prensîbên xebitandinê naguhezîne, lê navber, IMHO, hinekî mentiqîtir dibe û aliyên erênî yên FTP û TFTP tevlihev dike.

Bi taybetî, dema ku dest pê kir, xerîdar navnîşana IP-ya serverê û porta ku li ser TFTP-ya xwerû vekirî ye daxwaz dike (ji ber nehevahengiya bi protokola standard re, min guncav dît ku ez ji bikarhêner re kapasîteya hilbijartina portê bihêlim), piştî ku pêwendiyek çêdibe, wekî encamek ku xerîdar dikare yek ji fermanan bişîne - bigire an bihêle, ku pelek ji serverê re bistîne an bişîne. Hemî pel di moda binary de têne şandin da ku mantiqê hêsan bikin.

Ji bo pêkanîna protokolê, min bi kevneşopî 4 çîn bikar anîn:

  • TFTPClient
  • TFTPSserver
  • TFTPClientTester
  • TFTPServerTester

Ji ber vê yekê ku dersên ceribandinê tenê ji bo derbkirina yên sereke hene, ez ê wan analîz nekim, lê kod dê di depoyê de be ku di dawiya gotarê de tê dîtin. Niha ez ê li dersên sereke binerim.

TFTPClient

Erka vê polê ev e ku bi karanîna ip û jimareya porta xwe ve bi serverek dûr ve were girêdan, fermanek ji têketina têketinê (di vê rewşê de, klavyeyê) bixwînin, wê parsek bikin, wê veguhezînin serverê, û, li gorî ka hûn pêdivî ye ku pelek bişînin an bistînin, veguhezînin an bistînin.

Koda ji bo destpêkirina xerîdar ji bo girêdana serverê û li benda fermanek ji çema têketinê bi vî rengî xuya dike. Hejmarek guhêrbarên gerdûnî yên ku li vir têne bikar anîn li derveyî gotarê, di nivîsa tevahî ya bernameyê de têne vegotin. Ji ber biçûkbûna wan, ez wan binav nakim da ku gotarê zêde negirim.

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

Ka em li ser awayên ku di vê bloka kodê de têne gotin bigerin:

Li vir pel tê şandin - bi karanîna skanerek, em naveroka pelê wekî rêzek byte pêşkêş dikin, ku em yek bi yek li soketê dinivîsin, piştî ku em wê digirin û dîsa vedikin (ne çareseriya herî eşkere, lê ew serbestberdana çavkaniyan garantî dike), piştî ku em peyamek li ser veguheztina serketî nîşan didin.

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

Ev perçeya kodê hilanîna daneyan ji serverê vedibêje. Her tişt dîsa piçûk e, tenê bloka yekem a kodê balkêş e. Ji bo ku hûn bi rastî fêm bikin ka çend byte hewce dike ku ji soketê were xwendin, hûn hewce ne ku hûn zanibin ka pelê veguhestî çiqas giran e. Mezinahiya pelê li ser serverê wekî jimareyek dirêj tê destnîşan kirin, ji ber vê yekê 4 byte li vir têne pejirandin, ku paşê di yek hejmarê de têne veguheztin. Ev ne nêzîkatiyek pir Java-yê ye, ew ji bo SI-yê pir dişibihe, lê ew pirsgirêka xwe çareser dike.

Dûv re her tişt piçûk e - em ji soketê hejmarek baytên naskirî distînin û wan li pelek dinivîsin, piştî ku em peyamek serfiraziyê nîşan didin.

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

Ger fermanek ji xeynî get an put bikeve pencereya xerîdar, dê fonksiyona showErrorMessage were gazî kirin, ku destnîşan dike ku têketin xelet bû. Ji ber biçûkbûnê, ez ê nebêjim. Hin balkêştir fonksiyona wergirtin û dabeşkirina rêzika têketinê ye. Em skanerê di nav wê de derbas dikin, ku em jê hêvî dikin ku rêzek ku bi du cîhan veqetandî ye û tê de ferman, navnîşana çavkanî û navnîşana meqsedê tê de ye, bistînin.

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

Fermanek dişîne-fermana ku ji skanerê tê veguhezîne soketê û zorê dide ku were şandin

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

Hilbijêr fonksiyonek e ku li gorî rêzika têketinê kiryarên bernameyê diyar dike. Li vir her tişt ne pir xweşik e û fêlbaziya ku tê bikar anîn ne ya çêtirîn e ku bi derketina bi zorê li derveyî bloka kodê ye, lê sedema bingehîn a vê yekê nebûna hin tiştan di Java-yê de ye, mîna delegeyên di C#, nîşangirên fonksiyonê yên ji C++, an li herî kêm gotoya tirsnak û xedar, ku dihêle hûn vê bi rengek xweşik bicîh bînin. Ger hûn dizanin ka meriv çawa kodê hinekî xweşiktir dike, ez di şîroveyan de pêşwaziya rexne dikim. Bi dîtina min li vir ferhengek String-delegatî lazim e, lê nûner tune...

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

TFTPSserver

Fonksiyona serverê ji fonksiyona xerîdar, bi gelemperî, cûda dibe, tenê di wê yekê de ku ferman ne ji klavyeyê, lê ji soketê têne jê re. Hin rêbaz bi gelemperî yek in, ji ber vê yekê ez ê wan nebêjim, ez ê tenê li ser cûdahiyan bisekinim.

Ji bo destpêkirinê, rêbaza xebitandinê tê bikar anîn, ku portek wekî têketinê distîne û daneya têketinê ji soketê di xelekek bêdawî de pêvajoyê dike.

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

Rêbaza danînê, ku rêbaza writeToFileFromSocket ku di pelê de çemek nivîsandinê vedike û hemî baytên têketinê ji soketê dinivîse, dipêçe, peyamek nîşan dide ku dema nivîsandin biqede bi serketî qedandina veguheztinê nîşan dide.

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

Rêbaza wergirtinê pelê serverê vedigire. Wekî ku berê jî di beşa li ser milê xerîdar a bernameyê de hate behs kirin, ji bo veguheztina pelek bi serfirazî hûn hewce ne ku mezinahiya wê zanibin, ku di jimareyek dirêj de hatî hilanîn, ji ber vê yekê ez wê di nav rêzek 4 byte de veqetînim, wan bit-bi-byte veguhezînim. li ser soketê, û dûv re, piştî ku wan li ser xerîdar werdigirim û li hejmarek paşde kom dikim, ez hemî baytên ku pelê pêk tînin vediguhezînim, ji herika têketina pelê dixwînim.


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

Rêbaza getAndParseInput wekî di xerîdar de ye, cûdahiya tenê ew e ku ew daneyan ji soketê ne ji klavyeyê dixwîne. Kod di depoyê de ye, mîna hilbijêr.
Di vê rewşê de, destpêkê di bloka kodê ya cihêreng de tê danîn, ji ber ku di nav vê pêkanînê de, piştî ku veguheztin qediya, çavkanî têne berdan û ji nû ve têne dagir kirin - dîsa ji bo parastina li dijî lehiyên bîranînê.

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

Bi kurtasî:

Me tenê guhertoya xweya xwe li ser protokolek veguheztina daneya hêsan nivîsî û fêhm kir ku divê ew çawa bixebite. Di prensîbê de, min Amerîka li vir keşif nekir û pir tiştên nû nenivîsî, lê li ser Habré gotarên bi vî rengî tune bûn, û wekî beşek ji nivîsandina rêzek gotaran li ser karûbarên cmd-ê ne mimkun bû ku meriv dest nede.

References:

Depoya koda çavkaniyê
Bi kurtî li ser TFTP
Heman tişt, lê bi rûsî

Source: www.habr.com

Add a comment