Catedra de Telecomunicatii                 

 

        13/11/2007                                             

 

 

                Software pentru Telecomunicatii (SwTc) - CTS  

 

 

 

 

Laborator 2

Introducere in programarea orientate spre obiecte (OO). Fire de executie (threads) si socket-uri flux (TCP) in Java

 

2.1. Descrierea laboratorului

 

In aceasta lucrare de laborator vor fi acoperite urmatoarele probleme:

 

- Utilizarea mediului de dezvoltare integrat (IDE) BlueJ (vezi si Tutorial BlueJ in limba romana)

- Obiecte si clase (Java)

            - Crearea obiectelor si invocarea metodelor Java cu BlueJ

            - Tipuri de date Java si starea unui obiect

            - Comportamentul unui obiect si interactiunea obiectelor

            - Codul sursa Java, editarea si compilarea lui in BlueJ

 

- Fire de executie (threads)

- Programe de lucru cu fire de executie

 

- Socket-uri flux (TCP) in Java:

- Incapsularea adreselor IP in Java. Utilizarea clasei java.net.InetAddress

- Socket-uri pentru fluxuri (conexiuni) TCP. Utilizarea clasei java.net.Socket

- Socket-uri pentru servere TCP. Utilizarea clasei java.net.ServerSocket

 

- Programe de lucru cu socket-uri flux (TCP) clienti si servere

 

- Teme de casa

 

 

2.2. Obiecte si clase (Java)

2.2.1. Definitii

 

 

Programele de calcul sunt secvente de instructiuni care prin executia lor pe sisteme (masini) de calcul rezolva probleme aparute in diferite domenii ale lumii reale. Programele sunt solutii ale acestor probleme.

Un program scris intr-un limbaj orientat spre obiecte (OO) reprezinta un model al unei parti din lumea reala.

 

 

Elementele care compun modelul (numite obiecte software) sunt construite prin analogie cu entitati care apar in lumea reala (obiecte reale, concepte).

Obiectele software obtinute prin modelare (analogie cu lumea reala) trebuie reprezentate in limbajul de programare.

 

Ca si in cazul obiectelor si conceptelor din lumea reala, obiectele software pot fi categorisite. O constructie software (structura complexa) numita clasa descrie intr-o forma abstracta toate obiectele de un tip particular.

 

 

La fel ca in lumea reala, in care obiectele si conceptele sunt clasificate pe baza atributelor esentiale pe care le au acestea, clasele reprezinta obiecte software care au atribute similare (atributele fiind elemente de date, variabile interne, proprietati care caracterizeaza obiectele).

 

De exemplu, la intrarea intr-un laborator noi clasificam obiectele individuale: banci, studenti, si interactionam cu ele pe baza categoriei lor fara a fi necesar sa le cunoastem toate detaliile (atributele).

 

 

Clasa defineste elementele comune, numite in Java campuri (atribute in teoria orientarii spre obiecte) si metode (operatii in teoria orientarii spre obiecte), ale unei categorii de obiecte. Clasa reprezinta astfel tipul de date al obiectelor.

 


De exemplu, toate obiectele clasificate ca banci au latime, inaltime, pozitie in sala, etc. Clasa Banca poate fi definita prin campurile ei ca:

class Banca

   latime

   inaltime

   pozitie


 

Obiectul este un exemplu specific al unei clase, numit instanta a clasei, in care fiecare camp are o anumita valoare, clasa fiind tiparul dupa care sunt construite obiectele.

 

De exemplu, o sala poate avea 30 de obiecte clasificate ca banci, fiecare banca avand propriile valori ale atributelor latime, inaltime, etc. Doua obiecte banca, banca1 si banca2, sunt instante (exemple) diferite ale aceleiasi clase Banca, au in comun atributele, dar pot avea diferite valori ale lor:

 

banca1

 

banca2

latime   80 cm

inaltime 70 cm

pozitie  rand 2, a 3-a

 

latime   120 cm

inaltime 70 cm

pozitie  rand 4, a 6-a

 

 

 

 

 

 

In laborator:

1. Numiti 2 clase de obiecte din imediata voastra vecinatate in acest moment.

2. Scrieti numele fiecarei clase si apoi numele a cate trei atribute evidente ale fiecarei clase.

3. Pentru cate un obiect din fiecare clasa definiti valori ale fiecaruia dintre cele trei campuri.

 

 

 

2.2.2. Crearea obiectelor

 

Pentru a crea noi instante ale unor banci reale trebuie folosite profile de lemn, metal, etc. Atunci cand modelam bancile intr-un program de calcul putem sa cream doua banci (in limbajul Java) folosind urmatoarele portiuni de cod:

                       


new Banca()  

 

 

 

new Banca()  

 


Pentru a putea trata (accesa) distinct cele doua obiecte, este necesara utilizarea a doua nume diferite pentru cele doua obiecte, ceea ce ar corespunde codului Java:

                       


Banca banca1 = new Banca()  

 

 

Banca banca2 = new Banca()  

 


In laborator:

1. Dati nume cate unui obiect Java din fiecare dintre cele doua clase anterior numite.

2. Scrieti codul Java pentru crearea celor doua obiecte.

 

 

In laborator:

1. Lansati in executie mediul de dezvoltare BlueJ.

2. Deschideti proiectul numit shapes.

I. Click pe meniul Project, apoi selectati Open Project … (sau direct Ctrl+O)

II. Selectati succesiv C:\, BlueJ, examples, shapes, (sau scrieti C:\BlueJ\examples\shapes)

 

 

 

 

In laborator:

1. Click-dreapta (meniul pop-up) pe , selectati new Circle(), acceptati valoarea implicita.

2. Creati un alt cerc, acceptand din nou valoarea implicita oferita de BlueJ.

3. Creati un patrat (Square) in aceleasi conditii.

 

 

 

In laborator:

1. Click-dreapta (meniul pop-up) pe primul obiect de tip cerc  si selectati Inspect.

2. Repetati operatia pentru al doilea cerc. Apoi comparati valorile atributelor (campurilor – fields).

 

    

 

2.2.3. Apelul (invocarea) metodelor

 

Metoda Java (operatia in teoria OO), atunci cand este executata, realizeaza o secventa de actiuni (reprezentate in programe prin instructiuni) asupra obiectului caruia ii apartine.

 

