Aplicatii de retea. Socluri. URL.

  1. Protocoale si porturi. Modelul client server.
  2. Modelul de comunicatie orientat pe conexiune. Clasele  Socket si ServerSocket
  3. Modelul de comunicatie neorientat pe conexiune. Clasele DatagaramSocket si  DatagramPacket
  4. URL
Clasele din pachetul java.net se impart in doua categorii, clase pentru socluri si clase care lucreaza cu URL (Uniform Resource Locators). Soclurile Java faciliteaza accesul la protocoalele standard utilizate in comunicarea intre calculatoarele gazda de pe Interent.
 

1. Protocoale si porturi. Modelul client server.

Protocoale

Inainte sa intram in detaliile posibilitatilor oferite de limbajul Java pentru scrierea aplicatiilor de retea trebuie sa cunoastem niste lucruri despre reteaua Internet. Internetul functioneaza pe un sistem de protocoale numit TCP/IP (Transport Control Protocol/Internet protocol)
Aceste protocoale stabilesc regulile cu ajutorul carora doua calculatoare comunica intre ele. Protocoalele sunt standarde de care se ocupa IETF ( Internet Engineering Task Force). Java implementeaza protocoalele de nivel superior al stivei de protocoale TCP/IP. Astfel faciliteaza utilizarea protocoalelor HTTP (HyperText Transfer Protocol) si FTP( File Transfer Protocol). Astfel programatorul va utiliza niste clase si interfete predefinite, fara a cunoaste detaliile de implementare a acestora. Nu trebuie sa cunoastem structurile de date utilizate de acest sistem de protocoale, nici metodele utilizate pentru transmiterea si receptionarea secventelor de octeti.

Modelul client server

Serverul este o aplicatie care ofera servicii clientilor sositi prin retea. Serverele ofera o gama variata de servicii.  Serverul cel mai cunoscut este serverul Web, care furnizeaza documentele cerute de catre clienti. Un alt serviciu cunoscut este posta electronica, care utilizeaza protocoalele SMPT (Simple Mail Transfer Protocol) si IMAP4 (Internet Mail Access Protocol). Pe principiul client-server funnctioneaza si protocoalele NFS (Network File Service) si  FTP sau serviciul de nume de domenii  DNS (Domain Name Service) si serviciul de informatii despre retea NIS (Network Information Services). Trebuie sa amintim si serviciul care permite logarea la un calculator aflat la distanta: TELNET si RLOGIN. Putem trage concluzia ca arhitectura client-server este instrumentul de baza in dezvoltarea aplicatiilor de retea.
Clientul  este o aplicatie care utilizeaza serviciile oferite de catre un server. Pentru a putea realiza acest lucru, clientul trebuie sa cunoasca unde se afla serverul in retea si cum trebuie comunicat cu acesta si ce servicii ofera. Deci daca un client doreste o comunicare cu serverul, trebuie sa cunosca trei lucruri: Daca aceste date ne stau la dispozitie, putem realiza comunicatia cu serverul.

Porturi si socluri

Porturile si soclurile reprezinta mecanismul prin care se realizeaza legatura cu un server. Porturile reprezinta o poarta la care sunt primiti clientii pentru un anumit serviciu. De exemplu la aceeasi adresa se pot oferi diferite servicii, acestea oferindu-se la porturi diferite. Acelasi calculator (cu o singura adresa IP) poate sa ne ofere oricate servicii doreste. Clientii care apeleaza la serviciile acestui calculator vor utiliza aceeasi adresa, indiferent la care serviciu apeleaza, si toti clientii care doresc utilizarea unui anumit serviciu vor utiliza acelasi port. Un numar de port este un numar inreg din intervalul 1-9999. IETF defineste  serviciile oferite pe porturile 1-1024. De obicei serviciile HTTP sunt oferite la portul 80, cele de FTP la portul 21, Telnet la 23 etc.

