MokaByte Numero 20 - Giugno 1998  
 
FTP in Java
II parte
di 

Luca Bertoncello

Prosegue l'analisi del File Transfert Protocol: questa volta l'implementazione 

 

 

Dopo aver paralto della teoria (nell'articolo precedente), e di come realizzare un server ftp,
vediamo come si implementa un client ftp.


Dopo tanto penare, eccoci infine a vedere come si fa una semplice applicazione che gestisce il protocollo FTP.
Questa applicazione consentirà all’utente di loggarsi, inserendo UserName e Password, disconnettersi, settare la modalità di trasferimento Files (Binaria o ASCII), leggere la directory corrente, cambiarla, scaricare e uploadare un file.

In pratica è un completo sistema di FTP, magari un po’ “spartano” da usare, ma completo di tutto quanto è fondamentale.
Bene, come avevo detto l’altra volta, per prima cosa è necessario connettersi al server alla porta 21.
Per fare questo si usa questa parte di codice (che, bene o male, dovrebbe essere un sistema abbastanza standard e conosciuto un po’ da tutti noi):

// Apertura Socket e inizializzazione degli Stream di Input e Output
        ftpSocket=new Socket(Host,21);
        os=new PrintStream(ftpSocket.getOutputStream());
        is=new DataInputStream(ftpSocket.getInputStream());
Qui, come si può notare, si apre il socket e si inizializzano gli Stream di Input e di Output.
A questo punto si lancia un procedura (la OpenComm), che ritorna true se tutto è andato bene, false altrimenti.
In fondo all’articolo darò il listato completo, per cui qui mi limiterò a dare spiegazioni sulle procedure fondamentali e sul funzionamento del sistema.
Vediamo ora un ciclo che troveremo molto ricorrente nel programma. Eccolo:
        ok=true;
        while(ok)
            {
                s=is.readLine();
                System.out.println(s);
                if(s.length()>5)
                    if(s.substring(0,4).compareTo("220 ")==0)
                        ok=false;
            }
Questo ciclo attende dati dal server fino a che non dà un certo messaggio (questo varierà di volta in volta). In questo caso attende il 220, ossia “Bene, dimmi di chi sei”.
Noi gli diciamo chi siamo con i comandi USER e PASS che danno, rispettivamente, lo username e la password.
In entrambi i casi dobbiamo attendere una risposta, sempre usando il ciclo di prima. Nel caso del comando USER, la risposta è 331, nel caso di PASS, 230 (se la pass è corretta) o 530 (Picche).
A questo punto ci si pone in attesa di comandi dall’utente.
I comandi validi in questo programma sono:

? ASCII               Imposta la modalità di trasferimento ASCII
? Binary              Imposta la modalità di trasferimento binaria
? Change   Directory   Cambia la directory corrente
? Dir                 Visualizza il contenuto della directory corrente
? Get File            Scarica un file
? Help                Visualizza l’Help
? Print  Directory    Visualizza la directory corrente
? Store File          Spedisce un file
? Quit                Esce dal programma

Vediamo un attimino due comandi (gli altri sono molto simili e lascio la semplice interpretazione alla lettura del sorgente).
I comandi che vedremo ora sono Change e Dir, che servono rispettivamente per cambiare la directory corrente e per visualizzare la directory corrente.
Questi due comandi hanno una grande differenza tra di loro. Il primo non ha un passaggio di dati, una volta lanciato il comando si attende la risposta ed è tutto finito li, il secondo invece ha anche un passaggio di dati che avviene, come ho detto nello scorso articolo, tramite un socket diverso, aperto sul momento e per questa funzione.
Gli altri comandi sono simili a questi due e, per amor di cronaca, dirò che i comandi ASCII, Binary, Print  Directory e Quit sono uguali come funzionamento al comando Change, mentre Get File e Store File sono uguali come funzionamento al comando Dir.
Bene, cominciamo dunque a vedere il più semplice, ossia il comando Change.
Il codice che esegue questo comando è il seguente:

  os.println("CWD "+Dir);
  ok=true;
  while(ok)
    {
      s=is.readLine();
      System.out.println(s);
      if(s.length()>5)
        if((s.substring(0,4).compareTo("250 ")==0) ||
           (s.substring(0,4).compareTo("550 ")==0))
          ok=false;
    }
Come si vede è decisamente banale. Si spedisce il comando (CWD + Directory) e si cicla attendendo la risposta (250=Tutto OK, 550=Directory inesistente).

Vediamo ora il comando più “complesso”, ossia il Dir.
Bene, come ho già detto, è necessario aprire un secondo socket su una porta diversa attraverso il quale passeranno i dati.
Bene, per prima cosa dobbiamo comunicare al server l’indirizzo del proprio computer e la porta sulla quale verrà aperto il socket.
Per fare questo è necessario usare il comando PORT. Port richiede, come parametro, l’indirizzo nel formato a,b,c,d e la porta, nel formato bh,bl. Il tutto spedito assieme in questa maniera:

 PORT a,b,c,d,bh,bl
Port ritorna come codice 200=OK! o 500=Porta non valida.
A questo punto si apre la porta e si manda il comando LIST. Si accetta il messaggio (150=OK! Dati in arrivo o 425=Errore!). Infine ci si pone in attesa (nel secondo socket) della directory. Alla fine si riceve nel socket primario il messaggio di “fine dati” (226).
Il codice che fa questo lavoro è il seguente:
  s1="";
  H=ftpSocket.getInetAddress().getLocalHost().getHostAddress();
  for(l=0;l<H.length();l++)
    if(H.charAt(l)=='.')
      s1+=',';
    else
      s1+=H.charAt(l);
  P=GetPort();
// Manda la porta da usarsi
  System.out.println("PORT "+s1+","+P);
  os.println("PORT "+s1+","+P);
  ok=true;
  while(ok)
    {
      s=is.readLine();
      System.out.println(s);
      if(s.length()>5)
        if((s.substring(0,4).compareTo("200 ")==0) ||
           (s.substring(0,4).compareTo("500 ")==0))
          ok=false;
    }
  if(s.substring(0,4).compareTo("200 ")==0)
    {
// Apre il socket
      Sck=new ServerSocket(NPort-1);
// Manda il comando
      System.out.println("LIST");
      os.println("LIST");
      ok=true;
      while(ok)
        {
          s=is.readLine();
          System.out.println(s);
          if(s.length()>5)
            if((s.substring(0,4).compareTo("150 ")==0) ||
               (s.substring(0,4).compareTo("425 ")==0))
              ok=false;
        }
      if(s.substring(0,4).compareTo("150 ")==0)
        {
// Se tutto è andato bene, visualizza i dati che arrivano dalla nuova porta
          sck=Sck.accept();
          isd=new DataInputStream(sck.getInputStream());
          ok1=false;
          s=isd.readLine();
          System.out.println(s);
          while(isd.available()!=0)
            {
              s=isd.readLine();
              System.out.println(s);
            }
          ok=true;
          while(ok)
            {
              s=is.readLine();
              System.out.println(s);
              if(s.length()>5)
                if(s.substring(0,4).compareTo("226 ")==0)
                  ok=false;
            }
          sck.close();
        }
      Sck.close();
    }
Come si vede la parte più onerosa in righe di codice è quella che contiene i cicli di attesa messaggi dal server. Questa parte è però fondamentale in quanto non si potrebbe altrimenti sapere se tutto è andato bene o meno.
Bene, a questo punto allego il sorgente perfettamente funzionante della classe, con tutti i commenti.
Come ho già detto prima le procedure sono simili o alla Change o alla GetDir, in quanto o non c’è comunicazione o, se c’è è prima necessario aprire un socket.
Anche le due funzioni di trasferimento file sono praticamente uguali alla GetDir, in fin dei conti, anche la directory corrente è un file: un file di testo che viene scaricato sullo schermo al posto di essere inserito in un file su disco.

Concludo dunque qui la mia breve dissertazione sul protocollo FTP. Come al solito, se qualcuno necessita di spiegazioni non ha che da mandarmi una E-Mail e risponderò volentieri.
L’uso del programma è banale. Occorre lanciarlo dando come parametro l’host a cui collegarsi. Il programma chiederà UserName e Password (naturalmente è anche possibile anche la connessione anonima) e quindi visualizza l’help e accetta i comandi.
 

Luca Bertoncello
(lucaber@biella.alpcom.it)

Sorgente del programma FTP.java

import java.awt.*;
import java.io.*;
import java.net.*;

public class ftp
{
    public static DataInputStream is;       // Stream di Input
    public static PrintStream os;           // Stream di Output
    public static Socket ftpSocket;         // Socket di comunicazione
    public static int NPort;                // Porta. Numero progressivo
    public static String TrMode;            // Transfer Mode. Ascii o Binary
    public static char getch()
    {
        char c;

// Questa procedura attende la pressione di un tasto.
        c=0;
        while(c==0)
            try
                {
                    c=(char) System.in.read();
                }
            catch(Exception e)
                {
                    c=0;
                }

        return(c);
    }
    public static int Comando(boolean vis)
    {
        char c=0;
        boolean ok=true;

        try
            {
                while(System.in.available()!=0)
                    getch();
            }
        catch(Exception e)
            {
            }
// Schermata con l'elenco dei comandi disponibili.
// Attende che si prema un tasto e si immetta un comando.
        if(vis)
            {
                System.out.println("Elenco comandi disponibili.");
                System.out.println("  (A)SCII              Imposta la modalita' di trasferimento ASCII");
                System.out.println("  (B)inary             Imposta la modalita' di trasferimento binaria");
                System.out.println("  (C)hange Directory   Cambia la directory corrente");
                System.out.println("  (D)ir                Visualizza il contenuto della directory corrente");
                System.out.println("  (G)et File           Scarica un file");
                System.out.println("  (H)elp               Visualizza questo Help");
                System.out.println("  (P)rint  Directory   Visualizza la directory corrente");
                System.out.println("  (S)tore File         Spedisce un file");
                System.out.println("  (Q)uit               Esce dal programma");
                System.out.println("Inserire un comando (C/D/H/P/S/Q): ");
            }

        c=getch();
        try
            {
                while(System.in.available()!=0)
                    getch();
            }
        catch(Exception e)
            {
            }

        c=Character.toUpperCase(c);

        if((c=='Q') || (c=='C') || (c=='H') || (c=='D') || (c=='A') || (c=='B') || (c=='G') || (c=='P') || (c=='S'))
            ok=false;

        return(c);
    }
    public static long atol(String s)
    {
// Converte la string passato in un long

        Long tempInt;
        String s1;
        int l,k;
        long q=0;

        for(l=-1,k=0;(k<s.length()) && (l==-1);k++)
            if((s.charAt(k)<'0') || (s.charAt(k)>'9'))
                l=k;

        if(l==-1)
            {
                s1=s;
                tempInt = Long.valueOf(s1);
                q=tempInt.longValue();
            }
        else
            if(l!=0)
                {
                    s1=s.substring(0,l);
                    tempInt = Long.valueOf(s1);
                    q=tempInt.longValue();
                }

        return(q);
    }
    public static int atoi(String s)
    {
// Converte la string passato in un integer

        Integer tempInt;
        String s1;
        int k,l;

        for(l=-1,k=0;(k<s.length()) && (l==-1);k++)
            if((s.charAt(k)<'0') || (s.charAt(k)>'9'))
                l=k;

        if(l==-1)
            {
                s1=s;
                tempInt = Integer.valueOf(s1);
                k=tempInt.intValue();
            }
        else
            if(l!=0)
                {
                    s1=s.substring(0,l);
                    tempInt = Integer.valueOf(s1);
                    k=tempInt.intValue();
                }
            else
                k=0;

        return(k);
    }
    public static String GetString(String Prompt)
    {
        String s="",Ret="";
        int n=0;
        char c;

// Prende una stringa da tastiera
        System.out.print(Prompt+": ");
        while(s.length()==0)
            try
                {
                    c=0;
                    while(c!='\n')
                        {
                            c=getch();
                            if(c!='\n')
                                s+=c;
                        }
                }
            catch(Exception e)
                {
                }

        Ret=s.substring(0,s.length()-1);
        return(Ret);
    }
    public static boolean OpenComm() throws IOException
    {
// Questa procedura si "logga" con il server, fornendo UserName e Password.
// Visualizza messaggi di errore nel caso UserName o Password siano errati
// Ritorna TRUE se si e' loggato, FALSE altrimenti

        String s="",User="",Passwd="";
        boolean Ret=true,ok;

        ok=true;
        while(ok)
            {
                s=is.readLine();
                System.out.println(s);
                if(s.length()>5)
                    if(s.substring(0,4).compareTo("220 ")==0)
                        ok=false;
            }
        User=GetString("UserName");
        os.println("USER "+User);
        System.out.println("USER "+User);
        ok=true;
        while(ok)
            {
                s=is.readLine();
                System.out.println(s);
                if(s.length()>5)
                    if(s.substring(0,4).compareTo("331 ")==0)
                        ok=false;
            }
        Passwd=GetString("Password");
        os.println("PASS "+Passwd);
        System.out.println("PASS ********");
        ok=true;
        while(ok)
            {
                s=is.readLine();
                System.out.println(s);
                if(s.length()>5)
                    if((s.substring(0,4).compareTo("230 ")==0) || (s.substring(0,4).compareTo("530 ")==0))
                        ok=false;
            }
        if(s.substring(0,4).compareTo("230 ")==0)
            System.out.println("Utente "+User+" Connesso");
        else
            Ret=false;

        return(Ret);
    }
    public static void ChDir() throws IOException
    {
        String Dir,s="";
        boolean ok;

// Cambia la directory corrente
        Dir=GetString("Nuova Directory");
        os.println("CWD "+Dir);
// Ciclo di attesa messaggio dal server
        ok=true;
        while(ok)
            {
                s=is.readLine();
                System.out.println(s);
                if(s.length()>5)
                    if((s.substring(0,4).compareTo("250 ")==0) || (s.substring(0,4).compareTo("550 ")==0))
                        ok=false;
            }
    }
    public static void PwDir() throws IOException
    {
        boolean ok;
        String s="";

// Visualizza la directory corrente
        os.println("PWD");
// Ciclo di attesa messaggio dal server
        ok=true;
        while(ok)
            {
                s=is.readLine();
                System.out.println(s);
                if(s.length()>5)
                    if(s.substring(0,4).compareTo("257 ")==0)
                        ok=false;
            }
    }
    public static String GetPort()
    {
        int l,h;
        String R;

// Scompone il numero di porta (progressivo) in Byte Alto e Byte Basso
        h=NPort/256;
        l=NPort-(h*256);
        R=""+h+","+l;
        NPort++;

        return(R);
    }
    public static void GetFile() throws IOException
    {
        boolean ok,ok1=true;
        String s="",s1,P="",H="",NFile="",NFileS="";
        int l;
        ServerSocket Sck;
        Socket sck;
        DataInputStream isd;
        RandomAccessFile f;

// Scarica un file
        NFile=GetString("File da scaricare");
        NFileS=GetString("File scaricato");
// Cicla finchè il file è stato trasferito senza errori
        while(ok1)
            {
                s1="";
                H=ftpSocket.getInetAddress().getLocalHost().getHostAddress();
                for(l=0;l<H.length();l++)
                    if(H.charAt(l)=='.')
                        s1+=',';
                    else
                        s1+=H.charAt(l);
                P=GetPort();
// Imposta la porta
                System.out.println("PORT "+s1+","+P);
                os.println("PORT "+s1+","+P);
                ok=true;
                while(ok)
                    {
                        s=is.readLine();
                        System.out.println(s);
                        if(s.length()>5)
                            if((s.substring(0,4).compareTo("200 ")==0) || (s.substring(0,4).compareTo("500 ")==0))
                                ok=false;
                    }
                if(s.substring(0,4).compareTo("200 ")==0)
                    {
// Imposta il tipo di file
                        System.out.println("TYPE "+TrMode);
                        os.println("TYPE "+TrMode);
                        ok=true;
                        while(ok)
                            {
                                s=is.readLine();
                                System.out.println(s);
                                if(s.length()>5)
                                    if((s.substring(0,4).compareTo("200 ")==0) || (s.substring(0,4).compareTo("500 ")==0))
                                        ok=false;
                            }
// Apre il nuovo socket
                        Sck=new ServerSocket(NPort-1);
// Manda il comando di download
                        System.out.println("RETR "+NFile);
                        os.println("RETR "+NFile);
                        ok=true;
                        while(ok)
                            {
                                s=is.readLine();
                                System.out.println(s);
                                if(s.length()>5)
                                    if((s.substring(0,4).compareTo("150 ")==0) || (s.substring(0,4).compareTo("425 ")==0))
                                        ok=false;
                            }
                        if(s.substring(0,4).compareTo("150 ")==0)
                            {
// Se tutto è andato bene, si prepara ad accettare i dati
                                sck=Sck.accept();
                                isd=new DataInputStream(sck.getInputStream());
                                f=new RandomAccessFile(NFileS,"rw");
                                ok1=false;
// Scrive i dati su disco
                                f.write(isd.read());
                                while(isd.available()!=0)
                                    f.write(isd.read());
                                f.close();
                                ok=true;
                                while(ok)
                                    {
                                        s=is.readLine();
                                        System.out.println(s);
                                        if(s.length()>5)
                                            if(s.substring(0,4).compareTo("226 ")==0)
                                                ok=false;
                                    }
                                sck.close();
                            }
                        Sck.close();
                    }
            }
    }
    public static void PutFile() throws IOException
    {
        boolean ok,ok1=true;
        String s="",s1,P="",H="",NFile="",NFileS="";
        int l;
        ServerSocket Sck;
        Socket sck;
        PrintStream osd;
        RandomAccessFile f;

// Procedure di upload
        NFile=GetString("File da mandare");
        NFileS=GetString("File remoto");
        while(ok1)
            {
                s1="";
                H=ftpSocket.getInetAddress().getLocalHost().getHostAddress();
                for(l=0;l<H.length();l++)
                    if(H.charAt(l)=='.')
                        s1+=',';
                    else
                        s1+=H.charAt(l);
                P=GetPort();
                System.out.println("PORT "+s1+","+P);
                os.println("PORT "+s1+","+P);
                ok=true;
                while(ok)
                    {
                        s=is.readLine();
                        System.out.println(s);
                        if(s.length()>5)
                            if((s.substring(0,4).compareTo("200 ")==0) || (s.substring(0,4).compareTo("500 ")==0))
                               ok=false;
                    }
                if(s.substring(0,4).compareTo("200 ")==0)
                    {
                        System.out.println("TYPE "+TrMode);
                        os.println("TYPE "+TrMode);
                        ok=true;
                        while(ok)
                            {
                                s=is.readLine();
                                System.out.println(s);
                                if(s.length()>5)
                                    if((s.substring(0,4).compareTo("200 ")==0) || (s.substring(0,4).compareTo("500 ")==0))
                                        ok=false;
                            }
                        System.out.println("STOR "+NFile);
                        os.println("STOR "+NFile);
                        Sck=new ServerSocket(NPort-1);
                        ok=true;
                        while(ok)
                            {
                                s=is.readLine();
                                System.out.println(s);
                                if(s.length()>5)
                                    if((s.substring(0,4).compareTo("150 ")==0) || (s.substring(0,4).compareTo("425 ")==0))
                                        ok=false;
                            }
                        if(s.substring(0,4).compareTo("150 ")==0)
                            {
                                sck=Sck.accept();
                                osd=new PrintStream(sck.getOutputStream());
                                f=new RandomAccessFile(NFileS,"r");
                                ok1=false;
                                while(f.getFilePointer()<f.length())
                                    osd.write(f.read());
                                f.close();
                                sck.close();
                                ok=true;
                                while(ok)
                                    {
                                        s=is.readLine();
                                        System.out.println(s);
                                        if(s.length()>5)
                                            if(s.substring(0,4).compareTo("226 ")==0)
                                                ok=false;
                                    }
                            }
                        Sck.close();
                    }
            }
    }
    public static void GetDir() throws IOException
    {
        boolean ok,ok1;
        String s="",s1,P="",H="";
        int l;
        ServerSocket Sck;
        Socket sck;
        DataInputStream isd;

// Visualizza il contenuto della directory corrente
        ok1=true;
        while(ok1)
            {
                s1="";
                H=ftpSocket.getInetAddress().getLocalHost().getHostAddress();
                for(l=0;l<H.length();l++)
                    if(H.charAt(l)=='.')
                        s1+=',';
                    else
                        s1+=H.charAt(l);
                P=GetPort();
// Manda la porta da usarsi
                System.out.println("PORT "+s1+","+P);
                os.println("PORT "+s1+","+P);
                ok=true;
                while(ok)
                    {
                        s=is.readLine();
                        System.out.println(s);
                        if(s.length()>5)
                            if((s.substring(0,4).compareTo("200 ")==0) || (s.substring(0,4).compareTo("500 ")==0))
                                ok=false;
                    }
                if(s.substring(0,4).compareTo("200 ")==0)
                    {
// Apre il socket
                        Sck=new ServerSocket(NPort-1);
// Manda il comando
                        System.out.println("LIST");
                        os.println("LIST");
                        ok=true;
                        while(ok)
                            {
                                s=is.readLine();
                                System.out.println(s);
                                if(s.length()>5)
                                    if((s.substring(0,4).compareTo("150 ")==0) || (s.substring(0,4).compareTo("425 ")==0))
                                        ok=false;
                            }
                        if(s.substring(0,4).compareTo("150 ")==0)
                            {
// Se tutto è andato bene, visualizza i dati che arrivano dalla nuova porta
                                sck=Sck.accept();
                                isd=new DataInputStream(sck.getInputStream());
                                ok1=false;
                                s=isd.readLine();
                                System.out.println(s);
                                while(isd.available()!=0)
                                    {
                                        s=isd.readLine();
                                        System.out.println(s);
                                    }
                                ok=true;
                                while(ok)
                                    {
                                        s=is.readLine();
                                        System.out.println(s);
                                        if(s.length()>5)
                                            if(s.substring(0,4).compareTo("226 ")==0)
                                                ok=false;
                                    }
                                sck.close();
                            }
                        Sck.close();
                    }
            }
    }
    public static void SetTrMode(String Mod) throws IOException
    {
        String s="";
        boolean ok;

// Imposta la modalità di trasferimento file.
// A=ASCII
// I=Binary
        TrMode=Mod;
        os.println("TYPE "+Mod);
        System.out.println("TYPE "+Mod);
        ok=true;
        while(ok)
            {
                s=is.readLine();
                System.out.println(s);
                if(s.length()>5)
                    if(s.substring(0,4).compareTo("200 ")==0)
                        ok=false;
            }
    }
    public static void Connect(String Host) throws IOException
    {
        boolean ok=true,vis=true;
        String s="";
        int c,n;

// Apertura Socket e inizializzazione degli Stream di Input e Output
        ftpSocket=new Socket(Host,21);
        os=new PrintStream(ftpSocket.getOutputStream());
        is=new DataInputStream(ftpSocket.getInputStream());
        NPort=50000;

// Se l'utente e' loggato correttamente, accetta i comandi
        if(OpenComm()==true)
            {
// Ciclo finchè non si decide di uscire.
// Prende l'istruzione dall'utente e la esegue.
                while(ok)
                    {
                        c=Comando(vis);
                        vis=false;
                        switch(c)
                            {
                                case 'A':
                                    SetTrMode("A");
                                break;
                                case 'B':
                                    SetTrMode("I");
                                break;
                                case 'G':
                                    GetFile();
                                break;
                                case 'H':
                                    vis=true;
                                break;
                                case 'C':
                                    ChDir();
                                break;
                                case 'D':
                                    GetDir();
                                break;
                                case 'P':
                                    PwDir();
                                break;
                                case 'S':
                                    PutFile();
                                break;
                                case 'Q':
                                    ok=false;
                                break;
                            }
                    }
            }

// Chiude la comunicazione con FTP e si disconnette
        os.println("QUIT");
        System.out.println("QUIT");
        ok=true;
        while(ok)
            {
                s=is.readLine();
                System.out.println(s);
                if(s.length()>5)
                    if(s.substring(0,4).compareTo("221 ")==0)
                        ok=false;
            }

        is.close();
        os.close();
        ftpSocket.close();
    }

    public static void main(String args[])
    {
        try
            {
                Connect(args[0]);
            }
        catch(Exception e)
            {
                System.out.println("Eccezione "+e);
            }
    }
}
 
 


 
 
 

MokaByte Web  1998 - www.mokabyte.it 
MokaByte ricerca nuovi collaboratori. Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it