Actiunile realizate de executia metodelor au in general efect asupra valorilor campurilor (atributelor) obiectului. Efectele acestor actiuni pot fi combinatii intre:

- modificarea valorilor campurilor obiectului, ca in cazul metodelor de tip setCamp(),

- obtinerea valorilor campurilor, ca in cazul metodelor de tip getCamp(),

- realizarea altor sarcini utilizand aceste valori.

 

Regruparea mai multor elemente de date (campuri/atribute) si/sau de comportament (metode/operatii) asociate se numeste incapsulare.

 

Incapsularea OO (orientata spre obiecte) inseamna in plus ascunderea detaliilor interne de tip:

- informatii (setul de campuri/atribute),

- si implementare (setul de coduri interne ale metodelor/operatiilor),

in spatele unei interfete publice (setul de declaratii/semnaturi ale metodelor/operatiilor).

 

In laborator:

1. Click-dreapta pe obiectul circle1 si selectati void makeVisible().

2. Click pe  pentru urmari efectul grafic al apelului metodei makeVisible().

3. Click-dreapta pe obiectul circle1 si selectati moveUp(). Urmariti efectul grafic.

4. Repetati apelul moveUp(), urmarind efectul grafic.        

 

 

2.2.4. Parametrii metodelor

 

Parametrii specifica valorile de intrare necesare metodelor pentru a fi executate.

 

Declaratiile (semnaturile) metodelor pot include liste de declaratii de parametri. Acesti parametri sunt variabile care au ca scop intregul corp al metodei si se numesc parametri formali sau simplu parametri. Parametrii formali sunt declarati ca orice variabila, folosind formatul tipVariabila numeVariabila.

 

Apelurile metodelor pot include liste de valori date parametrilor, valori care trebuie sa corespunda ca tip celor declarate. Valorile pasatele metodelor in momentul apelurilor se numesc parametri actuali sau simplu argumente.

 

De exemplu, apelul circle1.changeSize(50) specifica valoarea 50 ca argument, utilizat de metoda changeSize() pentru a da valoarea 50 diametrului cercului.

 

In laborator:

1. Click-dreapta pe obiectul circle1 si selectati void makeVisible().

2. Click pe  pentru urmari efectul grafic al apelului metodei.

3. Click-dreapta pe circle1 si selectati void changeSize(int newDiameter).

4. Stabiliti valoarea diametrului la 150 in fereastra care apare pe ecran. Urmariti efectul grafic.

5. Apelati metoda void slowMoveVertical(int distance) pasandu-i 50. Urmariti efectul grafic.

6. Apelati de mai multe ori metoda void moveUp(). Urmariti efectul grafic. Comparati efectele.

7. Apelati metoda void slowMoveHorizontal(int distance) pasandu-i 50. Urmariti efectul grafic.

 

      

 

 

 

2.2.5. Tipuri de date

 

Descrierea problemelor reale sub forma de modele reprezentate ca programe de calcul necesita definirea datelor problemei.

 

Urmatoarele campuri descriu obiectul circle1 de tip Circle:

 


circle1

int diameter       30

int xPosition      20

int yPosition      60

String color       "blue"

boolean isVisible  false


Tipul de date este o descriere abstracta a unui grup de entitati asemanatoare.

 

Tipul de date defineste structura variabilelor si domeniul de definitie al valorilor. Mai exact, tipul de date specifica:

- spatiul de memorie alocat pentru stocarea valorii campului/parametrului/variabilei (de ex., 4B = 32b pentru tipul int, 1b pentru tipul boolean, etc.),

- gama/multimea valorilor posibile (-231…231-1 pentru int, valorile true si false pentru boolean),

- formatul valorilor literale/de tip imediat (de ex., 100000 sau -2000 pentru tipul int, true sau false pentru tipul boolean, etc.),

- regulile privind conversiile catre alte tipuri (de ex., tipul int se poate converti direct, implicit, la tipurile long, float si double, si poate fi convertit explicit, prin cast – conversie prin trunchiere, la tipurile byte si short, pe cand tipul boolean nu poate fi convertit la nici un alt tip, etc.),

- valorile implicite (doar in cazul campurilor!, 0 pentru tipul int, false pentru tipul boolean, etc.),

- operatorii asociati (permisi) – care tin de partea de prelucrare asupra datelor.

 

Tipurile de date primitive Java:

 

Categorie

Tip

Valoare implicita

Spatiu memorie

Gama valori

Conversii explicite

(cast, trunchiere)

Conversii implicite

(extindere)

Valori intregi

cu

semn

byte

0

8 biti (1B)

-128 … 127

La char

La short, int, long, float, double

short

0

16 biti (2B)

-32768 … 32767

La byte, char

La int, long, float, double

int

0

32 biti (4B)

-2147483648 … 2147483647

La byte, short, char

La long, float, double

long

0l

64 biti (8B)

-9223372036854775808 …9223372036854775807

La byte, short, int, char

La float, double

Valori

in

virgula mobile

cu

semn

float

0.0f

32 biti (4B)

+/-1.4E-45 … +/-3.4028235E+38,
+/-infinity, +/-0, NAN

La byte, short, int, long, char

La double

double

0.0

64 biti (8B)

+/-4.9E-324 …

+/-1.8+308,
+/-infinity, +/-0, NaN

La byte, short, int, long, float, char

Nu exista

(nu sunt necesare)

Caractere codificate UNICODE

char

\u0000

(null)

16 biti (2B)

\u0000 … \uFFFF

La byte, short

La int, long, float, double

Valori logice

boolean

false

1 bit folosit din 32 biti

true, false

Nu exista

(nu sunt posibile)

Nu exista

(nu sunt posibile)

 

In Java, pe langa tipurile de date primitive, exista si tipuri de date complexe numite tipuri referinta, tablourile si clasele.

 

 

In laborator:

1. Click-dreapta pe obiectul circle1 si selectati void makeVisible().

2. Click pe  pentru urmari efectul grafic al apelului metodei.

3. Apelati metoda void changeColor(String newColor) pasandu-i "red". Urmariti efectul grafic.

4. Apelati metoda void changeColor(String newColor) pasandu-i "rosu". Ce observati?

