Meddalwedd ysgrifennu gyda swyddogaeth cyfleustodau gweinydd cleient Windows, rhan 02

Gan barhau Γ’'r gyfres barhaus o erthyglau sy'n ymwneud Γ’ gweithrediadau arferol o gyfleustodau consol Windows, ni allwn helpu ond cyffwrdd Γ’ TFTP (Protocol Trosglwyddo Ffeil Trivial) - protocol trosglwyddo ffeiliau syml.

Fel y tro diwethaf, gadewch i ni fynd dros y theori yn fyr, gweld y cod sy'n gweithredu ymarferoldeb tebyg i'r un gofynnol, a'i ddadansoddi. Mwy o fanylion - o dan y toriad

Ni fyddaf yn copΓ―o-gludo gwybodaeth gyfeiriol, y gellir dod o hyd i ddolenni iddi yn draddodiadol ar ddiwedd yr erthygl, byddaf yn dweud mai amrywiad symlach o'r protocol FTP yw TFTP yn ei hanfod, lle mae'r gosodiad rheoli mynediad wedi'i ddileu, ac mewn gwirionedd nid oes dim byd yma heblaw gorchmynion ar gyfer derbyn a throsglwyddo ffeil . Fodd bynnag, er mwyn gwneud ein gweithrediad ychydig yn fwy cain ac wedi'i addasu i'r egwyddorion presennol o ysgrifennu cod, mae'r gystrawen wedi'i newid ychydig - nid yw hyn yn newid egwyddorion gweithredu, ond mae'r rhyngwyneb, IMHO, yn dod ychydig yn fwy rhesymegol a yn cyfuno agweddau cadarnhaol FTP a TFTP.

Yn benodol, pan gaiff ei lansio, mae'r cleient yn gofyn am gyfeiriad IP y gweinydd a'r porthladd y mae TFTP arferol ar agor arno (oherwydd anghydnawsedd Γ’'r protocol safonol, roeddwn i'n ystyried ei bod yn briodol gadael y defnyddiwr y gallu i ddewis porthladd), ac ar Γ΄l hynny a cysylltiad yn digwydd, ac o ganlyniad gall y cleient anfon un o'r gorchmynion - cael neu roi, i dderbyn neu anfon ffeil at y gweinydd. Anfonir pob ffeil yn y modd deuaidd i symleiddio'r rhesymeg.

I weithredu'r protocol, defnyddiais 4 dosbarth yn draddodiadol:

  • TFTTPClient
  • TFTPSGweinydd
  • TFTTPClientTester
  • TFTPServerTester

Oherwydd bod dosbarthiadau profi yn bodoli ar gyfer dadfygio'r prif rai yn unig, ni fyddaf yn eu dadansoddi, ond bydd y cod yn y gadwrfa; gellir dod o hyd i ddolen iddo ar ddiwedd yr erthygl. Nawr byddaf yn edrych ar y prif ddosbarthiadau.

TFTTPClient

Tasg y dosbarth hwn yw cysylltu Γ’ gweinydd pell yn Γ΄l ei ip a'i rif porthladd, darllenwch orchymyn o'r ffrwd mewnbwn (yn yr achos hwn, y bysellfwrdd), dosrannu ef, ei drosglwyddo i'r gweinydd, ac, yn dibynnu a ydych chi angen anfon neu dderbyn ffeil, ei throsglwyddo neu ei chael.

Mae'r cod ar gyfer lansio'r cleient i gysylltu Γ’'r gweinydd ac aros am orchymyn o'r ffrwd mewnbwn yn edrych fel hyn. Disgrifir nifer o newidynnau byd-eang a ddefnyddir yma y tu allan i'r erthygl, yn nhestun llawn y rhaglen. Oherwydd eu dibwysedd, nid wyf yn eu dyfynnu er mwyn peidio Γ’ gorlwytho'r erthygl.

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

Gadewch i ni fynd dros y dulliau a elwir yn y bloc cod hwn:

Yma mae'r ffeil yn cael ei hanfon - gan ddefnyddio sganiwr, rydyn ni'n cyflwyno cynnwys y ffeil fel amrywiaeth o beit, rydyn ni'n ysgrifennu fesul un i'r soced, ac ar Γ΄l hynny rydyn ni'n ei chau a'i hagor eto (nid yr ateb mwyaf amlwg, ond mae'n gwarantu rhyddhau adnoddau), ac ar Γ΄l hynny rydym yn arddangos neges am drosglwyddo llwyddiannus.

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

Mae'r darn cod hwn yn disgrifio adfer data o'r gweinydd. Mae popeth eto'n ddibwys, dim ond y bloc cyntaf o god sydd o ddiddordeb. Er mwyn deall yn union faint o beit y mae angen eu darllen o'r soced, mae angen i chi wybod faint mae'r ffeil a drosglwyddwyd yn pwyso. Cynrychiolir maint y ffeil ar y gweinydd fel cyfanrif hir, felly derbynnir 4 beit yma, sy'n cael eu trosi wedyn yn un rhif. Nid yw hwn yn ddull Java iawn, mae braidd yn debyg ar gyfer OS, ond mae'n datrys ei broblem.

Yna mae popeth yn ddibwys - rydym yn derbyn nifer hysbys o beit o'r soced ac yn eu hysgrifennu i ffeil, ac ar Γ΄l hynny rydym yn arddangos neges llwyddiant.

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

Os rhoddwyd gorchymyn heblaw cael neu roi yn ffenestr y cleient, bydd y swyddogaeth showErrorMessage yn cael ei alw, gan nodi bod y mewnbwn yn anghywir. Oherwydd dibwys, ni fyddaf yn ei ddyfynnu. Ychydig yn fwy diddorol yw'r swyddogaeth o dderbyn a hollti'r llinyn mewnbwn. Rydyn ni'n pasio'r sganiwr i mewn iddo, ac rydyn ni'n disgwyl derbyn llinell wedi'i gwahanu gan ddau fwlch ac sy'n cynnwys y gorchymyn, cyfeiriad ffynhonnell a chyfeiriad cyrchfan.

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

Anfon gorchymyn - yn trosglwyddo'r gorchymyn a roddwyd o'r sganiwr i'r soced ac yn ei orfodi i gael ei anfon

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

Mae dewisydd yn swyddogaeth sy'n pennu gweithredoedd y rhaglen yn dibynnu ar y llinyn a gofnodwyd. Nid yw popeth yma'n brydferth iawn ac nid y tric a ddefnyddir yw'r un gorau gydag allanfa orfodol y tu allan i'r bloc cod, ond y prif reswm am hyn yw absenoldeb rhai pethau yn Java, fel cynrychiolwyr yn C#, awgrymiadau swyddogaeth C ++, neu ar leiaf y goto ofnadwy ac ofnadwy, sy'n eich galluogi i weithredu hyn yn hardd. Os ydych chi'n gwybod sut i wneud y cod ychydig yn fwy cain, rwy'n croesawu beirniadaeth yn y sylwadau. Mae'n ymddangos i mi fod angen geiriadur Llinynnol-cynadleddwyr yma, ond nid oes cynrychiolydd...

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

TFTPSGweinydd

Mae ymarferoldeb y gweinydd yn wahanol i ymarferoldeb y cleient, ar y cyfan, dim ond yn yr ystyr bod gorchmynion yn dod iddo nid o'r bysellfwrdd, ond o'r soced. Mae rhai o'r dulliau yn gyffredinol yr un fath, felly ni fyddaf yn eu dyfynnu, ni fyddaf ond yn cyffwrdd Γ’'r gwahaniaethau.

I ddechrau, defnyddir y dull rhedeg, sy'n derbyn porthladd fel mewnbwn ac yn prosesu'r data mewnbwn o'r soced mewn dolen dragwyddol.

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

Mae'r dull rhoi, sy'n lapio'r dull writeToFileFromSocket sy'n agor ffrwd ysgrifennu i ffeil ac yn ysgrifennu'r holl beit mewnbwn o'r soced, yn dangos neges sy'n nodi bod y trosglwyddiad wedi'i gwblhau'n llwyddiannus pan fydd yr ysgrifennu wedi'i gwblhau.

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

Mae'r dull cael yn adfer ffeil y gweinydd. Fel y soniwyd eisoes yn yr adran ar ochr cleient y rhaglen, i drosglwyddo ffeil yn llwyddiannus mae angen i chi wybod ei maint, wedi'i storio mewn cyfanrif hir, felly rwy'n ei rannu'n amrywiaeth o 4 beit, gan eu trosglwyddo beit-wrth-beit. i'r soced, ac yna, ar Γ΄l eu derbyn a'u cydosod ar y cleient i mewn i rif yn Γ΄l, rwy'n trosglwyddo'r holl beitau sy'n rhan o'r ffeil, wedi'u darllen o'r ffrwd mewnbwn o'r ffeil.


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

Mae'r dull getAndParseInput yr un fath ag yn y cleient, a'r unig wahaniaeth yw ei fod yn darllen data o'r soced yn hytrach nag o'r bysellfwrdd. Mae'r cod yn y gadwrfa, yn union fel y dewiswr.
Yn yr achos hwn, mae'r ymgychwyn yn cael ei roi mewn bloc ar wahΓ’n o god, oherwydd o fewn y gweithrediad hwn, ar Γ΄l i'r trosglwyddiad gael ei gwblhau, caiff adnoddau eu rhyddhau a'u hailfeddiannu eto - eto i ddarparu amddiffyniad rhag gollyngiadau cof.

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

I grynhoi:

Rydym newydd ysgrifennu ein hamrywiad ein hunain ar brotocol trosglwyddo data syml ac wedi cyfrifo sut y dylai weithio. Mewn egwyddor, wnes i ddim darganfod America yma ac ni wnes i ysgrifennu llawer o bethau newydd, ond nid oedd unrhyw erthyglau tebyg ar HabrΓ©, ac fel rhan o ysgrifennu cyfres o erthyglau am gyfleustodau cmd roedd yn amhosibl peidio Γ’ chyffwrdd ag ef.

Cyfeiriadau:

Ystorfa cod ffynhonnell
Yn fyr am TFTP
Yr un peth, ond yn Rwsieg

Ffynhonnell: hab.com

Ychwanegu sylw