Un  soclu este de fapt un nod abstract de comunicatie. Soclurile reprezinta o interfata de nivel scazut pentru comunicarea in retea. Soclurile permit comunicarea intre procese aflate pe acelasi calculator sau pe calculatoare diferite din retea.  Mecanismul de socluri a fost definit prima data in BSD UNIX. Java suporta trei tipuri de socluri. Clasa Socket  utilizeaza un protocol orientat pe conexiune (TCP), clasa DatagramSocket utilizeaza protocolul UDP la nivelul transport, care este un protocol neorientat pe conexiune. O alta varianta a DatagramSocket este MulticastSocket utilizat pentru a trimite date deodata la mai multi receptori. Soclurile utilizeaza fluxuri de date (streamuri) pentru a trimite si a receptiona mesaje.

2. Modelul de comunicatie orientat pe conexiune.
Clasele  Socket si ServerSocket

Acest model se bazeaza pe protocolul TCP. Intr-o aplicatie retea intotdeauna avem doua parti: o parte client care initializeaza conversatia si trimite cereri, si o parte server care primeste cererile si raspunde la acestea. Clientul intotdeauna creaza un soclu pentru a initia conversatia si trebuie sa cunoasca serverul caruia adreseaza cererea,  iar serverul trebuie sa fie pregatit pentru a receptiona aceste cereri. In momentul receptionarii mesajului creaza un soclu pe partea serverului, soclu care va facilita deservirea clientului. Atat pe partea de client cat si pe  partea de server se utilizeaza cate un obiect de tip Socket pentru comunicare. Pe partea de server mai trebuie sa cream un obiect de tip ServerSocket, care are sarcina primirii conexiunilor si acceptarea acestora.


Clientul trebuie sa cunoasca doua lucruri despre server: numele serverului (utilizat pentru determinarea adresei IP al serverului) si numarul portului la care acesta asculta cererile clientilor. Acelasi calculator gazda poate oferi mai multe servicii, deci poate gazdui mai multe procese de tip server. De exemplu poate fi server Mail, server FTP, server HTTP, dar aceste aplicatii lucreaza cu diferite porturi, deci cererile adresate acestor serveri vor fi receptionate pe diferite porturi.
Privind modalitatea de deservire a clientilor serverul prin constructie poate fi server paralel (concurent) si server secvential.

Serverul concurent permite deservirea in paralel a mai multor clienti. Aceasta paralelitate se poate realiza prin crearea a mai multor procese fiu, cate unul pentru fiecare client sau prin crearea de fire de executie pentru deservirea fiecarui clienti In Java noi vom utiliza cea de a doua modalitate. De obicei utilizam server cu arhitectura paralela pentru servicii la care deservirea unui client dureaza in timp.
In general o aplicatie server trebuie sa execute urmatorii pasi:

1. Serverul aloca un port de comunicare care va fi cunoscut de catre clienti
2. Cat timp ( mai sunt clienti la port )
    {
        stabilire conexiune cu client ( creare soclu )
        deservire client ( flux de intrare, flux de iesire )
        eliberare conexiune ( eliberare soclu )
    }
O aplicatie client executa urmatoarele:
1. Aloca un port de comunicare
2. Se conecteaza la server la portul cunoscut de dinainte
3. Se stabileste o conexiune prin care se trimit si se citesc date (soclu + fluxuri)

Clasa Socket

Constructori:
          Socket(InetAddress address, int port)
                    Creaza un soclu si care se conecteaza la adresa IP specificat prin obiectul InetAddress si portul specificat prin parametrul port.

         Socket(InetAddress address, int port, InetAddress localAddr, int localPort)

Creaza un soclu si care se conecteaza la adresa IP specificat prin obiectul InetAddress si portul specificat prin parametrul port. Ultimii doi parametri reprezinta adresa clientului si portul pe care acesta comunica .
          Socket(String host, int port)
                     Creaza un soclu si care se conecteaza la calculatorul host pe portul specificat prin parametrul port.

          Socket(String host, int port, InetAddress localAddr, int localPort)
                     Creaza un soclu si care se conecteaza la calculatorul host pe portul specificat prin parametrul port. Ultimii doi parametri reprezinta adresa clientului si portul pe care acesta comunica

Metode principale:
 