5. Apelati metoda void changeColor(String newColor) pasandu-i red. Ce observati?

 

2.2.6. Starea unui obiect

 

Ansamblul valorilor campurilor (atributelor) unui obiect la un moment dat reprezinta starea obiectului.

Starea unui obiect poate diferi in timp, ca urmare a comportamentului. Starea a doua obiecte de acelasi tip poate fi diferita la un moment dat.

 


Optional, in laborator:

1. Inspectati starea obiectului circle1 cu double-click pe  (sau click-dreapta si Inspect).

2. Schimbati culoarea obiectului circle1si inspectati-i din nou starea (valorile campurilor).

3. Creati doua obiecte . Inspectati-le starea.

4. Au toate campurile aceleasi nume? Sunt toate valorile aceleasi?

5. Apelati metode care schimba pozitia celor doua obiecte. Inspectati-le starea. Ce s-a schimbat?

6. Creati doua obiecte din clase diferite. Inspectati-le starea. Ce campuri au aceleasi nume?

 

 

2.2.7. Comportamentul unui obiect

 

 

O metoda realizeaza o actiune asupra valorilor campurilor obiectului caruia ii apartine, putand folosi valorile acelor campuri, si astfel efectueaza o sarcina pentru codul (context) care a apelat-o.

Metoda este un atom de comportament al obiectului.

Comportamentul global al obiectului este obtinut prin inlantuirea apelurilor de metode.

Toate obiectele din aceeasi clasa au aceleasi metode disponibile.

Clasa Circle are metodele:

 

<--proprii

 

       ^

mostenite

 
Text Box:                  

 

 

 

 

 

 

 


In laborator:

1. Creati o imagine care sa schiteze o casa si un soare similare celor din imaginea de mai sus.

2. Notati-va sarcinile pe care le-ati indeplinit pentru a obtine acest efect.

De exemplu:

I.   Circle circle1 = new Circle()   // altfel spus, e creat un cerc

II.  circle1 makeVisible()           // apoi e facut vizibil cercul

III. circle1 moveHorizontal(200)     // e deplasat orizontal 200 pixeli

IV.  circle1 changeSize(50)          // e redimensionat la 50 pixeli

V.   circle1 changeColor("yellow")   // si e colorat in galben

VI.  ...

3. Ar fi putut fi apelate metodele in alta ordine pentru a obtine acelasi efect?

 

Observatie: Pentru a obtine automat pasii in forma electronica se deschide Terminal Window (cu View->Show Terminal sau cu Ctrl+T) si se seteaza in acea fereastra Options -> Record method calls. In acest fel in Terminal Window vor fi scrisi automat pasii parcursi, ca in exemplul care urmeaza.

 

        

 

2.2.8. Interactiunea (colaborarea) obiectelor

 

Sarcinile realizate manual in exercitiul anterior sunt in mod normal scrise sub forma de instructiuni Java intr-un fisier, pentru a putea fi executate din nou. Primii 5 pasi ar fi scrisi in Java:

 


     Circle circle1 = new Circle();

     circle1.makeVisible();

     circle1.moveHorizontal(200);

     circle1.changeSize(50);

     circle1.changeColor("yellow");

 

BlueJ ofera un exemplu de program (proiectul picture) care contine pe langa clasele Canvas, Circle, Square si Triangle si codul unei clase Picture care creaza obiectele necesare si le apeleaza metodele, astfel incat ele sa fie pozitionate, dimensionate si colorate ca in desenul anterior.

Obiectul de tip Picture interactioneaza (colaboreaza, comunica prin mesaje = apeluri metode) cu obiectele de tip Circle, Square si Triangle pentru a realiza sarcina globala.

 


In laborator:

1. Deschideti proiectul numit picture (Ctrl-O, apoi pe C:\BlueJ\examples selectati picture)

2. Creati un obiect Picture.

3. Apelati metoda void draw().

4. Click pe  pentru urmari efectul grafic al apelului metodei.

 

 

2.2.9. Codul sursa Java. Editarea si compilarea cu BlueJ

 

Sarcinile pentru crearea obiectelor si apelul metodelor pot fi scrise sub forma de instructiuni Java, salvate intr-un fisier, utilizate si reutilizate (executate) cand este nevoie de ele.

Listele instructiunilor Java (grupate in metode, iar acestea impreuna cu campurile) definesc o clasa Java. Textul scris al instructiunilor formeaza codul sursa al clasei. Pentru a fi executate, instructiunile trebuie mai intai compilate (translatate) cu compilatorul javac la cod de octeti Java. Apoi codul de octeti este executat de interpretorul java.


In laborator:

1. Deschideti proiectul numit picture.

2. Vizualizati codul sursa al clasei Picture, fie double-click pe , fie right-click, Open Editor.

3. Care este numele clasei? Gasiti instructiunea care defineste numele clasei.

4. Gasiti campurile pentru soare si partile componente ale casei. Observati cum sunt declarate.

 

 

 

In laborator:

1. Gasiti codul metodei a carei declaratie (semnatura) este public void draw().

2. Care sunt sarcinile elementare (instructiunile de tip apel) indeplinite pentru a crea zidul?

3. Conteaza ordinea in care sunt efectuate aceste sarcini?

 

 

 

 

 

 

 

 

In laborator:

1. Vizualizati codul sursa al clasei Square. Gasiti semnaturile metodelor invocate in metoda draw() a clasei Picture. Care sunt sarcinile indeplinite de codurile acestor metode?

 

 

 

 

2.3. Utilizarea firelor de executie (threads) in Java

2.3.1. Modalitati de a crea un fir de executie

 

Programele de calcul simple sunt secventiale, fiecare avand un inceput, o secventa de executii si un sfarsit. In orice moment pe durata executiei unui astfel de program exista un singur punct de executie.

        

 

Un fir de executie (thread), sau mai simplu, un fir, este similar acestor programe secventiale, in sensul ca are un inceput, o secventa de executii si un sfarsit, si in orice moment pe durata executiei firului exista un singur punct de executie.

Totusi, un fir nu este el insusi un program, deoarece nu poate fi executat de sine statator. In schimb, firul este executat (ruleaza) intr-un program. Posibilitatea utilizarii mai multor fire de executie intr-un singur program, ruland (fiind executate) in acelasi timp si realizand diferite sarcini (nu in mod necesar diferite), este numita multithreading.

 

