Diferență între revizuiri ale paginii „POO Lab Lucrarea 5”

De la WikiLabs
Jump to navigationJump to search
 
(Nu s-au afișat 19 versiuni intermediare efectuate de un alt utilizator)
Linia 16: Linia 16:
 
== Cerințe ==
 
== Cerințe ==
  
* Scrieţi o clasă '''ServerPeer''', de tip fir de execuţie, care să administreze o conexiune cu un client. Aceast thread trebuie:
+
# Modificați clasa <code style="color: green">seriaf.poo.client.ClientPeer</code> scrisă la tema anterioară, transformând-o într-un fir de execuție care în paralel cu firul principal de execuție, să citească obiecte de tip <code style="color: green">seriaf.poo.structs.Message</code> dinspre server și să le afișeze pe ecran. Acest thread, odată instanțiat, trebuie pornit de către clasa <code style="color: green">seriaf.poo.client.TextClient</code>.
** poată deserializa obiectele de tip '''Message''' trimise de către clientul asociat și să le trimită către '''Server''';
+
# Modificați clasa <code style="color: green">seriaf.poo.server.ServerPeer</code> scrisă la tema anterioară, transformând-o într-un fir de execuție. Acest fir nou de execuție trebuie să citească obiectele de tip <code style="color: green">seriaf.poo.structs.Message</code> și <code style="color: green">seriaf.poo.structs.PrivateMessage</code> de la clientul asociat și să le distribuie corect la ceilalți clienți.
** conțină o metodă care să poată trimite obiecte de tip '''Message''' către client (dacă numele clientului este identic cu câmpul '''receiver''' din mesaj, sau acesta este '''null''');
+
# Adăugați clasei <code style="color: green">seriaf.poo.structs.Message</code> o metodă getter publică pentru expeditor, numită <code style="color: green">String getSender()</code>.
** stocheze numele clientului.
+
# Pentru a putea retransmite mesajele celorlalți clienți, clasa <code style="color: green">seriaf.poo.server.ServerPeer</code> trebuie aibă o metodă publică <code style="color: green">void sendMessage(Message)</code> care să serializeze obiectul primit ca argument către clientul conectat.
* Numele clientului se modifică doar când se primește de la client un obiect de tip '''SystemMessage''' unde câmpul '''type''' are valoarea '''TYPE_CHANGE_NAME'''. Dacă numele nu este deja utilizat de alt client, se face modificarea și se răspunde clientului cu un obiect de de tip '''SystemMessage''' unde câmpul '''type''' are valoarea '''TYPE_OK''', altfel, se răspunde cu un obiect de tip '''SystemMessage''' unde câmpul '''type''' are valoarea '''TYPE_ERROR'''.  
+
# Pentru a putea folosi clasa <code style="color: green">seriaf.poo.server.Server</code> să trimită mesajele clienților conectați, clasa <code style="color: green">seriaf.poo.server.ServerPeer</code> trebuie să aibă o referință la clasa <code style="color: green">seriaf.poo.server.Server</code>. Astfel, modificați constructorul clasei <code style="color: green">seriaf.poo.server.ServerPeer</code> astfel încât să aibă semnătura <code style="color: green">ServerPeer(Server, Socket)</code>.
* Modificați clasa '''Server''' astfel încât:
+
# Pentru a putea trimite corect mesajele private, clasa <code style="color: green">seriaf.poo.server.ServerPeer</code> trebuie să aibă o metodă publică numită <code style="color: green">String getUsername()</code> care întoarcă numele client-ului conectat la acel ServerPeer. Numele este stocat în clasă într-un câmp privat, care se actualizează la fiecare mesaj primit, extrăgând din mesaj numele expeditorului.
** să conțină referințe la obiecte de tip '''ServerPeer''', câte un obiect pentru fiecare client conectat;
+
# Modificați clasa <code style="color: green">seriaf.poo.server.Server</code> scrisă la tema anterioară, astfel încât la conectarea unui client, aceasta să creeze un nou <code style="color: green">seriaf.poo.server.ServerPeer</code> pe care să-l pornească ca Thread, apoi să revină în metoda <code style="color: green">ServerSocket.accept()</code>, așteptând o nouă conexiune.
** creeze câte un obiect nou de tip '''ServerPeer''' pentru fiecare client nou conectat și să le pornească pe fiecare ca fir execuție;
+
# Clasa <code style="color: green">seriaf.poo.server.Server</code> trebuie acum să fie instanțiabilă. Adăugați un constructor care să ia ca argument port-ul TCP pe care server-ul sa asculte și numărul maxim de clienți posibili. În acest constructor trebuie instanțiat obiectul de tip <code style="color: green">ServerSocket</code>.
** să centralizeze toate obiectele de tip '''Message''' de la toți '''ClientPeer''', și să le paseze mai departe la aceștia;
+
# Bucla cu apelul metodei '''accept''' din <code style="color: green">seriaf.poo.server.Server</code> trebuie acum să fie mutată într-o metodă separată a clasei <code style="color: green">seriaf.poo.server.Server</code> numită <code style="color: green">void listen()</code>.
** să elimine automat din lista de clienți pe cei care s-au deconectat;
+
# În metoda '''main''' din <code style="color: green">seriaf.poo.server.Server</code> trebuie să rămână o instanțiere de obiect <code style="color: green">seriaf.poo.server.config.ServerConfig</code>, o instanțiere de obiect <code style="color: green">seriaf.poo.server.Server</code> și un apel de metodă '''listen()''', cu blocurile '''try-catch''' necesare.
Hint: Folosiți-vă de clasa [http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html java.util.ArrayList].
+
# Clasa <code style="color: green">seriaf.poo.server.Server</code> trebuie mai conțină două metode, <code style="color: green">void dispatch(Message)</code> și <code style="color: green">void removeClient(ServerPeer)</code>. Prima este apelată de către obiectele de tip <code style="color: green">seriaf.poo.server.ServerPeer</code> cu mesajele primite de la clienți, și are rolul de a le trimite mai departe, către ceilalți clienți. A doua metodă este apelată când un client de deconectează (voit sau datorită unei erori), pentru a fi elimiat din listă.
* Modificați clasa '''Client''' astfel încât, pe lângă funcționalitatea anterioară, deserializeze în continuu obiecte de tip '''Message''' de pe stream și să le afișeze în consolă. În plus, la fiecare conectare, se trimite server-ului un obiect de tip '''SystemMessage''' unde câmpul '''sender''' trebuie să conțină numele clientului. Dacă răspunsul este '''TYPE_ERROR''', programul se încheie.
+
# Server-ul nu trebuie accepte o conexiune dacă numărul de clienți conectați este deja egal cu proprietatea MAX_CLIENTS citită din fișierul ''server.conf'' de către <code style="color: green">seriaf.poo.server.config.ServerConfig</code>.
 +