void close()
Inchide soclul.
InetAddress getInetAddress()
Returneaza adresa la care soclul este conectat
InputStream getInputStream()
Returneaza un stream de intrare pentru soclu.
InetAddress getLocalAddress()
Returneaza adresa pe care soclul este creat.
int getLocalPort()
Returneaza numarul portului local.
OutputStream getOutputStream()
Returneza un stream de iesire pentru soclu.
int getPort()
Returneaza portul la care soclul este conectat.

Clasa ServerSocket

Constructori:
ServerSocket(int port)
           Creaza un soclu server pe portul specificat.

ServerSocket(int port, int backlog)
     Creaza un soclu server pe portul specificat. Al doilea parametru indica lungimea cozii de asteptare.

Metode principale:
 Socket accept()
                       Asculta conexiunile si le accepta.
 void close()
                    Inchide soclul.
 InetAddress getInetAddress()
                       Returneaza adresa locala al soclului server.
int getLocalPort()
                       Returneaza portul la care serverul asteapta conexiunile.
 

Exemplu 1.

Urmatoarea aplicatie este formata dintr-o parte client si una server. Clientul va trimite un sir de caractere serverului, iar serverul il va trimite inapoi convertit in majuscule.

Pentru rezolvarea acestei probleme construim doua pachete:

Incepen cu descrierea serverului. Serverul are o structura paralela, permite deservirea a mai multor clienti. Pentru fiecare client se va crea un obiect de tip ClientHanedle, un obiect care se ruleaza pe un fir de executie separat si realizeaza deservirea clientului.

//Source file: c:\Rose\ClientServer\ServerPackage\Server.java

package ServerPackage;
 

public class Server
{
   public static void main(String[] args)
   {
    int port;
    if( args.length <1 )
    {
      System.out.println( "Usage Server <portnumber>");
      System.exit( 1 );
    }
    try{
      port = Integer.parseInt( args[ 0 ] );
    }
    catch( NumberFormatException e )
    {
          port = 1024;
    }
    try{
      java.net.ServerSocket ss = new java.net.ServerSocket( port );
      while( true )
       ( new ClientHandle( ss.accept() )).start();
    }
    catch( java.io.IOException e )
    {
      System.out.println( "Server error ");
    }
   }
}

//Source file: c:\Rose\ClientServer\ServerPackage\ClientHandle.java

package ServerPackage;

import java.net.Socket;
import java.net.SocketException;

public class ClientHandle extends Thread
{
   Socket client;

   public ClientHandle(Socket client) throws SocketException
   {
     this.client = client;
   }

   public void run()
   {
    try{
      java.io.InputStream  in  = client.getInputStream();
      java.io.OutputStream out = client.getOutputStream();
      java.io.BufferedReader myin  = new java.io.BufferedReader(
                                        new java.io.InputStreamReader( in));
      java.io.PrintWriter    myout = new java.io.PrintWriter( new
                                       java.io.OutputStreamWriter( out ));
      String request = myin.readLine();
      System.out.println( "Request: "+request);
      myout.println( request.toUpperCase());
      myout.flush();
      myin.close();
      myout.close();
      client.close();
   }
   catch( java.io.IOException e )
   {
      System.out.println("I/O error "+e );
   }
  }
}

Partea de client:


 
//Source file: c:\Rose\ClientServer\ClientPackage\Client.java

package ClientPackage;

public class Client
{
   private String hostname;
   private int hostport;
   private String message;

   public Client(String hostname, int hostport, String message)
   {
     this.hostname = hostname;
     this.hostport = hostport;
     this.message = message;
     doIt();
   }

   public void doIt()
   {
    try{
      java.net.Socket s = new java.net.Socket( hostname, hostport );
      java.io.BufferedReader  in = new java.io.BufferedReader( new
                        java.io.InputStreamReader(s.getInputStream()));
      java.io.PrintWriter    out = new java.io.PrintWriter( s.getOutputStream() );
      out.println( message ); out.flush();
      System.out.println("Answer from the server: "+in.readLine() );
      s.close();
    }
    catch( java.io.IOException  e )
    {
      System.out.println( "I/O error "+e );
    }
   }
 