Pentru a crea un nou fir de executie exista doua modalitati.

 

1. Se poate declara o clasa ca subclasa a clasei Thread, subclasa care trebuie sa rescrie codul (override) metodei run() a clasei Thread (care nu contine nici un cod), noul fir de executie fiind creat prin alocarea si lansarea unei instante a subclasei.

 

Formatul pentru crearea unei subclase care extinde clasa Thread si ii reimplementeaza metoda run() este urmatorul:

                       

     class FirT extends Thread {
        public void run() {
                              // codul firului de executie
        }
     }

 

Formatul pentru crearea unei instante a subclasei este urmatorul:

                       

       FirT fir = new FirT();

 

2. Se poate declara o clasa care implementeaza interfata Runnable, interfata care contine doar declaratia unei metode run() (declaratie identical celei din clasa Thread, deoarece clasa Thread implementeaza interfata Runnable), noul fir de executie fiind creat prin alocarea unei instante a noii clase, pasarea acestei instante ca parametru al constructorului, la crearea unei instante a clasei Thread, si lansarea acelei instante a clasei Thread.

 

Formatul pentru crearea unei clase care implementeaza interfata Runnable si ii implementeaza metoda run() este urmatorul:

                       

     class FirR implements Runnable {
        public void run() {
                              // codul firului de executie
        }
     }

 

Formatul pentru crearea unei instante a noii clase si apoi a unei instante a clasei Thread este urmatorul:

       FirR r = new FirR();
       Thread fir = new Thread(r);

 

In ambele cazuri formatul pentru lansarea noului fir de executie, este urmatorul:

      fir.start();    

 

Variante compacte pentru crearea si lansarea noilor fire de executie:

       new FirT().start();                   // nu exista variabila de tip FirT 
                                             // care sa refere explicit firul
       FirR r = new FirR();                  // nu exista variabila de tip Thread
       new Thread(r).start();                // care sa refere explicit firul 
       Thread fir = new Thread(new FirR());  // nu exista variabila de tip FirR
       fir.start();                          // care sa refere explicit firul                                        
       new Thread(new FirR()).start();       // nu exista variabila de tip Thread
                                             // si nici variabila de tip FirR 
                                             // care sa refere explicit firul 

 

2.3.2. Programe de lucru cu fire de executie

 

Urmatorul program va fi folosit pentru a ilustra lucrul cu fire de executie. Clasa FirSimplu extinde clasa Thread, iar metoda principala lanseaza metoda run() ca nou fir de executie.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

  public class FirSimplu extends Thread { // obiectele din clasa curenta sunt thread-uri

    public FirSimplu(String str) {

        super(str);                       // invocarea constructorului Thread(String)

    }                                     // al superclasei Thread

    public void run() {                   // “metoda principala” a thread-ului curent

        for (int i = 0; i < 5; i++) {

            System.out.println(i + " " + getName()); // obtinerea numelui thread-ului

            try {

                sleep((long)(Math.random() * 1000)); // thread-ul “doarme” 0...1 secunda

            } catch (InterruptedException e) {}

        }

        System.out.println("Gata! " + getName());    // obtinerea numelui thread-ului

    }

    public static void main (String[] args) {

        new FirSimplu("Unu").start();         // “lansarea” thread-ului (apeleaza run())

    }

  }

 

Rezultatul executiei programului:                        

 

Clasa DemoDouaFire lanseaza doua fire de executie de tip FirSimplu executate concurent.

1

2

3

4

5

6

 public class DemoDouaFire {

    public static void main (String[] args) {

        new FirSimplu("Unu").start();     // “lansarea” primului thread

        new FirSimplu("Doi").start();     // “lansarea” celui de-al doilea thread

    }

 }

 

Firele au evolutii diferite, in functie de durata intarzierii introdusa in linia de cod:

          sleep((long)(Math.random() * 1000));

 

a programului FirSimplu.

 

In laborator:

1. Lansati mediul BlueJ. Inchideti proiectele anterioare (Ctrl+W). Creati un nou proiect fire

    (Project -> New Project…, selectati D:/, apoi SwTc2007, apoi numarul grupei, si scrieti fire).

2. Creati o noua clasa, numita FirSimplu, folosind codul dat mai sus.

3. Tot in proiectul fire creati clasa DemoDouaFire folosind codul de mai sus. Compilati clasele.

4. Right-click pe clasa, selectati si executati main(). Urmariti efectul in Terminal Window.

Rezultatele a doua executii succesive:                     

Doua variante (aplicatie de sine statatoare si applet) de program "ceas" bazat pe threads:

import java.util.*;

import java.text.DateFormat;

import java.io.*;

 

public class CeasConsola implements Runnable

{

  private Thread firCeas = null;

  private static int contor = 0;

 

 

  public void start() {                   // (re)pornire fir

    if (firCeas == null) {

      firCeas = new Thread(this);

      firCeas.start();                    // apeleaza run()

    }

    System.out.println("Ceas pornit");

  }

  public void run() {                    // executie fir

    Thread firulMeu = Thread.currentThread();

    contor++;

 

    while (firCeas == firulMeu) {

 

      Calendar cal = Calendar.getInstance();

      Date date = cal.getTime();

      DateFormat df = DateFormat.getTimeInstance();

      System.out.println("[" + contor + "]  " +

                   df.format(date));   

 

      try { Thread.sleep(1000); }

      catch (InterruptedException e){ }

    }

  }

 

  public void stop() {                      // oprire fir

    System.out.println("Ceas oprit");

    firCeas = null;                          // firul dispare

  }

  public static void main(String[] args)

                            throws IOException {

    CeasConsola ceas;

    BufferedReader in = new BufferedReader

          (new InputStreamReader(System.in));

    while (true) {

      ceas = new CeasConsola();

      ceas.start();        // (re)pornire fir la citirea

      in.readLine();       // unui caracter de la consola

      ceas.stop();        // oprire fir la citirea

      in.readLine();       // unui caracter de la consola

    }

  }

}

import java.applet.Applet;

import java.awt.*;

import java.util.*;

import java.text.DateFormat;