# Atenție care metode trebuie să fie sincronizate (pentru a trece testele, nu folosiți blocuri <code>synchronized</code>, ci declarați toată metoda sincronizată).
 +
# Administrați excepțiile astfel încât dacă apare o eroare de conexiune, server-ul să fie robust și să continue să servească corect ceilalți clienți conectați și să poate accepta un client nou în locul clientului pierdut.
 +
 
 +
Note:
 +
* Obiectele de tip <code style="color: green">seriaf.poo.structs.Message</code> se trimit tuturor clienților conectați. Obiectele de tip <code style="color: green">seriaf.poo.structs.PrivateMessage</code> se trimit doar către expeditor și către destinatar.
 +
* Clasa '''Server''' ar trebui să păstreze o listă cu toți clienții conectați (cu obiectele de tip '''ServerPeer'''). Pentru acest lucru puteți folosi de clasa [http://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html java.util.ArrayList].
 +
* Această listă trebuie fie menținută în sensul că noii clienți trebuie adăugați, iar clienții deconectați trebuie eliminați.
 +
* Pentru a verifica dacă un obiect e instanță a unei clase anume, puteți folosi operatorul '''instanceof''':
 +
<syntaxhighlight lang="java">
 +
Object o = ois.readOject();
 +
if(o instanceof String){
 +
    String s = (String)o;
 +
    System.out.print(s);
 +
}else{
 +
    System.out.print("The object is not a String!");
 +
}
 +
</syntaxhighlight>

Versiunea curentă din 17 decembrie 2015 14:39

Noțiuni și cunoștințe necesare

Cerințe

  1. Modificați clasa seriaf.poo.client.ClientPeer scrisă la tema anterioară, transformând-o într-un fir de execuție care în paralel cu firul principal de execuție, să citească obiecte de tip seriaf.poo.structs.Message dinspre server și să le afișeze pe ecran. Acest thread, odată instanțiat, trebuie pornit de către clasa seriaf.poo.client.TextClient.
  2. Modificați clasa seriaf.poo.server.ServerPeer scrisă la tema anterioară, transformând-o într-un fir de execuție. Acest fir nou de execuție trebuie să citească obiectele de tip seriaf.poo.structs.Message și seriaf.poo.structs.PrivateMessage de la clientul asociat și să le distribuie corect la ceilalți clienți.
  3. Adăugați clasei seriaf.poo.structs.Message o metodă getter publică pentru expeditor, numită String getSender().
  4. Pentru a putea retransmite mesajele celorlalți clienți, clasa seriaf.poo.server.ServerPeer trebuie să aibă o metodă publică void sendMessage(Message) care să serializeze obiectul primit ca argument către clientul conectat.
  5. Pentru a putea folosi clasa seriaf.poo.server.Server să trimită mesajele clienților conectați, clasa seriaf.poo.server.ServerPeer trebuie să aibă o referință la clasa seriaf.poo.server.Server. Astfel, modificați constructorul clasei seriaf.poo.server.ServerPeer astfel încât să aibă semnătura ServerPeer(Server, Socket).
  6. Pentru a putea trimite corect mesajele private, clasa seriaf.poo.server.ServerPeer trebuie să aibă o metodă publică numită String getUsername() care să întoarcă numele client-ului conectat la acel ServerPeer. Numele este stocat în clasă într-un câmp privat, care se actualizează la fiecare mesaj primit, extrăgând din mesaj numele expeditorului.
  7. Modificați clasa seriaf.poo.server.Server scrisă la tema anterioară, astfel încât la conectarea unui client, aceasta să creeze un nou seriaf.poo.server.ServerPeer pe care să-l pornească ca Thread, apoi să revină în metoda ServerSocket.accept(), așteptând o nouă conexiune.
  8. Clasa seriaf.poo.server.Server trebuie acum să fie instanțiabilă. Adăugați un constructor care să ia ca argument port-ul TCP pe care server-ul sa asculte și numărul maxim de clienți posibili. În acest constructor trebuie instanțiat obiectul de tip ServerSocket.
  9. Bucla cu apelul metodei accept din seriaf.poo.server.Server trebuie acum să fie mutată într-o metodă separată a clasei seriaf.poo.server.Server numită void listen().
  10. În metoda main din seriaf.poo.server.Server trebuie să rămână o instanțiere de obiect seriaf.poo.server.config.ServerConfig, o instanțiere de obiect seriaf.poo.server.Server și un apel de metodă listen(), cu blocurile try-catch necesare.
  11. Clasa seriaf.poo.server.Server trebuie să mai conțină două metode, void dispatch(Message) și void removeClient(ServerPeer). Prima este apelată de către obiectele de tip seriaf.poo.server.ServerPeer cu mesajele primite de la clienți, și are rolul de a le trimite mai departe, către ceilalți clienți. A doua metodă este apelată când un client de deconectează (voit sau datorită unei erori), pentru a fi elimiat din listă.
  12. Server-ul nu trebuie să accepte o conexiune dacă numărul de clienți conectați este deja egal cu proprietatea MAX_CLIENTS citită din fișierul server.conf de către seriaf.poo.server.config.ServerConfig.
  13. Atenție care metode trebuie să fie sincronizate (pentru a trece testele, nu folosiți blocuri synchronized, ci declarați toată metoda sincronizată).
  14. Administrați excepțiile astfel încât dacă apare o eroare de conexiune, server-ul să fie robust și să continue să servească corect ceilalți clienți conectați și să poate accepta un client nou în locul clientului pierdut.

Note:

  • Obiectele de tip seriaf.poo.structs.Message se trimit tuturor clienților conectați. Obiectele de tip seriaf.poo.structs.PrivateMessage se trimit doar către expeditor și către destinatar.
  • Clasa Server ar trebui să păstreze o listă cu toți clienții conectați (cu obiectele de tip ServerPeer). Pentru acest lucru vă puteți folosi de clasa java.util.ArrayList.
  • Această listă trebuie să fie menținută în sensul că noii clienți trebuie adăugați, iar clienții deconectați trebuie eliminați.
  • Pentru a verifica dacă un obiect e instanță a unei clase anume, puteți folosi operatorul instanceof:
Object o = ois.readOject();
if(o instanceof String){
    String s = (String)o;
    System.out.print(s);
}else{
    System.out.print("The object is not a String!");
}