   public static void main(String[] args)
   {
    String hostname, message;
    int hostport;
    if( args.length < 3 )
    {
      System.out.println("Usage Client <hostname> <hostport> <message>");
      System.exit( 1 );
    }

    hostname = args[ 0 ];
    message  = args[ 2 ];
    try{
       hostport =Integer.parseInt( args[ 1 ] );
    }
    catch( NumberFormatException e )
    {
      hostport = 1024;
    }
    Client c = new Client( hostname, hostport, message );
   }
}

Exemplu 2

Urmatoarea aplicatie va fi un server web. Aplicatia se va rula pe un fir de executie cu prioritatea mai mica decat cea normala si va rezolva cererile HTTP. Vom proiecta aplicatia a.i. sa aiba o arhitectura paralela si sa rezolve cererile de tip GET nume_fisier.Arhitectura paralela permite deservirea concomitenta a mai multor clienti.
import java.io.*;
import java.net.*;
import java.util.*;

public class TinyHttpd{
    public static void main ( String args[] ) throws IOException
    {

         ServerSocket ss = new ServerSocket( Integer.parseInt( args[0]));
         while( true )
         {
             Socket s = ss.accept();
             TinyHttpdConnection t = new TinyHttpdConnection( s );
             t.start();
         }
    }
}
 

class TinyHttpdConnection extends Thread{
    Socket client;
    TinyHttpdConnection ( Socket client) throws SocketException{
         this.client = client;
         setPriority(NORM_PRIORITY -1);
    }

    public void run(){
         try{
             BufferedReader in = new BufferedReader(
                new InputStreamReader(client.getInputStream(),"8859_1"));
             OutputStream out = client.getOutputStream();
             PrintWriter pout = new PrintWriter(
                new OutputStreamWriter(out,"8859_1"),true);
             String request = in.readLine();
             System.out.println("Request: "+request);
             StringTokenizer st = new StringTokenizer( request );
             if( (st.countTokens()>=2) && st.nextToken().equals("GET"))
             {
                  if( (request = st.nextToken()).startsWith("/"))
                      request=request.substring( 1 );
                  if( request.endsWith("/") || request.equals(""))
                      request=request+"index.html";
                  try{
                      FileInputStream fis = new FileInputStream( request );
                      byte[] data = new byte[ fis.available()];
                      fis.read(data);
                      out.write(data);
                      out.flush();
                  }
                  catch( FileNotFoundException e ){
                      pout.println(e.toString());
                  }
            }
            else{
                  pout.println("400 Bad request");
            }
            client.close();
         }
         catch( IOException e )
         {
             System.out.println(e.toString());
         }
   }
}

Utilizarea aplicatiei:
Aplicatia de mai sus se utilizeaza in modul urmator. Prima data se compileaza si dupa aceea se executa cu comanda:

java TinyHttpd 1234.

Numarul 1234 reprezinta portul la care serverul asteapta cererile. Dupa ce serverul este lansat in executie, dintr-un program de navigare (browser) se formuleaza o cerere de forma: http://127.0.0.1:1234/Index.html, daca exista in catalogul aplicatiei TyniHttpd fisierul Index.html si daca clientul si serverul se ruleaza pe acelasi calculator. 127.0.0.1 este adresa IP al localhost-ului. Daca aplicatia se ruleaza pe un calculator cu numele unu.doi.com atunci in locul  adresei 127.0.0.1 se pune unu.doi.com.

Descrierea aplicatiei:
Aplicatia defineste doua clase. O clasa care rezolva o cerere pe un fir de executie, TinyHttpdConnection si o clasa TinyHttpd care creaza soclul server si intra in asteptarea clientilor. In momentul aparitiei unui client ( accept()) se creaza un nou fir de executie caruia se transmite referinta la soclul returnat de catre accept(). Aceasta referinta la soclu permite firului de executie sa extraga de aici fluxurile de intrare si de iesire prin care se realizeaza comunicarea efectiva. Firul de executie extrage din flux cu ajurorul unor operatii pe siruri de caractere, numele fisierului cerut. Daca exista fisierul, acesta se deschide pentru citire si datele citite vor fi scrise in soclu pentru a  putea fi extrase si de catre client.

