Documente Academic
Documente Profesional
Documente Cultură
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.
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 ale 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 ale acestora. Nu trebuie sa
cunoastem structurile de date utilizate de acest sistem de protocoale, nici metodele utilizate pentru transmiterea si receptionarea secventelor de octeti.
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 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.
Clientul trebuie sa cunoasca doua lucruri despre server: numele serverului (utilizat pentru determinarea adresei IP a 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 poate fi server paralel (concurent) si server secvential.
Serverul concurent permite deservirea in paralel a mai multor clienti. Acest paralelism 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 client. In Java 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 clienti 2. Cat timp ( mai { stabilire deservire eliberare } un port de comunicare care va fi cunoscut de catre sunt clienti la port ) conexiune cu client ( creare soclu ) client ( flux de intrare, flux de iesire ) conexiune ( eliberare soclu )
Clasa Socket
Constructori:
Socket(InetAddress address, int port)
Creaza un soclu si se conecteaza la adresa IP specificata prin obiectul InetAddress si portul specificat prin parametrul port.
Creaza un soclu si 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 se conecteaza la calculatorul host pe portul specificat prin parametrul port.
Socket(String host, int port, InetAddress localAddr, int localPort)
Creaza un soclu si 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 pe care soclul este creat. Returneaza numarul portului local.
OutputStream
int getPort()
getOutputStream()
Returneza un stream de iesire pentru soclu. Returneaza portul la care soclul este conectat.
Clasa ServerSocket
Constructori:
ServerSocket(int port)
Creaza un soclu server pe portul specificat. Al doilea parametru indica lungimea cozii de asteptare. Metode principale:
Socket accept()
Inchide soclul.
InetAddress getInetAddress()
Exemplul 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:
Incepenm cu descrierea serverului. Serverul are o structura paralela, permite deservirea a mai multor clienti. Pentru fiecare client se va crea un obiect de tip ClientHandle, un obiect care ruleaza intr-un fir de executie separat si realizeaza deservirea clientului.
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 ");
} }
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:
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 ); } }
Exemplul 2
Urmatoarea aplicatie va fi un server web. Aplicatia 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(); } } }
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:
forma: http://127.0.0.1:1234/Index.html, daca exista in catalogul aplicatiei TyniHttpd fisierul Index.html si daca clientul si serverul ruleaza pe acelasi calculator. 127.0.0.1 este adresa IP a localhost-ului. Daca aplicatia 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 i 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.
Constructori:
DatagramPacket(byte[] buf, int length)
Construieste un obiect de tip DatagramPacket pentru primirea unui pachet de lungime length
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 lungimea datelor care se trimit sau care se primesc 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)
.
DatagramSocket(int port)
Inchide soclul
void connect(InetAddress address, int port)
Deconecteaza soclul.
InetAddrress getInetAddress()
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 intro 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:
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 ); } }
Fisierul HTML:
<applet HEIGHT=10 WIDTH=10 CODE=MessageApplet.class> <param NAME="PORT" VALUE="1234"></applet>
Apletul utilizeaza metodele 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() ,stop() , 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 care va contine atat adresa cat si 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 fie trimis si putem expedia pachetul cu metoda send(). Dupa expedierea pachetului se va inchide soclul.
DatagramSocket ds = new DatagramSocket(); //Crearea soclu ds.send( packet ); //Trimitere pachet ds. close(); //Inchidere soclu
Partea de server:
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:
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 care 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 aceasta trebuie alocata 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)
Exemple 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());
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 );
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 formatata a.i sa poata fi incarcata intr-un obiect Properties:
Mere=7000 Pere=10000 Struguri=25000 Basnana=27000
Interfata apletului:
Diagrama de clase:
Diagrama de secventiere:
Codul sursa: 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; } }
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 ); } } } import import import import import import import import java.applet.Applet; java.awt.event.ActionListener; java.awt.Choice; java.awt.TextField; java.awt.Button; java.util.Properties; java.util.Vector; 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 ); } } }