public class CeasApplet extends Applet

                                             implements Runnable {

  private Thread firCeas = null;

  private static int contor = 0;

  public void init() { setBackground(Color.white); }

 

  public void start() {                       // (re)pornire fir

    if (firCeas == null) {

      firCeas = new Thread(this, "Ceas");

      firCeas.start();                         // apeleaza run()

    }

  }

 

  public void run() {                        // executie fir

    Thread firulMeu = Thread.currentThread();

    contor++;

 

    while (firCeas == firulMeu) {

      repaint();         // apeleaza paint() (redeseneaza)

      try { Thread.sleep(1000); }

      catch (InterruptedException e){ }

    }

  }

  public void paint(Graphics g) {      // deseneaza

     Calendar cal = Calendar.getInstance();

     Date date = cal.getTime();

     DateFormat df = DateFormat.getTimeInstance();

     g.drawString("[" + contor + "]  " +

                        df.format(date), 5, 10);

  }

  public void stop(){                        // oprire fir

    firCeas = null;                           // firul dispare

  }

}

// Applet-ul nu are metoda principala.

// Metodele start(), stop(), run(), etc. sunt invocate

// de Browserul Web in care este “executat”.

/*     Exemplu de includere intr-o pagina Web:

    <HTML>
         <TITLE>  
              Applet Ceas  
         </TITLE>
         <BODY> 
              <APPLET CODE=CeasApplet.class 
                     WIDTH=200 HEIGHT=50> 
              </APPLET> 
         </BODY>
    </HTML>     */

 

Varianta applet (miniaplicatie) Java CeasApplet:

 

 

Ceas

 

 

In laborator: Testati applet-ul CeasApplet folosind link-ul de mai sus.

 

2.4. Introducere in socket-uri Java

2.4.1. Utilizarea clasei java.net.InetAddress

 

Clasa InetAddress incapsuleaza o adresa IP intr-un obiect care poate intoarce informatia utila. Aceasta informatie utila se obtine invocand metodele unui obiect al acestei clase. De exemplu, equals() intoarce adevarat daca doua obiecte reprezinta aceeasi adresa IP.

 

Clasa InetAddress nu are constructor public. De aceea, pentru a crea obiecte ale acestei clase trebuie invocata una dintre metodele de clasa getByAddress() si getByName().

 

O adresa IP speciala este adresa IP loopback (tot ce este trimis catre aceasta adresa IP se intoarce si devine intrare IP pentru gazda locala), cu ajutorul careia pot fi testate local programe care utilizeaza socket-uri. Pentru a identifica adresa IP loopback sunt folosite numele "localhost" si valoarea numerica "127.0.0.1".

 

Pentru a obtine InetAddress care incapsuleaza adresa IP loopback pot fi folosite apelurile:

 

 

InetAddress.getByName(null)

InetAddress.getByName("localhost")

InetAddress.getByName("127.0.0.1")

 

2.4.2. Socket-uri flux (TCP) Java

 

Java ofera, in pachetul java.net, mai multe clase pentru lucrul cu socket-uri flux (TCP).  Urmatoarele clase Java sunt implicate in realizarea conexiunilor TCP obisnuite: ServerSocket, Socket.

Clasa ServerSocket reprezinta socket-ul (aflat eventual pe un server bazat pe TCP) care asteapta si accepta cereri de conexiune (eventual de la un client bazat pe TCP).

Clasa Socket reprezinta punctul terminal al unei conexiuni TCP intre doua masini (eventual un client si un server).

 

Clientul (sau, mai general, masina conector) creeaza un punct terminal Socket in momentul in care cererea sa de conexiune este lansata si acceptata.

Serverul (sau, mai general, masina acceptor) creeaza un Socket in momentul in care primeste si accepta o cerere de conexiune, si continua sa asculte si sa astepte alte cereri pe ServerSocket.

 

Secventa tipica a mesajelor schimbate intre client si server este urmatoarea:

Odata conexiunea stabilita, metodele getInputStream() si getOutputSteam() ale clasei Socket trebuie utilizate pentru a obtine fluxuri de octeti, de intrare respectiv iesire, pentru comunicatia intre aplicatii.

 

2.4.3. Utilizarea clasei Socket

 

Secventa tipica pentru crearea socket-ului unei aplicatii conector (client):

 

1

2

3

4

5

6

7

8

    // Stabilirea adresei serverului

    String adresaServer = "localhost";

 

    // Stabilirea portului serverului

    int portServer = 2000;

 

    // Crearea socketului (implicit este realizata conexiunea cu serverul)

    Socket socketTCPClient = new Socket(adresaServer, portServer); 

 

Dupa utilizare, socket-ul este inchis. Secventa tipica pentru inchiderea socket-ului:

 

1

2

    // Inchiderea socketului (implicit a fluxurilor TCP)

    socketTCPClient.close();

 

Crearea unui socket pentru aplicatii conector/client si afisarea informatiilor privind socketul:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

import java.net.*; // pachetul claselor de biblioteca pentru socketuri

import java.io.*;  // pachetul claselor de biblioteca pentru intrare-iesire

 

public class InfoSocketTCP { // Afiseaza info. privind conexiunea TCP creata

 

   public static void main (String args[]) {

      BufferedReader inConsola  = new BufferedReader(new

                                    InputStreamReader(System.in));

      String adresaIP = null;

      int numarPort = 0;

      Socket conexiuneTCP;

      try {

         System.out.print("Introduceti adresa IP dorita: ");

         adresaIP = inConsola.readLine();

 

         System.out.print("Introduceti numarul de port dorit: ");

         numarPort = Integer.parseInt(inConsola.readLine());

 

         conexiuneTCP = new Socket(adresaIP, numarPort);  // creare socket

 

         afisareInfoSocketTCP(conexiuneTCP);              // afisare info socket

 

         conexiuneTCP.close();                            // inchidere socket

      }

      catch (NumberFormatException ex) {                  // eroare numar port

         System.err.println("Numarul de port nu are format intreg");

      }

      catch (UnknownHostException ex) {                   // eroare DNS

         System.err.println("Nu poate fi gasita adresa " + adresaIP);

      }

      catch (SocketException ex) {                        // eroare conectare

         System.err.println("Nu se conecteaza la " + adresaIP + ":" + numarPort);

      }

      catch (IOException ex) { 

         System.err.println(ex);

      }

   }

 