3. Modelul de comunicatie neorientat pe conexiune. Clasele DatagaramSocket si  DatagramPacket

Trimiterea unei datagrame este similara cu trimiterea unei scrisori prin serviciul postal. In cazul trimiterii unei scrisori  avem nevoie de un plic pe care scriem adresa destinatarului si dupa aceea punem scrisoarea in plic si o aruncam intr-o cutie postala. Analog la trimiterea unei datagrame trebuie sa cunoasten adresa si portul calculatorului caruia ii este adresata datagrama, dupa care putem sa punem datele in datagrama si sa le trimitem. Datagramele utilizeaza la nivelul transportului protocolul UDP. Acest protocol este unul nesigur, neorientat pe conexiune. Nu se face confirmare in cazul receptionarii acestor datagrame. Nici calea urmata de aceste datagrame nu se cunoaste de dinainte. De aceea daca trimitem la acelasi destinatar mai multe datagrame, unul dupa altul, nu putem fi siguri nici in ordinea primirii acestor datagrame. Din cauza ca protocolul nu necesita confirmarea sosirii datagramelor este un protocol rapid si se utilizeaza in cazul serviciilor unde nu este nici o nenorocire daca se pierde un pachet-doua (DNS utilizeaza UDP).
 

Descrierea claselor principale:

Clasa DatagramPacket:

Constructori:

DatagramPacket(byte[] buf, int length)
    Construieste un obiect de tip DatagramPacket pentru primirea unui pachet de lungime length

DatagramPacket(byte[] buf, int length, InetAddress address, int port)
     Construieste un obiect de tip DatagramPacket pentru trimiterea  unui pachet de lungime length pe un port specificat, la un host specificat

DatagramPacket(byte[] buf, int offset, int length)
    Construieste un obiect de tip DatagramPacket pentru primirea unui pachet de lungime length, specificand un offset in buffer

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
    Construieste un obiect de tip DatagramPacket pentru trimiterea  unui pachet de lungime length pe un port specificat, la un host specificat, specificand si un offset

Metode:
InetAddress getAddress()
Returneaza adresa IP al calculatorului de la care se primiste sau la care se trimite datagrama
byte[] getData()
Returneaza sirul de octeti care se trimite sau care se primeste
int getLength()
Returneaza lungimea datelor care se trimit sau care se primesc
int getOffset()
Returneaza offsetul datelor care se trimit sau care se primesc
int getPort()
Returneaza numarul portului calculatorului la distanta la care se trimit datele sau de la care se primesc datele
void setAddress(InetAddress iaddr)
                            Seteaza adresa.
void setData(byte[] buf)
                       Seteaza bufferul pentru pachet
void setData(byte[] buf, int offset, int length)
                       Seteaza bufferyl pentru pachet
void setLength(int length)
                       Seteaza lungimea pachetului.
void setPort(int iport)
                    Seteaza portul..

Clasa DatagramSocket

Constructori:
 

DatagramSocket()
Construieste un soclu si leaga la un port liber pe calculatorul local.
DatagramSocket(int port)

           Construieste un soclu si leaga la un portul specificat pe calculatorul local.

DatagramSocket(int port, InetAddress laddr)

Construieste un soclu  si leaga la adresa si portul specificat


Metode:
 

void close()
                       Inchide soclul.
void connect(InetAddress address, int port)
                       Conecteaza soclul la o adresa si port la distanta.
void disconnect()
                       Deconecteaza soclul.
InetAddrress getInetAddress()
                       Returneaza adresa la distanta la care soclul este conectat.
InetAddress getLocalAddress()
                       Returneaza adresa locala la care soclul este conectat.
int getLocalPort()
                       Returneaza portul local la care soclul este legat.
int getPort()
                        Returneaza portul la distanta la care soclul este legat.
void receive(DatagramPacket p)
                       Primire de datagrama prin acest soclu.
