Schreiwen Software mat der Funktionalitéit vu Windows Client-Server Utilities, Deel 02

Weiderhin déi lafend Serie vun Artikelen gewidmet fir personaliséiert Implementatioune vu Windows Konsol Utilities, mir kënnen net anescht wéi TFTP (Trivial File Transfer Protocol) beréieren - en einfachen Dateietransferprotokoll.

Wéi déi lescht Kéier, loosst eis kuerz iwwer d'Theorie goen, de Code gesinn deen d'Funktionalitéit ähnlech wéi déi erfuerderlech implementéiert, an analyséieren. Méi Detailer - ënner dem Schnëtt

Ech wäert keng Referenzinformatioun kopéieren-paste, Linken op déi traditionell um Enn vum Artikel fonnt kënne ginn, ech soen just datt am Kär TFTP eng vereinfacht Variatioun vum FTP Protokoll ass, an deem d'Zougangskontroll Astellung gouf geläscht, an tatsächlech gëtt et näischt hei ausser Kommandoen fir eng Datei z'empfänken an ze transferéieren . Wéi och ëmmer, fir eis Ëmsetzung e bësse méi elegant ze maachen an un déi aktuell Prinzipien vum Schreiwen vum Code ugepasst ze maachen, ass d'Syntax liicht geännert - dëst ännert d'Prinzipien vun der Operatioun net, awer d'Interface, IMHO, gëtt e bësse méi logesch an kombinéiert déi positiv Aspekter vun FTP an TFTP.

Besonnesch, wann lancéiert, freet de Client d'IP Adress vum Server an den Hafen op deem personaliséiert TFTP op ass (wéinst Inkompatibilitéit mam Standardprotokoll hunn ech et passend ugesinn fir de Benotzer d'Fäegkeet ze loossen en Hafen ze wielen), duerno Verbindung geschitt, als Resultat vun deem de Client ee vun de Kommandoen schécken kann - kréien oder setzen, fir eng Datei op de Server ze kréien oder ze schécken. All Dateie ginn am binäre Modus geschéckt fir d'Logik ze vereinfachen.

Fir de Protokoll ëmzesetzen, hunn ech traditionell 4 Klassen benotzt:

  • TFTClient
  • TFTPServer
  • TFTClientTester
  • TFTPServerTester

Wéinst der Tatsaach, datt Testklassen nëmme fir d'Debugging vun den Haapten existéieren, wäert ech se net analyséieren, awer de Code gëtt am Repository e Link op et kann um Enn vum Artikel fonnt ginn. Elo kucken ech op d'Haaptklassen.

TFTClient

D'Aufgab vun dëser Klass ass d'Verbindung mat engem Fernserver mat senger IP- an Portnummer ze verbannen, e Kommando aus dem Input Stream ze liesen (an dësem Fall d'Tastatur), et ze analyséieren, et op de Server ze transferéieren, an, ofhängeg ob Dir muss e Fichier schécken oder kréien, Transfert oder kréien.

De Code fir de Client ze starten fir mam Server ze verbannen an op e Kommando aus dem Input Stream ze waarden, gesäit esou aus. Eng Zuel vu globale Variabelen déi hei benotzt ginn, ginn ausserhalb vum Artikel beschriwwen, am ganzen Text vum Programm. Wéinst hirer Trivialitéit zitéieren ech se net fir den Artikel net ze iwwerlaascht.

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

Loosst eis iwwer d'Methoden goen, déi an dësem Codeblock genannt ginn:

Hei gëtt d'Datei geschéckt - mat engem Scanner präsentéiere mir den Inhalt vun der Datei als eng Array vu Bytes, déi mir een nom aneren an de Socket schreiwen, duerno zoumaachen an se erëm opmaachen (net déi offensichtlechst Léisung, awer et garantéiert d'Verëffentlechung vun de Ressourcen), duerno weisen mir e Message iwwer erfollegräich Transfert.

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

Dëse Code Fragment beschreift d'Erhuelung vun Daten vum Server. Alles ass erëm trivial, nëmmen den éischte Block vum Code ass interessant. Fir genee ze verstoen wéivill Bytes aus der Socket gelies musse ginn, musst Dir wëssen wéi vill déi transferéiert Datei weit. D'Dateigréisst um Server gëtt als laang ganz Zuel duergestallt, also 4 Bytes ginn hei ugeholl, déi duerno an eng Zuel ëmgewandelt ginn. Dëst ass net eng ganz Java Approche, et ass éischter ähnlech fir SI, awer et léist säi Problem.