   // Afiseaza informatii privind socketul TCP primit ca parametru

   public static void afisareInfoSocketTCP (Socket socketTCP) {

      System.out.println("\nConexiune de la adresa "+socketTCP.getLocalAddress()

                         + " de pe portul " + socketTCP.getLocalPort()

                         + " catre adresa " + socketTCP.getInetAddress() 

                         + " pe portul " + socketTCP.getPort() + "\n");   

   }

}

 

In laborator:

1. Lansati mediul BlueJ. Inchideti proiectele anterioare (Ctrl+W). Creati un nou proiect socket

      (Project->New Project…, selectati D:/,   SwTc2007,   numarul grupei, si scrieti socket).

2. Creati o noua clasa, numita InfoSocketTCP, folosind codul dat mai sus. Compilati codul.

3. Right-click pe clasa, selectati si executati main().

4. Folositi adresa localhost si numarul de port 3000. Urmariti efectul in Terminal Window.

5. Folositi apoi adresa www.google.com si numarul de port 7. Folositi si alte adrese.

 

Observatie: Cat timp bara de stare a executiei este activa () codul nu poate fi recompilat, nu poate fi inchisa fereastra Terminal Window, etc.

Pentru a opri executia, folositi right click pe  si selectati Reset Machine (sau folositi direct Ctrl+Shift+Tab).

 

2.4.4. Utilizarea clasei ServerSocket

 

Secventa tipica pentru crearea socket-ului server al unei aplicatii acceptor (server):

1

2

3

4

5

    // Stabilirea portului serverului

    int portServer = 2000;

 

    // Crearea socketului server (care accepta conexiunile)

    ServerSocket serverTCP = new ServerSocket(portServer);

 

Secventa tipica pentru crearea socket-ului pentru tratarea conexiunii TCP cu un client:

1

2

3

4

    // Blocare in asteptarea cererii de conexiune - in momentul acceptarii

    // cererii se creaza socketul care serveste conexiunea

 

    Socket conexiuneTCP = serverTCP.accept();

 

Un caz special este acela in care se creaza un socket server fara a se preciza portul pe care asculta serverul. In acest caz, este alocat un numar de port aleator (unul dintre cele neocupate in acel moment). Acest lucru se realizeaza prin pasarea valorii 0 constructorului ServerSocket().

 

Program pentru crearea unui socket server cu numar de port alocat aleator.

1

2

3

4

5

6

7

8

9

10

11

import java.net.*;

import java.io.*;

public class InfoPortTCPAleator {

public static void main (String args[]) {

    try { ServerSocket serverTCP = new ServerSocket(0);

          System.out.println("\nServer pe portul " + serverTCP.getLocalPort());

          serverTCP.close();

    }

    catch (IOException ex) {   System.err.println(ex);  }

  }

}

 

2.5. Programe de lucru cu socket-uri – clienti si servere

2.5.1. Server ecou TCP care poate trata doar un client (single threaded)

 

Secventa tipica pentru crearea fluxurilor de octeti asociate socket-ului:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

    // Obtinerea fluxului de intrare octeti TCP

    InputStream inTCP = socketTCPClient.getInputStream();

 

    // Obtinerea fluxului de intrare caractere dinspre retea

    InputStreamReader inTCPCaractere = new InputStreamReader(inTCP);

    // Adaugarea facilitatilor de stocare temporara

    BufferedReader inRetea = new BufferedReader(inTCPCaractere);

 

    // Obtinerea fluxului de iesire octeti TCP

    OutputStream outTCP = socketTCPClient.getOutputStream();

 

    // Obtinerea fluxului de iesire spre retea,

    // cu facilitate de afisare (similare consolei standard de iesire)

    PrintStream outRetea  = new PrintStream(outTCP);

Secventa tipica pentru trimiterea de date:

 

1

2

3

4

5

6

7

8

    // Crearea unui mesaj

    String mesajDeTrimis = "Continut mesaj";

 

    // Scrierea catre retea (trimiterea mesajului)

    outRetea.println(mesajDeTrimis);

 

    // Fortarea trimiterii

    outRetea.flush();                 

 

Secventa tipica pentru primirea de date:

 

1

2

3

4

5

    // Citirea dinspre retea (receptia unui mesaj)

    String mesajPrimit = inRetea.readLine();

       

    // Afisarea mesajului primit

    System.out.println(mesajPrimit);

 

Program server ecou TCP care raspunde mai multor mesaje trimise succesiv, mesajul format dintr-un punct (".") semnaland serverului terminarea mesajelor de trimis (clientul urmand sa isi termine executia):

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

import java.net.*;

import java.io.*;

import javax.swing.JOptionPane; 

public class ServerEcouFluxRepetiv {          // Server ecou flux

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

 

    int portServer = Integer.parseInt(JOptionPane.showInputDialog(

                              "Introduceti numarul de port dorit: "));

 

    // Crearea socketului server (care accepta conexiunile)

    ServerSocket serverTCP = new ServerSocket(portServer);

    System.out.println("Server in asteptare pe portul "+portServer+"...");

 

    // Blocare in asteptarea cererii de conexiune - in momentul acceptarii

    // cererii se creaza socketul care serveste conexiunea

    Socket conexiuneTCP = serverTCP.accept();

    System.out.println("Conexiune TCP pe portul " + portServer + "...");

 

    // Crearea fluxurilor de caractere conectate la fluxurile de octeti

    // obtinute de la socketul TCP

    PrintStream outRetea = new PrintStream(conexiuneTCP.getOutputStream());

    BufferedReader inRetea = new BufferedReader(

                   new InputStreamReader(conexiuneTCP.getInputStream()));

 

    while (true) {

      // Citirea unei linii din fluxul de intrare TCP

      String mesajPrimit = inRetea.readLine();

       

      // Afisarea liniei citite la consola de iesire

      System.out.println("Mesaj primit: " + mesajPrimit);

 

      // Scrierea liniei in fluxul de iesire TCP, cu fortarea trimiterii

      outRetea.println(mesajPrimit);

      outRetea.flush();

  

      // Testarea conditiei de oprire a servirii

      if (mesajPrimit.equals(".")) break;

    }

 

    // Inchiderea socketului (si implicit a fluxurilor)

