|
SISTEME DISTRIBUITE. SOCKET-URI
Pe scurt despre retele de calculatoare
Sisteme distribuite
O retea este formata dintr-o multime de calculatoare si periferice (imprimante, plotere, scannere, modemuri etc.) conectate intre ele.
Un sistem distribuit este o colectie de calculatoare si/sau procesoare autonome (avand o unitate de comanda proprie), interconectate (capabile de a schimba informatii intre ele). Elementele colectiei poarta numele de noduri. Un astfel de sistem poate fi programat pentru a rezolva probleme de concurenta si de paralelism.
Sistemele distribuite au aparut ca o necesitate pentru:
- schimbul de informatii intre noduri;
- partajarea unor resurse (printere, memorie backup, unitati de disc etc.);
- siguranta in functionare: daca un nod 'cade', intregul sistem trebuie (daca este posibil) sa fie capabil sa asigure in continuare functionarea, prin redirijarea (rutarea) mesajelor prin nodurile functionale.
Tipuri de retele
O prima clasificare a retelelor este constituita de impartirea lor in retele LAN (Local Area Network) si retele WAN (Wide Area Network).
Retelele LAN sunt folosite de obicei pentru a partaja resurse (fisiere si periferice) si de a facilita schimbul de informatii intre nodurile componente. Conectarea tipica este realizata prin cablu, cu o limita data pentru un drum intre doua noduri (de exemplu 10 km).
La fiecare moment de timp, intr-o astfel de retea este transmis un singur mesaj.
Numele retelei desemneaza produsul si nu reteaua (asa cum se intampla la retelele WAN). De exemplu Ethernet este un produs al companiei XEROX.
Problemele algoritmice tipice care se pun pentru astfel de retele sunt urmatoarele:
1) sincronizarea: asteptarea indeplinirii unei conditii;
2) difuzarea totala (broadcasting) : transmiterea unui mesaj catre toate celelalte noduri;
3) selectarea unui proces pentru indeplinirea anumitor actiuni;
4) detectarea terminarii: un nod ce intreprinde o actiune, in care antreneaza si alte noduri, trebuie sa fie capabil sa detecteze momentul incheierii actiunii;
5) excluderea reciproca: utilizarea unor resurse sub excludere reciproca (de exemplu modificarea unui fisier sau scrierea la imprimanta);
6) detectarea si prevenirea blocarii totale (deadlock);
7) gestionarea distribuita a fisierelor.
Problemele algoritmice care se pun pentru astfel de retele sunt urmatoarele:
1) siguranta schimbului de informatii pe fiecare arc al grafului. Informatia eronata trebuie recunoscuta ca atare si corectata. Mentionam ca este posibil ca mesajele sa soseasca intr-un nod in alta ordine decat cea in care au fost transmise, pot fi duplicate etc. Toate aceste aspecte trebuie luate in considerare si tratate corespunzator;
2) selectarea de cai de comunicare (rutare) intre nodurile care schimba informatii, deoarece un graf complet este prea costisitor si in general nici nu este realizabil. Modul de rutare trebuie actualizat in cazul in care un nod al retelei cade (devine nefunctional);
3) evitarea congestionarii unor cai de comunicatie, realizata tot prin (re)rutare;
4) evitarea blocarii totale;
5) securizarea datelor si a transmisiilor.
Deosebirile esentiale intre cele doua tipuri de retele se refera la:
1) securizare: in retele LAN se presupune ca transmiterea este corecta, pe cand in retelele WAN trebuie presupus ca in timpul transmiterii mesajelor se poate intampla 'ceva rau';
2) timpul de comunicare: in retelele WAN timpul de comunicare este mult mai mare (de 10k ori mai mare, k 1) decat in retelele LAN, iar acest timp este la randul sau mult mai mare decat cel cerut de prelucrarile locale;
3) omogeneitatea: in retelele WAN trebuie presupus ca sunt utilizate simultan mai multe protocoale (vezi paragraful urmator), ceea ce pune problema recunoasterii si conversiei intre ele;
4) increderea reciproca: nu este presupusa in retelele WAN si drept urmare trebuie luate masuri de securitate adecvate.
Protocoale
Comunicarea intre componentele unei retele (respectiv unui sistem distribuit) se face prin multimi de reguli, numite generic protocoale.
Un protocol cuprinde atat formatul mesajului ce este efectiv transmis, cat si modul in care trebuie raspuns la mesajul respectiv.
Protocolul folosit pe Internet este IP (Internet Protocol). Mesajele circula in pachete, ce pot fi:
- pachete Internet, pentru care protocolul este TCP (Transmission Control Protocol);
- datagrame, pentru care protocolul este UDP (User Datagram Protocol); nu vom vorbi despre ele.
Internetul foloseste nume (adrese) simbolice pentru retele si pentru masinile gazda; ele se mai numesc nume de domeniu. Lor li se asociaza in mod biunivoc adrese (numerice) IP, folosite efectiv la comunicarea intre nodurile retelei. Asocierea cade in sarcina unui sistem de nume de domeniu (DNS = Domain Name System).
O adresa numerica IP are foma:
a.b.c.d
unde a b c d sunt numere naturale din multimea .
Modelul Client/Server
Dupa cum stim, comunicarea intre nodurile unei retele consta in transmiterea (receptionarea) de pachete catre (de la) gazde ale aceleasi retele sau ale unei alte retele.
Modelul utilizat pe scara larga in sistemele distribuite (si care va fi cel folosit in continuare) este sistemul Client/Server. In acest model exista:
- o multime de procese Server, fiecare jucand rolul de gestionar de resurse pentru o colectie de resurse de un anumit tip;
- o multime de procese Client, fiecare executand activitati care necesita acces la resurse hard/soft disponibile (partajate) de servere.
Un gestionar de resurse poate avea si el nevoie de resurse gestionate de un alt proces Server. Drept urmare, unele procese pot fi atat de tip Client, cat si de tip Server. Dar doar serverele gestioneaza resurse.
Serverele sunt cele care isi incep primele activitatea. In mod tipic, un server ofera succesiv clientilor posibilitatea de a se conecta la el (spunem ca accepta conexiuni de la clienti). La inceput clientul isi manifesta dorinta de a se conecta si, daca serverul este gata sa accepte conexiunea, aceasta se realizeaza efectiv; este vorba deci de o actiune de la client catre server. Apoi transmisia informatiilor devine bidirectionala, fluxul de informatii putand circula acum in ambele sensuri.
Teoretic, activitatea unui server se desfasoara la infinit.
Pentru conectare la server, clientul trebuie sa cunoasca adresa serverului (fie cea numerica, fie cea simbolica), precum si numarul portului pus la dispozitie de server. Portul nu este o locatie fizica, ci o extensie software corespunzatoare unui serviciu. Serverul poate oferi mai multe servicii, pentru fiecare fiind alocat un (numar de) port. Dintre porturile standard amintim:
7 :ping (ecou) 21 : ftp (transfer de fisiere) 23 : telnet
25 : email 79 : finger 80 : Web
110 : POP3 (Post Office Protocol)
Porturile din intervalul 0..1023 sunt in general rezervate pentru servicii speciale (de genul celor de mai sus) si pentru clienti privilegiati.
Clientul initiaza conexiunea prin retea, specificand adresa serverului si portul prin care doreste sa comunice. Serverul trebuie sa precizeze numai portul; apoi el trebuie sa execute o comanda prin care sa anunte ca este gata sa accepte conexiuni pe portul respectiv; drept urmare el ramane in asteptare pana cand un client doreste sa se conecteze si conexiunea este stabilita cu succes. Un server poate accepta conexiuni de la mai multi clienti: pentru fiecare creaza un fir de executare.
Clasa InetAddress
Aceasta clasa, aflata in pachetul java.net, furnizeaza informatii asupra adreselor (simbolica si numerica) unui calculator gazda. Amintim numai metoda:
public static InetAddress getLocalHost()
care returneaza numele calculatorului gazda (pe care se afla in curs de executare aplicatia). Acesta, convertit la String, are forma: adresa_simbolica/adresa_IP.
Exemplul 1. Determinarea adreselor calculatorului gazda se face simplu astfel:
import java.net.*;
class Adrese
catch(UnknownHostException e)
}
Intrebare. 'Are sens sa studiem facilitatile oferite de Java pentru lucru in retea daca avem la dispozitie un singur calculator?'. DA! Putem de exemplu deschide mai multe ferestre de comanda, fiecare corespunzand unui calculator din retea. Drept adresa IP putem folosi numele calculatorului, numele standard 'localhost' sau '127.0.0.1'. Este chiar indicat sa verificam in astfel programele pe care le scriem. Desigur, este vorba doar de o prima verificare, pentru ca lucrul in retea implica si alte aspecte, care nu pot fi surprinse in acest mod de lucru.
Comunicare prin socket-uri
Socket-uri
Socket-urile sunt obiecte folosite pentru transmiterea de date folosind protocolul TCP/IP. Ele sunt obiecte ce trebuie create la ambele capete ale conexiunii. Socket-urile client sunt obiecte de tipul clasei Socket, iar socket-urile server sunt obiecte de tipul clasei ServerSocket; ambele clase fac parte din pachetul java.net. Socket-urilor li se pot atasa un flux de intrare si unul de iesire, prin care pot receptiona/ transmite, date.
Prezentam in continuare schema generala a lucrului cu socket-uri, apeland la facilitatile de intrare/iesire la nivel de octet.
Un mod tipic de creare a unui socket client este urmatorul:
Socket cs = null;
InputStream is = null; OutputStream os = null;
try
catch(UnknownHostException e)
catch(IOException e)
unde adresa este adresa IP a serverului, iar nrport este numarul portului ales pentru comunicare. Socket-ului ii sunt atasate fluxul os ce va fi folosit pentru a transmite date serverului, precum si fluxul is ce va fi folosit pentru receptionarea datelor transmise de server. Dupa caz, fluxurile vor fi inzestrate cu facilitatile de transmitere a datelor primitive, a obiectelor etc.
Un mod tipic de creare a unui socket server este urmatorul:
ServerSocket ss = null; Socket cs = null;
try
catch(IOException e)
InputStream is = null; OutputStream os = null;
try
catch(UnknownHostException e)
catch(IOException e)
Observam ca pentru server nu este necesara precizarea unei adrese IP, ci numai a unui port, care trebuie sa coincida cu cel folosit de client.
Metoda accept lucreaza astfel: se astepta ca un client
sa incerce sa se lege la server pe portul precizat; in momentul in
care acest lucru se intampla si legatura s-a stabilit, metoda
creeaza si intoarce un socket cs de tip client. Acestuia ii atasam un flux de
intrare si unul de iesire. Ca si pentru client, fluxurile
pot fi inzestrate cu facilitatile de transmitere a datelor primitive, a obiectelor etc.
In final toate socket-urile si fluxurile trebuie inchise explicit prin invocarea metodei close
Clasele Socket si ServerSocket
Clasa Socket din pachetul java.net
public class Socket extends Object
ofera constructori publici si metode publice pentru lucrul cu socket-urile client. Dintre ele mentionam urmatoarele:
Socket(String gazda, int port)
throws IOException,UnknownHostException
creeaza un socket si il conecteaza la portul port al serverului cu numele simbolic gazda
Socket(InetAddress adresa, int port) throws IOException
creeaza un socket si il conecteaza la portul port al serverului cu adresa adresa
InputStream getInputStream() throws IOException
intoarce un flux de intrare pentru socket-ul curent;
OutputStream getOutputStream() throws IOException
intoarce un flux de iesire pentru socket-ul curent;
void close() throws IOException
inchide (distruge) socket-ul curent.
Clasa ServerSocket din pachetul java.net
public class ServerSocket extends Object
ofera un constructor public, precum si metode publice pentru lucrul cu socket-urile server. Dintre ele mentionam urmatoarele:
ServerSocket(int port) throws IOException
creeaza un socket de tip server la portul port
Socket accept() throws IOException
asteapta realizarea unei conexiuni la socket-ul server curent. In momentul in care conexiunea este realizata, metoda creeaza si intoarce un socket de tip client;
void close() throws IOException
inchide (distruge) socket-ul curent.
Exemplul Dorim sa aflam daca un server avand o adresa data ofera sau nu servicii Web pe un anumit port.
Programul se reduce la incercarea de a crea un socket de tip client pentru adresa si portul citite de la intrarea standard.
import java.util.*;
import java.io.*;
import java.net.*;
public class WebCheck
catch(IOException e)
System.out.println(out);
}
Exemplul 3 Chat (conversatie) intre doua calculatoare.
Pentru simplificare, consideram ca fiecare mesaj trimis de la un calculator la celalalt se reduce la o linie de text.
Serverul este primul care trebuie pornit. Apoi clientul este cel care incepe discutia, transmitand un mesaj si asteptand sa primeasca mesajul de raspuns, activitati care se repeta pana cand clientul transmite mesajul 'STOP'. Serverul repeta si el succesiunea de activitati receptie + transmisie pana cand primeste mesajul 'STOP'
Clientul va folosi unitatea de compilare Chat_C.java, iar serverul va folosi unitatea de compilare Chat_S.java
// Unitatea de compilare Chat_C.java
import java.net.*;
import java.io.*;
import java.util.*;
class Chat_C
catch(UnknownHostException e)
DataInputStream dis; DataOutputStream dos;
dis = new DataInputStream(cs.getInputStream());
dos = new DataOutputStream(cs.getOutputStream());
String linie;
for( ; ; )
System.out.println('GATA');
cs.close(); dis.close(); dos.close();
}
// Unitatea de compilare Chat_S.java
import java.net.*;
import java.io.*;
import java.util.*;
class Chat_S
cs.close(); dis.close(); dos.close();
}
Exemplul 4 Un server capabil sa comunice cu mai multi clienti.
Prezentam mai jos o solutie pentru construirea unui server capabil sa comunice cu mai multi clienti.
Actiunea clientilor se rezuma la conectarea lor la server:
// Unitatea de compilare Client_n.java
import java.net.*;
import java.util.*;
class Client_n
In esenta serverul porneste cate un fir de executare pentru fiecare client care se conecteaza la el. Mai precis:
serverul creaza un socket de server ss, care in mod repetat asteapta ca un client sa se conecteze cu succes; cand acest lucru se realizeaza, este obtinut un socket de client cs si este pornit un fir de executare de tipul clasei Conexiune, fir caruia ii este comunicata referinta la cs si identitatea clientului (al catelea este el conectat la server); apoi serverul reia asteptarea conexiunii de la alt client;
sa observam ca firul nou creat este anonim; ca urmare actiunea care trebuie sa o intreprinda cade in sarcina constructorului. Concret, constructorul creaza fluxurile atasate lui cs si invoca, prin start(), metoda run a clasei Conexiune; actiunea care trebuie intreprinsa este inclusa in aceasta metoda. In acest exemplu actiunea consta in tiparirea de 200 de ori, la intervale variabile de timp, a identitatii clientului.
// Unitatea de compilare Server_n.java
import java.io.*;
import java.net.*;
import java.util.*;
class Server_n
}
class Conexiune extends Thread
public void run()
}
catch(InterruptedException e)
}
Observatie. In exemplul de mai sus firele corespunzatoare clientilor foloseasc o resursa comuna, ceea ce conduce la probleme de concurenta. Intr-adevar, chiar pentru doi clienti, cele cate 200 de tipariri ale identitatilor lor apar intercalate. Eventuala asigurare a excluderii reciproce, sau rezolvarea altor probleme de concurenta, cade in sarcina programatorului !