void send(DatagramPacket p)
                       Trimitere de datagrama prin acest soclu
.

Exemplu:

Exemplul urmator va fi format  pe partea clientului dintr-un aplet, care instiinteaza aplicatia server ori de cate ori este pornit. Apletul se include intotdeauna intr-o pagina HTML si programul navigator este cel care incarca si ruleaza apletul. Astfel daca pagina HTML reprezinta un homepage vom putea contoriza numarul vizitatorilor la pagina respectiva.

Aplicatia este formata dintr-o parte client si una server. Clientul este un aplet, iar serverul  o aplicatie.

Partea de client:

//Source file: c:\Rose\curs9\exemplu2\ClientPackage\MessageApplet.java

package ClientPackage;

import java.applet.Applet;

public class MessageApplet extends Applet
{
   private String myHost;
   private int myPort;

   public void init()
   {
    myHost = getCodeBase().getHost();
    myPort = Integer.parseInt( getParameter("PORT"));
   }

   public void sendMessage(String message)
   {
    try{
     byte data[] =message.getBytes();
        java.net.InetAddress addr = java.net.InetAddress.getByName( myHost );
        java.net.DatagramPacket packet = new java.net.DatagramPacket(
   data, data.length, addr, myPort );
        java.net.DatagramSocket ds = new java.net.DatagramSocket();
        ds.send( packet );
        ds.close();
    }
    catch( java.io.IOException e ){
     System.out.println(e );
    }
   }
 

   public void start()
   {
    sendMessage("Pagina incarcata");
   }
}


Fisierul HTML:

<applet HEIGHT=10 WIDTH=10 CODE=MessageApplet.class>
<param NAME="PORT" VALUE="1234"></applet>
Apletul utilizeaza metoda getCodeBase() si getHost() pentru aflarea numelui host-ului care il gazduieste, iar  numarul portului se citeste din fisierul HTML. Astfel vor fi initializate datele obiectului MessageApplet, myHost si myPort. Metodele start() si stop() respectiv init() sunt metode publice care vor fi utilizate de catre navigator. init() se apeleaza o singura data la incarcarea paginii, iar start() ori de cate ori se revine la pagina respectiva. Metoda init() initializeaza datele obiectului, iar start() si stop() apeleaza metoda  sendMessage() pentru trimiterea datagramei. Prima data se creaza un obiect InetAddress, care este incarcat cu adresa hostului, dupa care se va crea un obiect de tip DatagramPacket car va contine atat adresa cat mesajul de trimis.
byte data[] =message.getBytes();
//conversie String --> sir de octeti
InetAddress addr = InetAddress.getByName( myHost );
//obtinerea adresei IP al hostului
DatagramPacket packet = new DatagramPacket( data, data.length, addr, myPort );
//crearea pachetului
Dupa ce avem pachetul gata, pregatit pentru trimitere, mai trebuie sa cream un soclu prin care sa se trimita si putem expedia pachetul cu metoda send(). Dupa epedierea pachetului se va inchide soclul.
DatagramSocket ds = new DatagramSocket();
//Crearea soclu
ds.send( packet );
//Trimitere pachet
ds. close();
//Inchidere soclu
Partea server:
import java.net.*;
import java.io.*;
 

public class AppletServer{

    public static void main( String args[]) throws IOException{
             DatagramSocket s = new DatagramSocket( Integer.parseInt(args[0]));
             while( true ){
                 byte data[] = new byte[1024];
                 DatagramPacket packet = new DatagramPacket( data,1024);
                 s.receive( packet );
                 String message = new String( packet.getData());
                 System.out.println("Hostname: "+packet.getAddress().getHostName()+" "+message);
             }
    }
}
 

Partea de server:


//Source file: c:\Rose\curs9\exemplu2\ServerPackage\AppletServer.java

package ServerPackage;

public class AppletServer
{