    conexiuneTCP.close();

    System.out.println("Bye!");

  }

}

 

In laborator:

1. In acelasi proiect (socket) creati o clasa numita ServerEcouFluxRepetiv folosind codul dat.

2. Compilati codul, apoi right-click pe clasa, selectati si executati main().

3. Folositi numarul de port 4000. Urmariti efectul in Terminal Window.

 

Nu uitati: Pentru a opri executia, right click pe  si Reset Machine (sau Ctrl+Shift+Tab).

 

2.5.2. Client ecou TCP

 

Cu ajutorul unui client potrivit putem testa serverul ecou TCP.

Program client pentru serverul ecou TCP anterior (care raspunde mai multor mesaje trimise succesiv):

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

import java.net.*;

import java.io.*;

import javax.swing.JOptionPane; 

 

public class ClientEcouFluxRepetitiv {    // Client pentru server ecou flux

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

    String adresaServer = JOptionPane.showInputDialog(

                              "Introduceti adresa IP a serverului: ");

    int portServer = Integer.parseInt(JOptionPane.showInputDialog(

                              "Introduceti numarul de port al serverului: "));

 

    Socket conexiuneTCP = new Socket(adresaServer, portServer); // Creare socket

    System.out.println("Conexiune TCP cu serverul " + adresaServer +

                       ":" + portServer + "...");

    System.out.println("Pentru oprire introduceti '.' si <Enter>");

 

    PrintStream outRetea = new

                 PrintStream(conexiuneTCP.getOutputStream());  // Creare fluxuri

    BufferedReader inRetea = new BufferedReader(

                 new InputStreamReader(conexiuneTCP.getInputStream()));

 

    while (true) {               // Cat timp conditia de oprire nu este indeplinita

      String mesajTrimis = JOptionPane.showInputDialog("Se trimite: ");  // Mesaj

 

      outRetea.println(mesajTrimis);           // Scrierea in fluxul de iesire TCP

      outRetea.flush();

 

      String mesajPrimit = inRetea.readLine(); // Citirea din fluxul de intrare TCP

       

      JOptionPane.showMessageDialog(null, "S-a primit: " + mesajPrimit); // Afisare

      System.out.println("S-a primit: " + mesajPrimit);    // Afisarea la consola

 

      if (mesajPrimit.equals(".")) break;     // Testarea conditiei de oprire

    }

    conexiuneTCP.close();    // Inchiderea socketului (si implicit a fluxurilor)

    System.out.println("Bye!");

  }

}

 

 

In laborator:

1. In proiectul socket creati o clasa ClientEcouFluxRepetitiv cu codul dat. Compilati codul.

2. Right-click pe clasa, executati main(). Folositi adresa localhost si portul 4000. Ce observati?

 

In laborator:

1. La unul dintre calculatoare right-click pe clasa ServerEcouFluxRepetiv.

2. Selectati si executati main(). Folositi numarul de port 4000.

 

 

3. La un alt calculator right-click pe clasa ClientEcouFluxRepetitiv, selectati si executati main().

4. Folositi adresa primului calculator (cel pe care se executa ServerEcouFluxRepetiv)

         si numarul de port 4000. Urmariti efectul in Terminal Window pe cele doua calculatoare.

 

In laborator (codul complet este parte din tema de casa!):               

1. Pornind de la codul ServerEcouFluxRepetiv sa se creeze o clasa ServerRepetiv care sa afiseze mesajele primite de la client si sa astepte un utilizator uman sa furnizeze mesajul de raspuns catre client. Sa se compileze si testeze codul folosind un calculator pentru server si unul pentru client.

 

2.5.3. Server ecou TCP care trateaza mai multi clienti succesiv (single threaded)

 

Serverul ecou TCP poate fi transformat astfel incat sa poata trata mai multi clienti (de exemplu, ClientEcouFluxRepetitiv) succesiv.

 

 

Program server ecou TCP care poate trata mai multi clienti (ClientEcouFluxRepetitiv) succesiv.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

import java.net.*;

import java.io.*;

import javax.swing.JOptionPane; 

 

public class ServerEcouFluxRepetivSecvential {

 

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

 

    int portServer = Integer.parseInt(JOptionPane.showInputDialog(

                              "Introduceti numarul de port al serverului: "));

 

    // Crearea socketului server (care accepta conexiunile)

    ServerSocket serverTCP = new ServerSocket(portServer);

 

    // Servirea mai multor clienti succesivi (in mod secvential)

    while (true) {

      System.out.println("Server in asteptare pe port "+portServer+"...");

 

      // Blocare in asteptarea cererii de conexiune - in momentul

      // acceptarii cererii se creaza socketul care serveste conexiunea

      Socket conexiuneTCP = serverTCP.accept();

      System.out.println("Conexiune TCP pe portul " + portServer + "...");

 

      // Crearea fluxurilor de caractere conectate la fluxurile de octeti

      // obtinute de la socketul TCP

      PrintStream outRetea = new

             PrintStream(conexiuneTCP.getOutputStream());

      BufferedReader inRetea = new BufferedReader(

             new InputStreamReader(conexiuneTCP.getInputStream()));

 

      // Servirea clientului curent

      while (true) {

        // Citirea unei linii din fluxul de intrare TCP

        String mesajPrimit = inRetea.readLine();

       

        // Afisarea liniei citite la consola de iesire

        System.out.println("Mesaj primit: " + mesajPrimit);

 

        // Scrierea liniei in fluxul de iesire TCP, cu fortarea trimiterii

        outRetea.println(mesajPrimit);

        outRetea.flush();

  

        // Testarea conditiei de oprire a servirii

        if (mesajPrimit.equals(".")) break;

      }

 

      // Inchiderea socketului (si implicit a fluxurilor)

      conexiuneTCP.close();

      System.out.println("Bye!");

    }

  }

}

 

 

In laborator:

1. In acelasi proiect (socket) creati o clasa ServerEcouFluxRepetivSecvential cu codul dat.

2. Compilati codul.

 

In laborator:

1. La unul dintre calculatoare right-click pe clasa ServerEcouFluxRepetivSecvential.

2. Selectati si executati main(). Folositi numarul de port 4000.

 

 

3. La un alt calculator right-click pe clasa ClientEcouFluxRepetitiv, selectati si executati main().