Dann ass alles trivial - mir kréien eng bekannt Zuel vu Bytes aus der Socket a schreiwen se op eng Datei, duerno weisen mir e Succès Message.

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

Wann e Kommando anescht wéi kréien oder setzen an d'Clientfenster agefouert gouf, gëtt d'showErrorMessage Funktioun genannt, wat beweist datt den Input falsch war. Wéinst Trivialitéit wäert ech et net zitéieren. E bësse méi interessant ass d'Funktioun fir den Input String z'empfänken an opzedeelen. Mir passéieren de Scanner an et, vun deem mir erwaarden eng Linn ze kréien, getrennt vun zwee Plazen an enthält de Kommando, Quelladress an Destinatiounsadress.

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

E Kommando schécken - schéckt de Kommando aus dem Scanner an de Socket a forcéiert et ze schécken

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

E Selektor ass eng Funktioun déi d'Aktiounen vum Programm bestëmmt ofhängeg vun der aginner String. Alles hei ass net ganz schéin an den Trick benotzt ass net dee beschten mat gezwongenen Austrëtt ausserhalb vum Codeblock, awer den Haaptgrond dofir ass d'Feele vun e puer Saachen am Java, wéi Delegéierten am C #, Funktiounsweiser vu C++, oder um mannst déi schrecklech a schrecklech Goto, déi Iech erlaben dëst schéin ëmzesetzen. Wann Dir wësst wéi Dir de Code e bësse méi elegant mécht, begréissen ech Kritik an de Kommentaren. Et schéngt mir datt e String-Delegéierte Wierderbuch hei gebraucht gëtt, awer et gëtt keen Delegéierte ...

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

TFTPServer

D'Funktionalitéit vum Server ënnerscheet sech vun der Funktionalitéit vum Client, duerch a grouss, nëmmen datt d'Befehle net vun der Tastatur kommen, mee aus der Socket. E puer vun de Methoden sinn allgemeng d'selwecht, also wäert ech se net zitéieren, ech beréieren nëmmen d'Ënnerscheeder.

Fir unzefänken, gëtt d'Runmethod benotzt, déi e Port als Input kritt an d'Inputdaten aus der Socket an enger éiweger Loop veraarbecht.

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

D'Put-Methode, déi d'WriteToFileFromSocket-Methode wéckelt, déi e Schreifstroum op eng Datei opmaacht an all Input-Bytes aus der Socket schreift, weist e Message un, deen den Erfolleg vum Transfert ugeet wann d'Schreiwe fäerdeg ass.

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

D'Get-Methode recuperéiert d'Serverdatei. Wéi schonn an der Rubrik op der Client Säit vum Programm erwähnt, fir eng Datei erfollegräich ze transferéieren, musst Dir seng Gréisst kennen, an enger laanger ganzer Zuel gespäichert, also hunn ech se an eng Array vu 4 Bytes opgedeelt, se byte-by-byte transferéieren op d'Socket, an dann, nodeems se op de Client an eng Zuel zréck empfaangen a montéiert sinn, iwwerdroen ech all Bytes, déi d'Datei ausmaachen, aus dem Input Stream aus der Datei liesen.


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

D'getAndParseInput Method ass d'selwecht wéi am Client, deen eenzegen Ënnerscheed ass datt et Daten aus der Socket liest anstatt vun der Tastatur. De Code ass am Repository, grad wéi de Selektor.
An dësem Fall gëtt d'Initialiséierung an engem separaten Codeblock gesat, well innerhalb vun dëser Ëmsetzung, nodeems den Transfert fäerdeg ass, ginn d'Ressourcen fräigelooss an erëm besat - erëm fir Schutz géint Erënnerungsleck ze bidden.

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

Ze resuméieren:

Mir hu just eis eege Variatioun op engem einfachen Datetransferprotokoll geschriwwen an erausfonnt wéi et soll funktionnéieren. Am Prinzip hunn ech Amerika net hei entdeckt an hunn net vill nei Saachen geschriwwen, awer et goufe keng ähnlech Artikelen iwwer Habré, an als Deel vun der Schreiwen vun enger Serie vun Artikelen iwwer cmd Utilities war et onméiglech net ze beréieren.

Referenzen:

Quellcode Repository
Kuerz iwwer TFTP
Déi selwecht Saach, mä op Russesch

Source: will.com

Setzt e Commentaire