   public static void main(String[] args)
   {
     try
     {
       java.net.DatagramSocket s = new java.net.DatagramSocket( Integer.parseInt(args[0]));
       while( true ){
          //Serverul se pregateste pentru primirea unui pachet
          byte data[] = new byte[1024];
          java.net.DatagramPacket packet = new java.net.DatagramPacket( data,1024);
          //Serverul asteapta primirea unui pachet
          s.receive( packet );
          //Se scot datele din pachet si se afiseaza la iesirea standard
          String message = new String( packet.getData());
          System.out.println("Hostname: "+ packet.getAddress().getHostName()+" "+message);
      }
     }
     catch( java.io.IOException e ){}

   }
}
Aplicatia server creaza un soclu de tip DatagramSocket si intra in asteptarea clientilor pe un port. Numarul portului se primeste de la linia de comanda, fiind de tip String la primire se va converti la intreg prin apelul metodei Integer.parseInt(args[0]). Dupa crearea soclului se creaza un pachet gol pentru receptionarea datagramelor sosite. Acest pachet este un obiect de tip DatagramPacket care are si o variabila de tip sir de octeti, care se utilizeaza pentru stocarea datelor sosite, insa acesta trebuie alocat inaintea sosirii datelor. Dupa ce s-au creat aceste doua obiecte, soclul apeleaza metoda receive() si intra in astepatarea datagramelor. In momentul sosirii unei datagrame, se extrage din aceasta mesajul si se va afisa pe terminal.

4. URL

URL (Uniform Resource Locator)  identifica o resursa(obiect) pe Internet. In mod uzual este format din trei sau patru parti:
protocol://hostname/nume_resursa sau
protocol://hostname:port/nume_resursa
protocol:  poate fi http, ftp,  gopher etc.
hostname: numele calculatorului pe care se afla resursa, putem specifica si adresa IP, daca o stim in loc de nume host
port: trebuie specificat doar daca serviciul este oferit pe un port nestandard.
nume_resursa: calea si numele resursei

Java ne ofera mai multi constructori pentru crearea unui obiect URL care pot genera exceptia MalformedURLException in caz ca lipseste ceva din specificatia URL. In momentul construirii obiectului nu se verifica existenta resursei, deci exceptia se genereaza daca un parametru este transmis incorect.

URL(String spec)

URL(String protocol, String host, int port, String file)

URL(String protocol, String host, String file)

Exemplu de utilizare:
try{
    URL url = new URL("http","www.uttgm.ro",3128, "index.html");
    System.out.println( url.toString());
}
catch( MalformedException e ){
//Se trateaza exceptia
}
  Un aplet poate comunica doar cu calculatorul pe care este stocat codul lui. Cu ajutorul metodei getDocumentBase() a clasei Applet putem afla numele acestui calculator si dupa acea putem cere accesul la o resursa de pe acel calculator:
try{
    URL url = new URL("http",getDocumentBase(),"index.html");
    System.out.println( url.toString());
}
catch( MalformedException e ){
//Se trateaza exceptia
}
Daca dorim sa aducem datele asociate unui URL trebuie sa cerem obiectului URL referinta la un stream atasat acestuia. Referinta se obtine prin apelul metodei openStream(). Exemplul urmator afiseaza  pe terminal continutul unui fisier HTML.
try{
    URL url = new URL("http://www.uttgm.ro/index.html");
    BufferedReader bin = new BufferedReader( new
                                InputStreamReader(url.openStream()));
    String line;
    while( ( line = bin.readLine()) != null )
        System.out.println( line );
}
catch (Exception e ){}

Exemplu cu URL:

Sa se scrie un aplet care permite utilizatorilor vizualizarea produselor unei firme. Din cauza ca preturile sunt in continua schimbare acestea se stocheaza intr-un fisier "preturi.dat", aflat in acelasi catalog cu codul apletului, precum si cu fisierul HTML. In caz ca se schimba preturile produselor, apletul ramane neschimat, doar fisierul "preturi.dat" se va modifica.
Cand clientul viziteaza pagina Web, browserul incarca prima data fisierul HTML, pe urma codul apletului. Apletul este rulat pe calculatorul clientului, acesta incarcand fisierul "preturi.dat". Pentru a incarca fisierul "preturi.dat", apletul creaza un URL:

try{
    URL url = new URL( getDocumentBase(),"preturi.dat");
}
catch( Exception e ){}

Lista de preturi  este formatat a.i sa fie ptrivit pentru a fi incarcat intr-un obiect Properties:

Mere=7000
Pere=10000
Struguri=25000
Basnana=27000

Incarcarea fisierului intr-un obiect Properties se va realiza prin:

Properties preturi = new Properties();
try{
    preturi.load( url.openStream());
}
catch( Exception e ){}

Interfata apletului:



Diagrama de clasa:




Diagrama de secventiere:

Codul sursa:

//Source file: Linie_de_comanda.java

public class Linie_de_comanda
{
   private String nume;
   private int cantitate;
   private double pretunitar;

   public Linie_de_comanda(String nume, int cantitate, double pretunitar)
   {
    this.nume= nume;
    this.cantitate = cantitate;
    this.pretunitar = pretunitar;
   }

   public String toString()
   {
     String s2 = Integer.toString( cantitate );
     String s3 = Double.toString( pretunitar );
     return nume+" : "+s2+" : "+s3;
   }
}

//Source file: CanvasComanda.java

import java.awt.Canvas;
import java.util.Vector;
import java.awt.Graphics;

public class CanvasComanda extends Canvas
{
   private Vector a;

   public void redraw(java.util.Vector a)
   {
    this.a =a;
    repaint();
   }

   public void paint(Graphics g)
   {
     int i;
     int x=80, y=0;
     java.awt.Font f = new java.awt.Font("Monospaced",java.awt.Font.PLAIN, 12 );
     g.setFont( f );
     java.awt.FontMetrics fm = g.getFontMetrics( f );
     int height = fm.getHeight();
     y+=height;
     g.drawString("Your order: ",x,y);
     for( i = 0; i<a.size(); i++ )
     {
      y+=height;
      g.drawString( a.elementAt(i).toString(), x, y );
     }
   }
}

//Source file: ApletComanda.java

import java.applet.Applet;
import java.awt.event.ActionListener;
import java.awt.Choice;
import java.awt.TextField;
import java.awt.Button;
import java.util.Properties;
import java.util.Vector;
import java.awt.event.ActionEvent;

public class ApletComanda extends Applet implements ActionListener
{
   private Vector a = new java.util.Vector();
   public CanvasComanda comanda;
   public Choice nume;
   public TextField cantitate;
   public Button adaugare;
   public Button gata;
   public Properties preturi;

   public void init()
   {
    setLayout( new java.awt.BorderLayout() );

    java.awt.Panel p = new java.awt.Panel();
    p.setLayout( new java.awt.FlowLayout());
    nume = new Choice();
    preturi = new java.util.Properties();
    try{
      java.net.URL url= new java.net.URL( getDocumentBase(),"preturi.dat");
      preturi.load( url.openStream());
    }
    catch( Exception e ){}

    java.util.Enumeration e = preturi.propertyNames();
    while( e.hasMoreElements())
      nume.addItem( ( String ) e.nextElement());

    p.add( nume );

    cantitate = new TextField( 5);
    p.add( cantitate );

    adaugare = new Button("Adaugare");
    p.add( adaugare );
    adaugare.addActionListener( this );

    gata = new Button( "Gata");
    p.add(gata );
    gata.addActionListener( this );

    add( p, "North");

    comanda = new CanvasComanda();
    add( comanda,"Center");
    comanda.setSize(250,150);
    comanda.redraw( a );
   }

   public void actionPerformed(ActionEvent arg0)
   {
    String command = arg0.getActionCommand();
    if( command.equals("Adaugare")){
      String itemName = nume.getSelectedItem();
      int cant = 0;
      double pret = 0.0;
      try{
       cant = Integer.parseInt( cantitate.getText());
       pret = (new Double( preturi.getProperty( itemName ))).doubleValue();
      }
      catch( NumberFormatException e ){}
      a.addElement( new Linie_de_comanda(itemName, cant, pret ));
      comanda.redraw( a );
    }
   }
}