4. Folositi adresa primului calculator (cel pe care se executa ServerEcouFluxRepetivSecvential)

         si numarul de port 4000. Urmariti efectul in Terminal Window pe cele doua calculatoare.

 

 

5. Pastrand serverul deschis repetati operatia cu clienti de pe diverse calculatoare.

 

 

2.5.4. Server ecou TCP care trateaza mai multi clienti succesiv (single threaded)

 

Serverul ecou TCP poate fi transformat astfel incat sa poata trata mai multi clienti in acelasi timp (concurent).

 

 

In cazul serverelor care pot trata mai multi clienti in mod concurent (in paralel), codul necesar pentru stabilirea conexiunilor cu clientii ce urmeaza a fi tratati de obiecte fir de executie (a caror clasa este numita generic ClasaFiruluiDeTratare) este urmatorul:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

    // initializare portServer

    // Crearea socketului server (care accepta conexiunile)

    ServerSocket serverTCP = new ServerSocket(portServer);

 

    // Servirea mai multor clienti in paralel (in mod concurent)

    while (true) {

      // Blocare in asteptarea cererii de conexiune

      Socket conexiuneTCP = serverTCP.accept();

 

      // Crearea si lansarea firului de executie pentru tratarea noului client

      ClasaFiruluiDeTratare firExecutie = new ClasaFiruluiDeTratare(conexiuneTCP);

      firExecutie.start();

 

    } // Gata pentru a accepta urmatorul client

 

Codul necesar pentru comunicatia cu un client (care se va afla in clasa din care se vor crea obiecte fir de executie, numita generic ClasaFiruluiDeTratare) este urmatorul:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

    Socket socketTCP;                         // Atribut

    public ClasaFiruluiDeTratare(Socket s) {  // Constructor

      socketTCP = s;                          // Initializarea atributului

    }

    public void run() {                      // Implementarea firului de executie

      // Crearea fluxurilor conectate la fluxurile obtinute de la socketul TCP

      PrintStream out = new PrintStream(conexiuneTCP.getOutputStream());

      BufferedReader in = new BufferedReader(

                   new InputStreamReader(conexiuneTCP.getInputStream()));

      // Tratarea clientului curent

      while (true) {

        // Citiri din fluxul de intrare TCP cu in.readLine();

        // Scrieri in fluxul de iesire TCP cu out.println(); out.flush();

      } // Incheierea tratarii clientului

 

      // Inchiderea socketului

      conexiuneTCP.close();

    }

 

O varianta mai simpla poate fi utilizarea unei singure clase, care sa extinda clasa Thread, si:

- in metoda main() sa implementeze stabilirea conexiunilor cu clientii

- in metoda run() sa implementeze codul necesar pentru comunicatia cu un client.

 

Exemplu de server ecou care poate trata mesaje sosite de la mai multi clienti in paralel:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

import java.net.*;

import java.io.*;

import javax.swing.JOptionPane; 

 

public class ServerEcouFluxRepetivConcurent extends Thread {

  private Socket conexiuneTCP;

 

  public ServerEcouFluxRepetivConcurent(Socket socketTCP) {

    conexiuneTCP = socketTCP;

  }

 

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

    int portServer = Integer.parseInt(JOptionPane.showInputDialog(

                              "Introduceti numarul de port al serverului: "));

 

    // Crearea socketului server (care accepta conexiunile)

    ServerSocket serverTCP = new ServerSocket(portServer);

 

    System.out.println("Server in asteptare pe portul "+portServer+"...");

 

    // Servirea mai multor clienti in acelasi timp (in mod concurent)

    while (true) {

      // Blocare in asteptarea cererii de conexiune - in momentul

      // acceptarii cererii se creaza socketul care serveste conexiunea

      Socket socketTCP = serverTCP.accept();

      System.out.println("Conexiune TCP pe portul " + portServer + "...");

 

      new ServerEcouFluxRepetivConcurent(socketTCP).start(); // run()

    }

  }

 

  public void run() {  // Fir de servire client

 

    try {

      // Crearea fluxurilor de caractere conectate la fluxurile de octeti

      // obtinute de la socketul TCP

      PrintStream outRetea = new

            PrintStream(conexiuneTCP.getOutputStream());

      BufferedReader inRetea = new BufferedReader(

            new InputStreamReader(conexiuneTCP.getInputStream()));

 

      // Servirea clientului curent

      while (true) {

        // Citirea unei linii din fluxul de intrare TCP

        String mesajPrimit = inRetea.readLine();

       

        // Afisarea liniei citite la consola de iesire

        System.out.println("Mesaj primit: " + mesajPrimit);

 

        // Scrierea liniei in fluxul de iesire TCP, cu fortarea trimiterii

        outRetea.println(mesajPrimit);

        outRetea.flush();

  

        // Testarea conditiei de oprire a servirii

        if (mesajPrimit.equals(".")) break;

      }

 

      // Inchiderea socketului (si implicit a fluxurilor)

      conexiuneTCP.close();

      System.out.println("Bye!");

    }

    catch (IOException ex) {

      System.err.println(ex);

    }

  }

}

 

 

In laborator:

1. In acelasi proiect (socket) creati o clasa ServerEcouFluxRepetivConcurent cu codul dat.

2. Compilati codul.

 

 

In laborator:

1. La unul dintre calculatoare right-click pe clasa ServerEcouFluxRepetivConcurent.

2. Selectati si executati main(). Folositi numarul de port 5000.

 

3. La un alt calculator right-click pe clasa ClientEcouFluxRepetitiv, selectati si executati main().

4. Folositi adresa primului calculator (cel pe care se executa ServerEcouFluxRepetivConcurent)

         si numarul de port 5000. Urmariti efectul in Terminal Window pe cele doua calculatoare.

 

5. Pastrand serverul deschis lansati si utilizati si alti clienti de pe diverse calculatoare.

 

 

 

2.6. Teme pentru acasa

 

 

I. Codul final al clasei ServerRepetiv (cerut in paragraful 2.5.2).

 

II. O varianta  a clasei ServerRepetiv, numita ServerRepetivClientiSuccesivi, care permite tratarea mai multor clienti unul dupa altul.

 

Codurile vor fi predate sub forma de listing sau scrise de mana!