Cws. Instrumente Necesare Realizarii Interfetei pe Partea de Client

1.Privire de ansamblu asupra aplicatiei

CWS face posibil ca mai multii utilizatori sa interactioneze prin intermediul internetului. Acestia pot face schimburi de mesaje de tip text si un numar finit de imagini.

Aplicatiea CWS are un mare ajantaj, este implementata in JAVA, ceia ce, face posibil ca aceasta sa ruleze pe mai multe platforme: Windows, Solaris, Linux, MacOS. Totodata are si un dezavantaj: ruleaza mai incet datorita acestei independente de platforma..

Aplicatia CWS este formata din 2 parti: server si client. Server-ul va rula pe o masina la distanta pe cand partea de client va rula pe masina utilizatorului. CWS, partea de client, are o interfata simpla usor de folosit, chiar si pentru un utilizator care are cunostinte minime in lucrul cu calculatorul, partea de server nu are nici un tip de interfata, acesta lucrand in background pe masina de la distanta.

Pentru a putea rula, aplicatia CWS, are nevoie de o conexiune la Internet s-au poate fi instalata pe o retea locala de tip LAN si nu necesita o latime de banda mare.

Fig 1. Client

Fig. 2 Server

2. Modul de utilizare

2.1. Modul in care se face instalarea server-lui si lansarea in executie

Trebuie instalat pe o masina care sa aiba o legatura la o retea locala (clientii vor putea fi numai din cadru retelei locale) s-au Internet (clientii pot fi din orice tip de retea). Masina pe care ruleaza server-ul aplicatiei CWS necesita ca portul 1979 sa fie liber, in caz contrar aplicatia nu va functiona.

Lansarea server-ul se face prin comanda: java Server.

Daca fereastra de mai sus exista, in urma executarii comenzii java Server, inseamna ca server-ul este activ si clientii pot sa se conecteze la el. Acesta fereastra apare in Windows.

2.2 Modul de utilizarea al clientului.

Ca un anumit client sa se poata conecta la server si intra in contact cu alti clientii trebuie sa urmeze o serie de pasii:

conectare

setarea nickname-ului, nick-ul va trebuia sa fie unic

logare

join la un anumit canal

alegerea unei imagini care sa insoteasca mesajul transmis

schimbul propriu-zis de mesaje

mesajele pot fi trimise unui anumit grup de utilizatori (unui anumit canal)

mesajele pot fi trimise unui anumit client care face parte dintr-un anumit grup de clienti

aceste mesaje vor fi insotite de o imagine care va putea fi setata de fiecare client in parte.

Detalii

2.2.1.Conectare

Dublu click pe butonul din figura de mai sus va duce la conectare cu server-ul, daca server-ul acepta sau nu conexiunea clientul este anuntat print-un mesaj.

2.2.2. Setarea Nickname-ului

In textfield –ul din figura de mai sus se introduce nickname-ul cu care un client va putea fi identificat pe chat.

2.2.3. Logare

Daca buton-ul de sus este apasat clientul trasmite un mesaj server-ului prin care il anunta ca vrea sa se conecteze, server-ul primeste deasmenea si numele de logare, server-ul verifica unicitatea nickname-ul si trasmite inpoi la client daca logarea a avut loc cu succes sau nu.

2.2.4. Join la un canal

Daca butonul de mai sus este apasat atunci clientul trimite un mesaj la server prin care ii spune ca doreste sa se alature unui anumit grup de clienti. Daca join-ul a avut loc cu succes atunci server-ul trimite lista de clienti de pe canal la care s-a alaturat clientul nostru.

Daca clientul doreste sa isi faca propriul lui canal atunci poate da comanda /join numeCanal. In urma acestei comenzi clientul anunta server-ul sa faca canalul cu numele de numeCanal.

Aceasta comanda este data din textfied-ul din imaginea de mai jos.

2.2.5. Alegerea unei imagini

Mesaje care vor fi schimbate intre clienti vor putea fi insotite de o imagine, aceste imagini vor putea fi selectate din tab-ul Setings ca in figura de mai sus, efectuindu-se un click pe imaginea dorita.

2.2.6. Schimbul de mesaje

Mesaje pot fi trimise unui anumit canal sau unui client anume de pe aflat pe un anumit canal. Pentru a afla lista de canale se foloseste comanda /lcl, care duce la obtinerea, la client, a listei de canale disponibile pe server.

Alegerea unui canal se face din combo-box-ul de mai jos:

Daca am ales un canal putem trimite mesaj pe tot canalul, selectand din lista clientilor item-ul All sau unui anumit client de pe acel canal facand o selectie din lista de clienti, ca in figura de mai jos.

Se poate observa, din figura de mai sus, ca s-a facut o selectie aupra clientului Stefan.

2.2.7. Mesaje insotite de o imagine.

Mesaje trimise intre clienti vor putea fi inotite de o imagine, care poate reprezenta starea unui anumit client.

3. Instrumente necesare realizarii interfetei (GUI) pe partea de client,modul de realizare

Aplicatie CWS este implementa in Java un limbaj de programare care contine o serie de clase prin care se poate crea o interfata cu utilizatorul corespunzatoare standardelor actuale. Clasele raspunzatoare de GUI fac parte de packet-ul Swing. In Java mai exista un packet de clase, numit AWT dar care nu au aceiasi putere ca Swing-ul. Swing-ul, parte componenta a JDK incepand cu versiunea 1.2, poate crea urmatoare tipuri de controale:

Dialog

Frame

Scroll Panel

Split Panel

Tool Bar

Internal Frame

Tabbed Pane

Buttons

Combo Box

Slider

TextFields

Label

Progress Bar

Tool Tip

Color chooser

File Chooser

Table

Text

Tree

Interfata grafica sau, mai bine zis, interfata grafica cu utilizatorul (GUI), este un termen cu înteles larg care se refera la toate tipurile de comunicare vizuala între un program si utilizatorii sai. Aceasta este o particularizare a interfetei cu utilizatorul (UI), prin care vom întelege conceptul generic de interactiune între un program si utilizatorii sai. Asadar, UI se refera nu numai la ceea ce utilizatorul vede pe ecran ci la toate mecanismele de comunicare între acesta si program.
Limbajul Java pune la dispozitie numeroase clase pentru implementarea diverselor functionalitati UI, însa ne vom ocupa în continuare de acelea care permit realizarea unei intefete grafice cu utilizatorul (GUI).

Cu Java putem crea aplicatii independenta sau miniaplicatii care ruleaza in interiorul paginilor Web, sa vedem in continuare cum se poate crea o astfel de aplicatie. Setul Swing introduce versiunea sa de applet, al carei parent este appletul AWT. Appleturile Swing sunt facute mai sofisticat asa incat suporta bare de meniu si layering de componente in dimensiunea adincimii, si permite pictarea deasupra pe componentele deja introduse in applet.

Cu suport pentru bare de meniu, appleturle Swing pot avea o bara de meniu. Layering-ul componentelor este in esenta plasarea de componente in layere multiple, unele peste altele. Asta permite componentelor sa fie pozitionate pentru a crea senzatia de adincime. Appleturile Swing contin o componenta speciala numita "layered pane", speciala pentru a obtine acest efect.

Alta facilitate care permite pictarea peste componente in interiorul appletului poate fi realizata folosind o componenta numita "glass pane" (panel de sticla). Este o componenta transparenta care permite componetelor din fundal sa fie vizibile. Dar normal ca se poate picta peste acest panel ca sa faci vizibil orice element din cadru.

Appleturile Swing sunt reprezentate de clasa JApplet, care este stocata in pachetul javax.swing. Se poate crea un applet Swing prin crearea unei clase care extinde JApplet. In timp ce adaugi membrii clasei, trebuie sa ii adaugi de fapt unui panel numit panel de continut, care primeste practic membrii appletului. Biblioteca Swing introduce un container intermediar care lucreaza cu complexitatea impusa de combinarea de componente in applet. Urmatoarea sursa de cod arata cum o componenta poate fi adaugata intr-un panel de continut intr-un applet Swing :

// Obtine un handle la panelul de continut al appletului 
Container contentPane = this.getContentPane( );

// Poate vrei sa atribui un nou layout
contentPane.setLayout(someLayoutObject);

// Adauga componenta la applet
contentPane.add(someComponent);

Se pot obtine de asemenea referinte la panelul layered sau glass, si la bara de meniu prin apelarea urmatoarelor metode asupra unei insatnte a appletului:

public void getLayeredPane( )
public void getGlassPane( )
public void getJMenuBar( )

Program exemplu cu JApplet

TJApplet.java, prezinta un applet care afiseaza o eticheta simpla cu margine. 

/*
* <Applet code=TJApplet width=200 height=100>
* </Applet>;
*/

import javax.swing.*;
import java.awt.*;

public class TJApplet extends JApplet {

public void init( ) {
// 1. Obtine un handle la panelul de continut al appletului
Container contentPane = getContentPane( );
// 2. Creaza o eticheta cu un icon si text
JLabel label = new JLabel("This is a Swing applet", // text
new ImageIcon("Metal.gif"), // icon
JLabel.CENTER); // pozitia orizontala

// 3. Atribuie o bordura prin folosirea unui icon si specificarea inseturilor
label.setBorder(BorderFactory.createMatteBorder(
10, // top inset
10, // left inset
10, // bottom inset
10, // right inset
new ImageIcon("border.gif"))); // icon border

// 4. Adauga eticheta la panel.
contentPane.add(label);
}
}

Detalii la cod

TJApplet extendinde clasa Swing JApplet si astfel se numeste applet Swing. In interiorul metodei init(), se primeste un handle la panelul de continut al appletului. Aceasta referinta se foloseste la adaugarea de componenente la applet. Apoi se creaza o eticheta Swing cu un text specificat si un icon. Apoi se atribuie o bordura si se specifica finetea bordurii. La urma se adauga eticheta la panel.

4. Instrumente necesare comunicatii prin retea (Sockets, Serializarea obiectelor), modul

Programarea în retea implica trimiterea de mesaje si date între aplicatii ce ruleaza pe calculatoare aflate într-o retea locala sau conectate la Internet. Pachetul care ofera suport pentru scrierea aplicatiilor de retea este java.net. Clasele din acest pachet ofera o modalitate facila de programare în retea, fara a fi nevoie de cunostine prealabile referitoare la comunicarea efectiva între calculatoare. Cu toate acestea sunt necesare câteva notiuni fundamentale referitoare la retele, cum ar fi protocol, adresa IP, port, socket.

4.1.Protocoale

Un protocol reprezinta o conventie de reprezentare a datelor folosita în comunicarea între doua calculatoare. Având în vedere faptul ca orice informatie care trebuie trimisa prin retea trebuie serializata astfel încât sa poata fi transmisa secvential, octet cu octet, catre destinatie, era nevoie de stabilirea unor conventii (protocoale) care sa fie folosite atât de calculatorul care trimite datele cât si de cel care le primeste.
Cele mai utilizate protocoale sunt TCP si UDP.

Definitii

TCP (Transport Control Protocol) este un protocol ce furnizeaza un flux sigur de date între doua calculatoare. Acest protocol asigura stabilirea unei conexiuni permanente între cele doua calculatoare pe parcursul comunicatiei.

UDP (User Datagram Protocol) este un protocol ce trimite pachete independente de date, numite datagrame, de la un calculator catre altul fara a garanta în vreun fel ajungerea acestora la destinatie. Acest protocol nu stabileste o conexiune permanta între cele doua calculatoare.

4.2.Cum este identificat un calculator în retea ?

Orice calculator gazda conectat la Internet este identificat în mod unic de adresa sa IP (IP este acronimul de la Internet Protocol). Aceasta reprezinta un numar reprezentat pe 32 de biti, uzual sub forma a 4 octeti, cum ar fi de exemplu: 193.226.26.231 si este numit adresa IP numerica. Corespunzatoare unei adrese numerice exista si o adresa IP simbolica, cum ar fi fenrir.infoiasi.ro.

De asemenea fiecare calculator aflat într-o retea locala are un nume unic ce poat fi folosit la identificarea locala a acestuia.

Clasa Java care reprezinta notiunea de adresa IP este InetAddress.

4.3.Ce este un port ?

Un calculator are în general o singura legatura fizica la retea. Orice informatie destinata unei anumite masini trebuie deci sa specifice obligatoriu adresa IP a acelei masini. Insa pe ude adresa sa IP (IP este acronimul de la Internet Protocol). Aceasta reprezinta un numar reprezentat pe 32 de biti, uzual sub forma a 4 octeti, cum ar fi de exemplu: 193.226.26.231 si este numit adresa IP numerica. Corespunzatoare unei adrese numerice exista si o adresa IP simbolica, cum ar fi fenrir.infoiasi.ro.

De asemenea fiecare calculator aflat într-o retea locala are un nume unic ce poat fi folosit la identificarea locala a acestuia.

Clasa Java care reprezinta notiunea de adresa IP este InetAddress.

4.3.Ce este un port ?

Un calculator are în general o singura legatura fizica la retea. Orice informatie destinata unei anumite masini trebuie deci sa specifice obligatoriu adresa IP a acelei masini. Insa pe un calculator pot exista concurent mai multe procese care au stabilite conexiuni în retea, asteptând diverse informatii. Prin urmare datele trimise catre o destinatie trebuie sa specifice pe lânga adresa IP a calculatorului si procesul catre care se îndreapta informatiile respective. Identificarea proceselor se realizeaza prin intermdiul porturilor.

Un port este un numar de 16 biti care identifica în mod unic procesle care ruleaza pe o anumita masina. Orice aplicatie care realizeaza o conexiune în retea va trebui sa ataseze un numar de port acelei conexiuni. Valorile pe care le poate lua un numar de port sunt cuprinse între 0 si 65535 (deoarece sunt numere reprezentate pe 16 biti), numerele cuprinse între 0 si 1023 fiind însa rezervate unor servicii sistem si, din acest motiv, nu trebuie folosite în aplicatii.

Serializarea obiectelor si socketii …

4.4.Ce este serializarea ?

Definitie

Serializarea este o metoda ce permite transformarea unui obiect într-o secventa de octeti din care sa poata fi refacut ulterior obiectul original. Cu alte cuvinte, serializarea permite salvarea într-o maniera unitara a datelor împreuna cu signatura unui obiect pe un mediu de stocare a informatiei extern programului. Procesul invers de citirea a unui obiect serializat pentru a-i reface starea originala se numeste deserializare. Intr-un cadru mai larg, prin serializare se întelege procesul de scriere/citire a obiectelor.

Utilitatea serializarii obiectelor consta in urmatoarele aspecte:

Compensarea diferentelor între sisteme de operare, adica putem crea un obiect pe o masina Windows, îl serializam, apoi îl trimitem prin retea catre o masina UNIX unde va fi corect reconstruit. In acest fel comunicarea între sisteme diferite se realizeaza unitar, independent de reprezentarea datelor, ordinea octetilor sau alte detalii specifice sistemelor repective.

Permite persistenta obiectelor, ceea ce înseamna ca durata de viata a unui obiect nu este determinata de executia unui program în care acesta este definit – obiectul poate exista si între apelurile programelor care îl folosesc. Acest lucru se realizeaza prin serializarea obiectului si scrierea lui pe disc înainte de terminarea unui program, apoi, la relansarea programului, obiectul va fi citit de pe disc si starea lui refacuta. Acest tip de persistenta a obiectelor se numeste persistenta usoara, întrucât ea trebuie efectuata explicit de catre programator si nu este realizeazata automat de catre sistem.

RMI (Remote Method Invocation) – comunicarea obiectelor prin socket-uri: este o modalitate prin care obiectele de pe o alta masina se comporta ca si când ar exista pe masina pe care ruleaza programul nostru. Atunci când este trimis un mesaj catre un obiect "remote" (de pe alta masina), serializarea este necesara pentru transportul argumentelor prin retea si pentru returnarea valorilor.

Java Beans – sunt componente grafice definite de utilizator si care pot fi folosite la fel ca si componentele grafice standard. Orice componenta Bean are o stare initiala a informatiilor sale, stare care este specificata la definirea sa. Atunci când ea este folosita într-un program aceasta stare trebuie încarcata de undeva, ceea ce înseamna ca aceste componente trebuie serializate si salvate pe disc.

In limbajul de programare Java putem salva starea anumitor obiecte prin serializare si deasemena putem restaura aceste obiecte prin procesul invers serilizarii sau putem trimite obiecte pe retea prin socketi. Serializarea unei clase se poate realiza foarte usor prin implementarea interfetei java.io.Serializable. Aceasta interfata, Serializable, nu are nici o metoda care trebuie implementata ci doar informeaza masina virtuala ca instantele acelei clase (obiectele) vor putea fi serializate.

Exista 2 clase principale care pot scrie si citii obiecte: ObjectInputStream si ObjectOutputStream. Clasa ObjectOutputStream are o metoda writeObject(obj) care scrie un obiect intr-un flux de date, clasa ObjectInputStream are o metoda readObject() care citeste un obiect dintr-un flux de date.

Doua exemple de scriere respectiv citire a unui obiecte:

COD1: (salvare)

import java.io.*;

import java.util.Date;

public class SaveDate

{

public static void main(String argv[]) throws Exception

{

FileOutputStream fos = new FileOutputStream("date.out");

ObjectOutputStream oos = new ObjectOutputStream(fos);

Date date = new Date();

oos.writeObject(date);

oos.flush();

oos.close();

fos.close();

}

}

Am creat un obiect de tip Date si l-am salvat in fluxul de iesire fos care este un fisier de pe HDD calculatorului.

COD 2: (restaurare)

import java.io.*;

import java.util.Date;

public class ReadDate

{

public static void main(String argv[]) throws Exception

{

FileInputStream fis = new FileInputStream("date.out");

ObjectInputStream ois = new ObjectInputStream(fis);

Date date =(Date)ois.readObject();

System.out.println("The date is:"+date);

ois.close();

fis.close();

}

}

In exemplu de sus am restaurat obiectul serializat in COD 1. In exemple de mai sus am lucrat cu instante ale clasei Date care este o clasa serilizabila. Exista o unealta care ne permite sa vedem daca o clasa este sau nu serializabila. Aceasta unealta o putem utiliza in urmatorul mod, la promterul sistemului de operare tastam comanda:

> serialver java.util.Date

Rezultat : java.util.Date: static final long serialVersionUID = 7523967970034938905L;

Unealta serialver vine cu JDK, mai sus am testat daca clasa Date este serializabila. Clasa Date este o clasa serializabila dar de retinut ca nu toate clasele din Java sunt serializabile exemplu clasa Socket nu este o clasa serializabila. Hai sa vedem daca putem scrie clase custom serializabile.

Avem urmatoare clasa:

COD 3

import java.io.*;

import java.util.*;

public class UserInfo implements Serializable

{

String name = null;

public UserInfo(String name)

{

this.name = name;

}

public void printInfo()

{

System.out.println("The name is: "+name);

}

}

Scriem o clasa care face 2 obiecte de tip UserInfo si le pune intr-un flux de date care se numeste name.out. De retinut este ca putem apela metoda writeObject de cate ori dorim.

COD 4

import java.io.*;

import java.util.Date;

public class SaveInfo

{

public static void main(String argv[]) throws Exception

{

FileOutputStream fos = new FileOutputStream("name.out"); ObjectOutputStream oos = new ObjectOutputStream(fos);

// create two objects

UserInfo user1 = new UserInfo("Java Duke");

UserInfo user2 = new UserInfo("Java Blue");

// write the objects to the output stream

oos.writeObject(user1);

oos.writeObject(user2);

oos.flush();

oos.close();

fos.close();

}

}

In final cream o clasa care restaureaza obiectele serializate in fluxul name.out. De retinut : putem folosii metoda readObject() de cate ori dorim.

COD 5

import java.io.*;

import java.util.Date;

public class ReadInfo

{

public static void main(String argv[]) throws Exception

{

FileInputStream fis = new FileInputStream("name.out");

ObjectInputStream ois = new ObjectInputStream(fis);

// read the objects from the input stream (the file name.out)

UserInfo user1 = (UserInfo) ois.readObject();

UserInfo user2 = (UserInfo) ois.readObject();

// invoke a method on the constructed object

user1.printInfo();

user2.printInfo();

ois.close();

fis.close();

}

}

Daca compilam si rula aceste fisiere sursa obtinem ca rezultat:

The name is: Java Duke

The name is: Java Blue

4.5.Controlul serializarii

Exista cazuri cand dorim ca unele variabile membre sau sub-obiecte ale unui obiect sa nu fie salvate automat în procesul de serializare. Acestea sunt cazuri comune atunci cand respectivele campuri reprezinta informatii confidentiale, cum ar fi parole, sau variabile auxiliare pe care nu are rost sa le salvam. Chiar declarate ca private în cadrul clasei aceste campuri participa la serializare. O modalitate de a controla serializare este implementarea interfetei Externalizable, asa cum am vazut anterior. Aceasta metoda este însa incomoda atunci când clasele sunt greu de serializat iar multimea campurilor care nu trebuie salvate este redusa.
Pentru ca un camp sa nu fie salvat în procesul de serializare atunci el trebuie declarat cu modificatorul transient si trebuie sa fie ne-static. De exemplu, declararea unei parole ar trebui facuta astfel:

transient private String parola; //ignorat la serializare

Atentie : Modificatorul static anuleaza efectul modificatorului transient;

static transient private String parola; //participa la serializare

4.6.Transportul obiectelor prin socketi

Definitie

Un socket (soclu) este o abstractie software folosita pentru a reprezenta fiecare din cele doua "capete" ale unei conexiuni între doua procese ce ruleaza într-o retea. Fiecare socket este atasat unui port astfel încât sa poata identifica unic programul caruia îi sunt destinate datele.

Socket-urile sunt de doua tipuri:

TCP, implementate de clasele Socket si ServerSocket

UDP, implementate de clasa DatagramSocket

O aplicatie de retea ce foloseste socket-uri se încadreaza în modelul client/server de concepere a unei aplicatii. In acest model aplicatia este formata din doua categorii distincte de programe numite servere, respectiv clienti.

Programele de tip server sunt cele care ofera diverse servicii eventualilor clienti, fiind în stare de asteptare atâta vreme cât nici un client nu le solicita serviciile.

Programele de tip client sunt cele care initiaza conversatia cu un server, solicitând un anumit serviciu. Uzual, un server trebuie sa fie capabil sa trateze mai multi clienti simultan si, din acest motiv, fiecare cerere adresata serverului va fi tratata într-un fir de executie separat.

Acum cand stim ce este un socket si cum putem scrie/citii obiecte din fluxuri de date hai sa vedem daca putem transporta obiecte prin socketi. Prima data vom incerca sa trasportam o instanta a clasei Date si apoi vom implementa o clasa custom si vom incerca sa trasportam prin socketi o instanta a acestei clase create de noi.

Dezvoltam o aplicatie client/server, unde socketii sunt de tip TCP. Cum va rula aplicatia de mai jos: un client se conecteaza la server, odata stabilita aceasta conexiune server-ul ii trimite clientului o instanta a clasei Date. Server-ul va putea accepta conexiuni multiple fiind capabil sa trateze mai multi clienti simultan.

COD 6 ()

import java.io.*;

import java.net.*;

import java.util.*;

public class DateServer extends Thread {

private ServerSocket dateServer;

public static void main(String argv[]) throws Exception {

new DateServer();

}

public DateServer() throws Exception {

dateServer = new ServerSocket(3000);

System.out.println("Server listening on port 3000.");

this.start();

}

public void run() {

while(true) {

try {

System.out.println("Waiting for connections.");

Socket client = dateServer.accept();

System.out.println("Accepted a connection from: "+

client.getInetAddress());

Connect c = new Connect(client);

} catch(Exception e) {}

}

}

}

class Connect extends Thread {

private Socket client = null;

private ObjectInputStream ois = null;

private ObjectOutputStream oos = null;

public Connect() {}

public Connect(Socket clientSocket) {

client = clientSocket;

try {

ois = new ObjectInputStream(client.getInputStream());

oos = new ObjectOutputStream(client.getOutputStream());

} catch(Exception e1) {

try {

client.close();

}catch(Exception e) {

System.out.println(e.getMessage());

}

return;

}

this.start();

}

public void run() {

try {

oos.writeObject(new Date());

oos.flush();

// close streams and connections

ois.close();

oos.close();

client.close();

} catch(Exception e) {}

}

}

Clientul nostru nu are nimic de transmis ci doar primeste un obiecte de tip Date si apoi afiseaza data.

COD 7

import java.io.*;

import java.net.*;

import java.util.*;

public class DateClient {

public static void main(String argv[]) {

ObjectOutputStream oos = null;

ObjectInputStream ois = null;

Socket socket = null;

String adresaServer="127.0.0.1";

Date date = null;

try {

// open a socket connection

socket = new Socket(adresaServer, 3000);

// open I/O streams for objects

oos = new ObjectOutputStream(socket.getOutputStream());

ois = new ObjectInputStream(socket.getInputStream());

// read an object from the server

date = (Date) ois.readObject();

System.out.print("The date is: " + date);

oos.close();

ois.close();

} catch(Exception e) {

System.out.println(e.getMessage());

}

}

}

Pentru a putea rula aceasta aplicatie client/sever, trebuie sa modificati in client adresa server-ului, asta numai in cazul in care server-ul ruleaza pe o masina si clientul pe alta masina. Daca atat clientul cat si serverul ruleaza pe aceiasi masina atunci totul e OK.

In exemplul precedent, am lucrat cu obiecte existente. Ce se intampla daca vrei sa transporti instante ale unor clase scrise de noi. E procesul diferit ?

Primul lucru pe care il facem este sa implementam clasa ale care instante vor fi transportate prin socketi.

COD 8.

import java.io.*;

import java.util.*;

public class SerializedObject implements Serializable {

private int array[] = null;

public SerializedObject() {

}

public void setArray(int array[]) {

this.array = array;

}

public int[] getArray() {

return array;

}

}

Clasa din COD 8 trebuie sa fie serializabila de aceia trebuie sa implementeze neaparat interfata Serializable. Urmatorul pas este de a implementa clientul, clientul creaza doua instante de tip SeralizedObject si le scrie apoi la fluxul de iesire (la server) asa cum ve-ti vedea in codul sursa COD 9.

COD 9.

import java.io.*;

import java.net.*;

public class ArrayClient {

public static void main(String argv[]) {

ObjectOutputStream oos = null;

ObjectInputStream ois = null;

//folositi aceasta adresa doar in cazul in care atat server

//cat si clientul ruleaza pe aceiasi masina

String adresaServer="127.0.0.1";

// doi vectori

int dataset1[] = {3, 3, 3, 3, 3, 3, 3};

int dataset2[] = {5, 5, 5, 5, 5, 5, 5};

try {

// deschid o conexiune

Socket socket = new Socket(adresaServer, 4000);

// fac fluxurile

oos = new ObjectOutputStream(socket.getOutputStream());

ois = new ObjectInputStream(socket.getInputStream());

// 2 instante SerializedObject

SerializedObject so1 = new SerializedObject();

SerializedObject so2 = new SerializedObject();

SerializedObject result = null;

int outArray[] = new int[7];

so1.setArray(dataset1);

so2.setArray(dataset2);

// scriu obiectele la fluxul de iesire

oos.writeObject(so1);

oos.writeObject(so2);

oos.flush();

// citesc un obiect de la server

result = (SerializedObject) ois.readObject();

outArray = result.getArray();

System.out.print("The new array is: ");

for(int i=0;i<outArray.length;i++) {

System.out.print(outArray[i] + " ");

}

oos.close();

ois.close();

} catch(Exception e) {

System.out.println(e.getMessage());

}

}

}

Trebuie sa mai implementam si serverul ca aplicatia noastra sa mearga. Server-ul din COD 10 este similar cu cel de la COD 6 diferenta consta doar prin felul in care se proceseaza datele.

COD 10.

import java.io.*;

import java.net.*;

public class ArrayMultiplier extends Thread {

private ServerSocket arrayServer;

public static void main(String argv[]) throws Exception {

new ArrayMultiplier();

}

public ArrayMultiplier() throws Exception {

arrayServer = new ServerSocket(4000);

System.out.println("Server listening on port 4000.");

this.start();

}

public void run() {

while(true) {

try {

System.out.println("Waiting for connections.");

Socket client = arrayServer.accept();

System.out.println("Accepted a connection from: "+ client.getInetAddress());

Connect c = new Connect(client);

} catch(Exception e) {}

}

}

}

class Connect extends Thread {

private Socket client = null;

private ObjectInputStream ois = null;

private ObjectOutputStream oos = null;

public Connect() {}

public Connect(Socket clientSocket) {

client = clientSocket;

try {

ois = new ObjectInputStream(client.getInputStream());

oos = new ObjectOutputStream(client.getOutputStream());

} catch(Exception e1) {

try {

client.close();

}catch(Exception e) {

System.out.println(e.getMessage());

}

return;

}

this.start();

}

public void run() {

SerializedObject x = null;

SerializedObject y = null;

int dataset1[] = new int[7];

int dataset2[] = new int[7];

int result[] = new int[7];

try {

x = (SerializedObject) ois.readObject();

y = (SerializedObject) ois.readObject();

dataset1 = x.getArray();

dataset2 = y.getArray();

// create an array by multiplying two arrays

for(int i=0;i<dataset1.length;i++) {

result[i] = dataset1[i] * dataset2[i];

}

// ship the object to the client

SerializedObject output = new SerializedObject();

output.setArray(result);

oos.writeObject(output);

oos.flush();

// close connections

ois.close();

oos.close();

client.close();

} catch(Exception e) {}

}

}

Important: atat clientul cat si server-ul trebuie sa aiba o copie a clasei SerializedObject daca nu va exista acesta copie se arunca o exceptie. O solutie la aceasta problema este sa scriem o interfata care sa extinda Serializable, clasa SerializedObject va implementa aceasta clasa. Utilizand aceasta tehnica este necesar sa furnizam clientului si server-ului numai o copie a acestei interfete.

Daca rulati aplicatia de mai sus obtineti ca rezultat :

The new array is: 15 15 15 15 15 15 15

RMI vs. Socketi si Seralizare

RMI – Remote Method Invocation

RMI poate fi exprimat dupa urmatoarea formula :

RMI = Sockets + Object Serialization + Some Utilities

Avantaje ale RMI:

-simplitate

-nu trebuie sa crezi un protocol intre client si server

-trateaza toate obiectele ca si cum ar fi pe aceiasi masina

Toate aceste avantaje, in schimb, duc la o viteza mica de transmisie de date "utile" intre client si server. Daca dorim sa realizam un proiect trebuie sa ne gandim cu mare atentie ce sistem sa folosim pentru o obtine rezultate cat mai optime.

5. Fire de executie

Un sistem multiprocesor (SM) este un mecanism care permite unui sistem de a
folosi mai mult de un procesor. Sistemul Mutiprocesor Simetric (SMS) este o parte a Calculului Paralel unde toate procesoarele sunt identice. In SMS
procesoarele sunt dirijate in asa fel incit sarcinile sunt impartite de catre
sistemul de operare iar aplicatiile sunt executate pe mai multe procesoare
care impart acelasi spatiu de memorie. SMS garanteaza ca intreg numarul de
procesoare deserveste sistemul de operare. Fiecare sub-proces poate fi executatpe orice procesor liber. Astfel se poate realiza echilibrarea incarcarii intreprocesoare. Java contine citeva caracteristici care-l fac un limbaj ideal
pentru SMS. Este un limbaj orientat obiect foarte simplu si cel mai important,este conceput sa suporte programare multiprocesor. In aceasta prezentare voi modul in care Java ajuta la crearea de aplicatii paralele. 

5.1 Ce sunt thread-urile?

Thread-ul reprezinta executia liniara a unei singure secvente de instructiuni
care ruleaza in interiorul programului nostru. Toti programatorii sunt familiarizati cu scrierea programelor secventiale. Programele secventiale au un punct de start, o secventa de executie si un punct terminal. Cel mai important lucru la programul secvential este acela ca la orice moment o singura instructiune este executata. Thread-ul este similar cu un program secvential in sensul ca thread-ul are si el un punct de start, o secventa de executie si un punct terminal. De asemenea intr-un thread se executa doar o singura instructiune la un moment dat. Si totusi un thread nu este la fel ca un program obisnuit. Spre deosebire de programe, thread-ul nu poate exista de unul singur. Thread-urile coexista in interiorul unui program. Deci putem avea mai multe thread-uri care se executa simultan.

Un singur thread nu ofera nimic nou. Orice program scris pina acum avea cel putin un thread in el. Noutatea apare atunci cind vrem sa folosim mai multe thread-uri, ceea ce inseamna ca aplicatia noastra poate sa faca mai multe lucruri in acelasi timp. Fiecare thread poate sa faca altceva in acelasi timp: unul sa incarce o pagina Web in timp ce altul animeaza o icoana sau toate pot colabora la acelasi job (generarea unei imagini 3D). Cind se foloseste corespunzator multithreading-ul cresc mult performantele appletului sau aplicatiei unde este folosit. Multithreading-ul poate simplifica fazele de proiectare si planificare a tuturor aplicatiilor greu de realizat intr-un program secvential. Un exemplu bun de thread-uri este un miniprocesor de calcule matematice care in timp ce se afiseaza sa zicem  graficul unei functii, pregateste cateva date speciale rezultate din calcule  matematice (minim, maxim, continuitate etc).
Alt exemplu este acela a unui browser Web care permite defilarea unui text al unei pagini pe care tocmai ai incarcat-o in timp ce browser-ul se ocupa cu aducerea  imaginilor. In cazul in care cautati ceva anume, ar fi neplacut sa astepti incarcarea paginii in intregime si apoi sa realizezi ca nu e ceea ce cauti. Thread-urile pot realiza de asemenea calculul mai rapid. Prin segmentarea unui task in subtask-uri si apoi avind cite un thread pentru fiecare subtask se poate creste mult viteza de executie. Aceasta este general valabil pentru un SMS.

5.2 Concurenta thread-urilor.

Fara a intra intr-o discutie pe teme hardware, este bine de spus ca procesoarele calculatoarelor pot executa doar o instructiune la un moment dat. De ce spunem ca thread-uri diferite se executa in acelasi timp? Spunind simultan nu inseamna numai lucruri in medii diferite. Pe omasina multiprocesor, thread-urile pot exista pe procesoare diferite in acelasitimp fizic, aceasta mentinindu-se valabil chiar daca procesoarele sunt pe
calculatoare diferite conectate intr-o retea. Dar si pe o masina cu un singurprocesor, thread-urile pot imparti acelasi procesor, rulind intr-o manieraintretesuta, competitia pentru timpii CPU creind iluzia ca ele se executasimultan. Aceasta iluzie pare reala cind 30 de imagini distincte pe secundacaptate de ochiul uman sunt percepute intr-un flux continuu de imagine. Comutarea intre thread-uri are si ea un pret : consuma timp CPU pentru ca acesta sa inghete starea unui thread si sa dezghete starea unui alt thread (schimbare de context). Daca thread-urile concurente sunt executate pe acelasi procesor si toate executa calcule atunci timpul total de executie nu va lua mai mult decit timpul de executie al unui program secvential care realizeaza acelasi lucru.
Din moment ce intr-un sistem monoprocesor thread-urile concura la timpul procesor cum este posibil cresterea vitezei sistemului? Aceasta se realizeaza prin intreteserea diferitelor faze ale diferitelor thread-uri. Multe task-uri pot
fi segmentate logic in tipuri de faze: faza de calcul si faza I/O. Faza de calcul
necesita atentia maxima din partea CPU-ului prin utilizarea diferitelor metode de calcul. Faza de I/O (intrare/iesire) necesita atentie maxima din partea  perifericelor (imprimante, hard discuri, placi de retea, etc) si in aceste situatii procesorul este in general liber, asteptind ca perifericul sa-si termine sarcina.  Cresterea vitezei este obtinuta prin intreteserea fazelor. In timp ce un thread se afla intr-o faza de I/O asteptind ca o secventa de date sa fie incarcata de pe hard disk, un thread cu o faza de calcul poate ocupa procesorul si cind ajunge la o faza I/O, celalalt thread (care tocmai a terminat faza I/O proprie) poateincepe sa utilizeze CPU-ul.

5.3 Contextul thread-urilor si memoria distribuita

Thread-urile ruleaza in contextul unui program, folosind resursele acestuia.
Fiecare thread are propriile variabile si puncte de executie, dar variabilele globale sunt impartite de toate thread-urile. Deoarece ele impart acelasi
spatiu (variabilele globale si alte resurse) toate acestea pot accesa la un
moment dat aceeasi data. Este important de observat ca memoria comuna prezinta o degradare a performantelor cind lucreaza cu un cluster de computere conectateintr-o retea, asemenea cazului unui sistem distribuit de tip clustere de PC-uri.Obiectele care sunt comune trebuie sa fie transmise pe retea de atitea ori de cit este nevoie. Programul poate avea de suferit in asemenea cazuri si executia  lui eficienta depinde mai degraba de parametrii retelei.

5.4 Executia paralela a thread-urilor

Cu toate ca exemplele de pina acum nu au fost in masura sa puna in evidenta vreun nivel de paralelism, ele de fapt au facut acest lucru. Odata ce s-a terminat executia instructiunii Thread.start(), instanta clasei MyThread isi incepe executia. Nu se poate spune cu siguranta ca mesajul de pe ecran a fost tiparit dupa terminarea metodei main(), tiparirea pe ecran putind avea loc si inaintea terminarii metodei main(). Totul depinde de felul in care au fost alocati timpii procesor la thread-uri si de faptul daca exista unul sau mai multe procesoare. Pentru a demonstra acest principiu, sa consideram urmatorul program si rezultatul lui:

class PrintThread implements Runnable { 
String str; 
public PrintThread (String str) { 
this.str = str; 

public void run() { 
for (;;) 
System.out.print (str); 

class Test { 
public static void main (String Args[]) { 
new Thread(new PrintThread("A")).start(); 
new Thread(new PrintThread("B")).start(); 

Iesirea programului de mai sus poate arata cam cum urmeaza :

AAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 
BBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB 
BBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB 
BBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBB… 

Cu un numar egal de "A" si de "B". Acest exemplu este menit sa demonstreze faptul ca aceste doua thread-uri lucreaza in paralel.
Multithreading preemptiv contra multithreading non-preemptiv. Multithreading preemptiv inseamna faptul ca un thread poate fi preemptat (suspendat) de catre alt thread in timp ce se executa. Nu toate sistemele care suporta multithreading prezinta mecanism preemptiv. Iesirea aceluiasi program pe un sistem SPARC/Solaris 2.5 ar arata in felul urmator:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Acest lucru este datorat faptului ca pe Solaris (si nu numai) thread-urile nu 
sunt preemptive. Un thread trebuie sa aiba "o comportare echitabila" si sa 
renunte la timpul sau procesor in asa fel incit sa permita si altor thread-uri 
sa se executa. Renuntarea la propriul timp CPU in mod voluntar se realizeaza prin invocarea metodei proprii yield(). In continuare iata o versiune revizuita  a clasei PrintThread care elibereaza CPU-ul dupa fiecare litera afisata:

class WellBehavedPrintThread implements Runnable { 
String str; 
public PrintThread (String str) { 
this.str = str; 

public void run() { 
for (;;) { 
System.out.print (str); 
Thread.currentThread().yield(); 

Instructiunea Thread.currentThread().yield() utilizeaza o metoda publica a 
clasei Thread pentru a capata handler-ul catre thread-ul curent si apoi ii spune sa elibereze procesorul. Iesirea acestui exemplu este:

ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB 
ABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABAB 

5.5 Starile thread-urilor

Creind o instanta a unui thread acesta nu este lansat. Aceasta sarcina de a 
lansa thread-ul in executie este realizata de metoda start(). Un thread se poate gasi in stari diferite in functie de evenimentele trecute. Putem fi in una din situatiile:

5.5.1. Metoda run() nu este in executie, timpul procesor nu este inca alocat. Pentru a starta un thread trebuie apelata functia start(). In aceasta stare se poate apela de asemenea metoda stop(), care va distruge thread-ul.

5.5.2. Thread in executie
Thread-ul a fost startat cu metoda start(). Este thread-ul pe care procesorul il 
executa in acest moment.

5.5.3. Thread gata de executie (runnable)
Thread-ul a fost startat dar nu este in executie pe procesor in acest moment. 
Motivul ar fi acela ca thread-ul a renuntat la procesor apelind metoda yeld(), 
sau din cauza unui mecanism de programare a timpului procesor care a decis sa distribuie timpul procesor al acestui thread altui thread. Thread-ul va putea trece in executie cind mecanismul de programare va decide acest lucru. Atita timp cit exista un alt thread cu o prioritate mai mare, thread-ul nu va trece in executie.

5.5.4. Thread nepregatit pentru executie (non runable thread)
Thread-ul nu poate fi executat din anumite motive. Ar putea sa astepte o operatie de tip I/O sa se termine. A trebuit sa apeleze metoda wait(), sleep() sau suspend().

5.6 Prioritatile thread-urilor

Fiecarui thread ii este asignata o prioritate cuprinsa intre MIN_PRIORITY 
(egala cu 1) si MAX_PRIORITY (egala cu 10). Un thread mosteneste prioritatea de la thread-ul care l-a creat, dar aceasta prioritate se poate schimba apelind metoda setPriority() si aceasta prioritate se poate afla apelind getPriority(). 
Algoritmul de planificare intodeauna va lansa thread-ul cu prioritatea cea mai mare sa se execute. Daca exista mai multe thread-uri cu aceeasi prioritate maxima atunci procesorul le va executa intr-o maniera round-robin. Astfel un thread cu prioritate mica se poate executa numai atunci cind toate thread-urile de prioritate mai mare sun in starea non-runnable().

Prioritatea thread-ului maineste NORM_PRIORITY( egala cu 5). Iata un exemplu de utilizare a prioritatilor:

class LowPriority extends Thread { 
public void run(){ 
setPriority(Thread.MIN_PRIORITY); 
for(;;){ 
System.out.println("The LOW priority thread is running"); 



class HighPriority extends Thread { 
public void run(){ 
setPriority(Thread.MAX_PRIORITY); 
for(;;){ 
for (int i=0; i<5; i++) 
System.out.println("The HIGH priority thread is running"); 
try { 
sleep(100); 
} catch (InterruptedException e) { 
System.exit(0); 



class Spawner { 
public static void main( String args[] ) { 
LowPriority l = new LowPriority(); 
HighPriority h = new HighPriority(); 
System.out.println("Starting threads…"); 
l.start(); 
h.start(); 
System.out.println("MAIN is done"); 

Iesirea acestui program va arata ca mai jos:

Starting threads… 
The LOW priority thread is running 
The LOW priority thread is running 
The HIGH priority thread is running 
The HIGH priority thread is running 
The HIGH priority thread is running 
The HIGH priority thread is running 
MAIN is done 
The LOW priority thread is running 
… 
The LOW priority thread is running 
The HIGH priority thread is running 
The HIGH priority thread is running 
The HIGH priority thread is running 
The HIGH priority thread is running 
The LOW priority thread is running 
… 
The LOW priority thread is running 
The HIGH priority thread is running 
^C 

Sa analizam putin iesirea programului:

1. Prima linie este afisata de thread-ul principal. Thread-ul principal 
lanseaza thread-ul de prioritate minima si thread-ul de prioritate maxima.
2. Threadul de prioritate minima incepe sa se execute, acesta este facut imediat non-runnable de catre un thread de prioritate mai mare.
3. Thread-ul de prioritate mai mare incepe sa se execute, isi seteaza prioritatea maxima si astfel isi incepe executia. Dupa afisarea a 5 linii, thread-ul este "adormit" si thread-ul main devine cel mai prioritar.
4. Thread-ul main afiseaza un mesaj dupa care se termina. In acest moment 
singurul thread gata de executie (runnable) este thread-ul de prioritate mica.
5. De aici incolo iesirea programului va arata la fel.
6. Thread-ul de prioritate mica va afisa mesaje proprii pina cind thread-ul de 
prioritate mai mare se va trezi.
7. Thread-ul de prioritate mare incepe sa se execute, afiseaza 5 linii si 
"adoarme" din nou.
8. Thread-ul de prioritate minima poate rula din nou

Sistemul poate genera o exceptie de tipul IllegalThreadStateException cind este apelata o metoda a unui thread cind starea acestuia nu permite acest lucru. De exemplu, exceptia, IllegalThreadStateException este generata cind se face apelul metodei suspend() unui thread care nu este runnable. Si un ultim amanunt despre starile thread-urilor: clasa Thread cuprinde o metoda numita isAlive() care returneaza true daca thread-ul a fost startat dar nu stopat si false cind thread-ul este in starea new Thread sau Dead. Nu se poate face distinctie intre un thread in stare new Thread si un thread in stare Dead si nici intre unul Runnable si unul not Runnable.

5.6.Grupuri de thread-uri

Fiecare thread apartine unui grup de thread-uri. Un grup de thread-uri este o 
multime de thread-uri (si posibil grupuri de thread-uri) impreuna cu un 
mecanism de realizare a operatiilor asupra tuturor membrilor multimii. Grupul implicit de thread-uri este implicit numit main, si fiecare group de thread-uri nou creat apartine acestui grup, mai putin acelea specificate in constructorul sau. Pentru a afla pentru un thread la ce grup de thread-uri apartine se poate folosi metoda getThreadGroup(). Pentru a crea propriul nostru grup de thread-uri, trebuie mai intii sa creem un obiect ThreadGroup. Se poate folosi unul din acesti constructori : 
ThreadGroup(String) – creaza un nou ThreadGroup cu numele specificat. 
ThreadGroup(threadGroup, String) – creaza un nou ThreadGroup cu numele 
specificat si apartinind la un anumit grup de thread-uri. 
Dupa cum arata si al doilea constructor, un grup de thread-uri poate fi creat in interiorul altui grup de thread-uri. Cel mai nou grup de thread-uri creat devine membru la cel mai vechi realizindu-se astfel o ierarhie de grupuri. Pentru a crea un thread in interiorul unui grup de thread-uri, altul decit grupul main trebuie doar mentionat numele grupului atunci cind se apeleaza constructorul thread-ului. In momentul in care avem mai multe thread-uri organizate intr-un grup de thread-uri putem apela la operatii comune pentru toti membrii acestui grup. Aceste operatii sunt in principal stop(), supend() and resume() care au aceeasi semnificatie ca atunci cind se foloseste un singur thread. Pe langa aceste operatii mai exista si alte operatii specifice grupului de thread-uri. 

5.6.1. Sincronizare

Cind se utilizeaza mai multe thread-uri avem nevoie de o sincronizare a 
activitatilor lor. Exista cazuri in care dorim sa prevenim accesul concurent 
la structurile de date ale programului care reprezinta secvente comune acestor thread-uri. Limbajul Java ne ajuta in acest sens cu ajutorul unui mecanism de sincronizare si excludere mutuala (permite numai unui singur thread sa opereze asupra unei sectiuni critice). Sincronizarea dintre thread-uri in Java se realizeaza folosind metodele notify() si wait(). Ecluderea mutuala se realizeaza prin folosirea monitoarelor.

Sa consideram urmatoarea clasa al carei obiectiv este de a stoca date:

class MyData { 
private int Data; 
public void store(int Data) { 
this.Data=Data; 

public int load() { 
return this.Data; 

Acum sa presupunem ca avem doua thread-uri: unul care incearca sa depoziteze o valoare si unul care incearca sa scoata o valoare. In continuare se prezinta codul care creaza cele doua thread-uri. Pentru a simula procesarea in timp real, vom cere thread-urilor sa "adoarma" dupa fiecare extragere de data :

class Main {// Clasa utilizata pentru a pune lucrurile in miscare 
public static void main(String argv[]) { 
MyData data = new MyData(); 
new Thread(new Producer(data)).start(); 
new Thread(new Consumer(data)).start(); 

class Producer implements Runnable {//Thread-ul – Producator 
MyData data; 
public Producer(MyData data) { 
this.data=data; 

public void run() { 
int i; 
for (i=0;;i++) { 
data.store(i); 
System.out.println ("Producer: "+i); 
try { 
// "adormire" de 0 pina la 0.5 sec 
Thread.sleep ((int) (Math.random()*500)); 
} catch (InterruptedException e) { } 


class Consumer implements Runnable { //Thread-ul – Consumator
MyData data; 
public Consumer(MyData data) { 
this.data=data; 

public void run() { 
for (;;) { 
System.out.println ("Consumer: "+data.load()); 
try { 
Thread.sleep ((int) (Math.random()*500)); 
} catch (InterruptedException e) { } 


Acest program consta din doua thread-uri: Consumer (consumator) si Producer (producator). Producatorul "produce" si stocheaza datele in structura comuna celor doua thread-uri utilizind metoda store(). Consumatorul extrage acele date din structura MyData comuna. La prima vedere codul pare a fi in regula, dar acesta nu este din moment ce iesirea programului arata astfel:

Producer: 0 
Consumer: 0 
Producer: 1 
Consumer: 1 
Consumer: 1 
Producer: 2 
Producer: 3 
Consumer: 3 
Producer: 4 
Producer: 5 
Consumer: 5 
Producer: 6 
Consumer: 6 
Producer: 7 
Consumer: 7 
Consumer: 7 
Producer: 8 
Producer: 9 
Producer: 10 
Consumer: 10 

Dupa cum se observa, numerele 2, 4, 8 si 9 sunt produse dar nu sunt niciodata consumate. Pe de alta parte, numerele 1 si 7 sunt produse o singura data, dar consumate de cite doua ori. Acest lucru este datorat ordinei gresite de executie. In primul rind ca producatorul nu are cum sa stie daca consumatorul a consumat data si in consecinta suprascrie noua data. In al doilea rind, consumatorul nu are cum sa stie daca citeste noua valoare sau pe cea veche. Este deci nevoie de o comunicare intre cele doua thread-uri.

5.6.2. Solutie
Pentru a rezolva aceasta problema se pot folosi variabile binare pentru a 
controla accesul la data. Flag-ul Ready va semnifica faptul ca noua data a fost produsa si este gata de consum si flag-ul Taken va semnifica faptul ca aceasta data a fost consumata si este gata de suprascriere.

class MyData { 
private int Data; 
private boolean Ready; 
private boolean Taken; 
public MyData() { 
Ready=false; 
Taken=true; 

public void store(int Data) { 
while (!Taken); 
this.Data=Data; 
Taken=false; 
Ready=true; 

public int load() { 
int Data; 
while (!Ready); 
Data = this.Data; //Salvare, deoarece dupa ce 
//Taken devine true, Data se poate schimba oricand
Ready=false; 
Taken=true; 
return Data; 

Utilizind codul de mai sus vom obtine rezultatul asteptat : fiecare numar este consumat o singura data si toate numerele sunt consumate. Oricum aceasta solutieare un dezavantaj major : metodele store() si load() folosesc bucle, thread-urile testeaza in mod constant flag-urile pentru a vedea daca valorile lor s-au schimbat. Utilizarea buclelor de test ar putea determina ca acest program sa nu functioneze pe platforme nepreemptive deoarece thread-urile nu elibereaza procesorul si astfel thread-ul care trebuie sa schimbe o valoare, poate sa nu fie planificat pentru executie (aceasta problema ar putea fi rezolvata folosind apelurile metodei yield() in aceste bucle). O alta problema in aceste conditii poate aparea atunci cind se foloseste acelasi cod dar cu mai multi consumatori. Rulind mai mult de un thread de tip Consumer vom avea mai multe thread-uri care consuma din aceeasi sursa ceea ce ar putea conduce la situatii cind aceeasi valoare este consumata de mai multe ori. Iata in continuare un exemplu de concurenta intre Consumatori:

Consumer1 Consumer2 Producer
while(!Ready)
while(!Ready)
this.Data = Data;
Taken = false;
Ready = true
Data = this.Data;
Data = this.Data;
Ready = false;
Taken = true;
return Data;
Ready = false;
Taken = true;
return data;

Dupa cum se poate observa cei doi consumatori consuma aceeasi valoare. Aceasta se poate intimpla usor cind thread-urile impart acelasi procesor prin preemptare, sau procesoare multiple care ruleaza thread-urile in acelasi timp. O solutie la aceasta ultima problema este utilizarea mecanismului de 
sincronizare din Java, mecanism care are la baza monitorul.

5.6.3. Monitoare

Un monitor (pentru prima data introdus de Hoare in 1974) este asociat intodeauna cu o data specifica si o functie care controleaza accesul la aceasta data. Cand un thread retine un monitor pentru a accesa o data, celelalte thread-uri sunt blocate si nu pot avea acces la acea data. Un thread poate prelua un monitor numai atunci cind celelalte thread-uri nu l-au preluat si il poate elibera cind doreste. Poate exista un monitor pentru fiecare instanta a unei clase care are o metoda declarata ca synchronized. Declararea unei metode synchronized indica faptul ca numai acest thread care contine monitorul poate executa aceasta metoda. Daca nici un thread nu a preluat monitorul, apelarea unei astfel de metode are ca efect preluarea acestui monitor. Achizitionarea monitorului este o operatie formata dintr-o singura instructiune care garanteaza faptul ca un singur thread va prelua acel monitor. 

5.6.4 Sincronizarea utilizand monitoare

In urmatorul exemplu al clasei MyDatase foloseste metode de tip synchronized:

class MyData { 
private int Data; 
private boolean Ready; 
private boolean Taken; 
public MyData() { 
Ready=false; 
Taken=true; 

public synchronized void store(int Data) { 
while (!Taken); 
this.Data=Data; 
Taken=false; 
Ready=true; 

public synchronized int load() { 
while (!Ready); 
Ready=false; 
Taken=true; 
return this.Data; 

Metodele "sincronizate" elimina nevoia de a stoca variabile de tip Data in 
interiorul metodei load(), astfel ca metodele load() si store() nu vor putea sa 
se execute in acelasi timp in thread-uri diferite. O problema apare, insa, cind 
un thread este "surprins" intr-o bucla de test si detine inca monitorul. 
Celalalt thread nu va mai fi niciodata capabil sa-si execute propriul cod 
deoarece nu poate prelua monitorul. Doua thread-uri sunt in stare de interblocare cind unul dintre ele asteapta ca o valoare sa se schimbe in timp ce-l impiedica pe celalalt sa modifice acea valoare. Lucrul de care avem nevoie este de a prelua monitorul dupa ce a asteptat ca un flag sa se modifice. Solutia este de a folosi cuvintul cheie synchronized in asa fel incit sa protejam numai segmentul de cod critic care are nevoie de excludere mutuala:

class MyData { 
private int Data; 
private boolean Ready; 
private boolean Taken; 
public MyData() { 
Ready=false; 
Taken=true; 

public void store(int Data) { 
while (!Taken); 
synchronized (this) { 
this.Data=Data; 
Taken=false; 
Ready=true; 


public int load() { 
while (!Ready); 
synchronized (this) { 
Ready=false; 
Taken=true; 
return this.Data; 


De notat faptul ca atunci cind folosim cuvintul cheie synchronized pe un 
segment de cod, trebuie sa declaram obiectul insusi ca parametru al lui 
synchronized. Si totusi o problema ramine: bucla de test. Aceasta practica de a folosi bucle de test este considerata a fi gresita pentru thread-uri: 
consumatoare mare de timp procesor intr-o implementare preemptiva care, dupa cum s-a vazut, cauzeaza interblocarea intr-o implementare nonpreemptiva.

5.6.5. Asteptarea de evenimente

Exista totusi o cale de a evita buclele de test si in acelasi timp de a elimina 
necesitatea folosirii unuia din flag-uri utilizate pentru sincronizare. Solutia 
este de a folosi metodele wait() si notify() care sunt membre ale clasei Object din care este derivata orice clasa (aceste metode exista pentru orice obiect in Java). Metodele wait() si notify() sunt utilizate pentru determinarea asteptarii unui eveniment si respectiv trimiterea lor la un thread. Acest mecanism functioneaza dupa cum urmeaza: metoda wait() face ca thread-ul sa elibereze monitorul si il comuta pe acesta din starea runnable in starea non-runnable. Thread-ul va astepta in aceasta stare din urma pina cind este "trezit" de un apel al metodei notify(). Cind un thread isi termina "asteptat-ul" el va recapata monitorul. Metoda notify() alege in mod arbitrar un thread din cele care sunt in asteptare si il elibereaza de aceasta stare. Metodele wait() si notify() sunt utilizate in clasa MyData dupa cum urmeaza:

class MyData { 
private int Data; 
private boolean Ready; 
public MyData() { 
Ready=false; 

public synchronized void store(int Data) { 
while (Ready) 
try { 
wait(); 
} catch (InterruptedException e) { } 
this.Data=Data; 
Ready=true; 
notify(); 

public synchronized int load() { 
while (!Ready) 
try { 
wait(); 
} catch (InterruptedException e) { } 
Ready=false; 
notify(); 
return this.Data; 

In acest moment avem rezultatele dorite: fara a avea bucle wait si fara probleme de sincronizare. De observat faptul ca metodele wait() si notify() pot fi apelate numai din metode synchronized. A fi reentrant inseamna faptul ca codul clasei este protejat impotriva accesului multiplu. Toate clasele construite in Java sunt reentrante ceea ce inseamna ca ele pot fi folosite in programarea multithreading fara probleme. Din moment ce Java este pentru reutilizarea obiectelor si din moment ce nu stim cind sa utilizam o clasa din nou, este necesar sa o proiectam reentranta inca de la inceput. Ultima implementare a clasei MyData este o implementare reentranta. Sa vedem ce se intimpla cind mai multe thread-uri incearca sa acceseze aceeasi metoda simultan: 

a. Mai multi consumatori – un singur producator. 

Consideram urmatoarea problema: producatorul nu a produs inca nimic. Primul consumator preia monitorul dar va intra in wait() si-l va elibera permitind ca al doilea consumator sa urmeze aceeasi cale. Vom avea eventual toti consumatorii in stare wait() si monitorul eliberat. In acest moment producatorul intra in actiune: produce si apeleaza notify(). Aceasta cauzeaza faptul ca unul din thread-urile in wait() incearca sa preia monitorul. Din moment ce producatorul tocmai a eliberat monitorul iesind din segmentul de cod synchronized aceasta nu va cauza nici o problema. Consumatorul va "consuma" data si va apela notify(). 
Acest notify() va "trezi" alt thread care va gasi ca nu exista nimic de citit 
pentru moment si astfel se va reintoarce in wait() eliberind din nou monitorul. Producatorul va fi din nou capabil sa-l preia si sa emita un nou numar.

b. Mai multi producatori – un singur consumator

Acum situatia este viceversa: acum sunt mai multi producatori si un singur 
consumator. Acum producatorii vor astepta (wait()) in timp ce consumatorul va "trezi" pe fiecare dintre ei la un anumit timp permitindu-le sa genereze noi date. Deoarece Ready este false dupa ce consumatorul "consuma" fiecare valoare, un singur thread producator va produce o singura valoare. Deoarece producatorul va schimba starea flag-ului Ready ceilalti producatori nu vor suprascrie o noua valoare (chiar daca acestia sunt "treji") pina ce aceasta valoare nu este consumata.

5.7. Bariere

In aplicatiile multithreading este necesar ca anumite thread-uri sa se 
sincronizeze la un anumit punct. Un exemplu este calculul paralel in faza, in 
care toate thread-urile trebuie sa-si termine faza de executie inainte de a 
trece toate odata la faza urmatoare. O bariera este un mecanism folosit pentru a sincroniza mai multe thread-uri. Un thread care intilneste o bariera intra automat in wait(). Cind ultimul thread "ajunge" la bariera, semnaleaza (notify()) si celorlalte thread-uri care sunt in asteptare rezultind o "trecere" in grup a barierei. Iata un exemplu in acest sens:

import java.util.*;

class Barrier { // Clasa Barrier sincronizeaza toti 
//participantii
private int ParticipatingThreads; 
private int WaitingAtBarrier; 
public Barrier(int num){ //Constructorul obiectului 
ParticipatingThreads = num; 
WaitingAtBarrier=0; 

public synchronized void Reached() { //Metoda bariera
WaitingAtBarrier++; 
if ( ParticipatingThreads != WaitingAtBarrier ) { 
//Inseamna ca thread-ul nu este ultimul
try { 
wait(); //Thread-ul este oprit pina ce //este eliberat
} catch (InterruptedException e) { } 
} else { 
// Acesta a fost ultimul thread activ
notifyAll(); 
WaitingAtBarrier=0; // le elibereaza pe toate


Ajungind la bariera toate thread-urile, mai putin ultimul, asteapta in interiorul metodei synchronized ca ultimul thread ajuns sa le elibereze. Odata ce s-au "trezit" ei intra in competitie pentru monitor si unul dintre ei il cistiga, apoi iese imediat eliberind monitorul. Celelalte thread-uri continua sa concureze pentru acest monitor si sa-l elibereze pina ce ultimul thread face acest lucru si-n acest moment sunt cu totii "liberi". In acest exemplu am presupus ca se cunoaste dinainte numarul de thread-uri participante. Daca, insa, acest numar nu este cunoscut dinainte poate fi aplicat un mecanism de inregistrare a participantilor. Orice thread care doreste sa astepte la bariera, mai intii trebuie sa se "inregistreze" incrementind numarul de thread-uri in asteptare. De unde stim ca toate thread-urile care asteapta la bariera s-au inregistrat? Ce se intimpla daca toate thread-urile mai putin unul si-au terminat de facut calculele si ultimul thread (care, probabil, nici nu a fost planificat sa ruleze inca) nu s-a inregistrat inca. Toate celelalte thread-uri vor trece bariera nestiind ca trebuie sa-l astepte si pe acesta din urma.

5.8. Asteptarea terminarii unui thread

Uneori este necesar a se astepta terminarea unui thread. De exemplu tread-ul principal (main) poate crea un al doilea thread pentru a executa ceva in interiorul lui. Java permite monitorizarea starii unui thread, altul decit cel in care se face aceasta operatie si suspendarea executiei pina ce acesta se termina. O metoda care poate fi folosita este metoda isAlive() care returneaza true daca thread-ul invocat nu este dead. Pentru asteptarea terminarii unui thread (fara a utiliza bucle) putem utiliza metoda join(). Aceasta metoda face ca un thread sa astepte pina cind un alt thread isi termina executia urmind sa-si reia executia in momentul in care thread-ul asteptat s-a terminat. Un thread poate fi intrerupt prin apelarea metodei interrupt(), caz in care metoda join() va "arunca" exceptia InterruptedException. Iata un exemplu de program in care un 
thread asteapta un alt thread sa se termine:

Class MainThread extends Thread { 
public void run() { 
SecondaryThread s = new SecondaryThread(); 
s.start(); 
// do my job… 
// wait for the secondary thread to die 
if (s.isAlive()) s.join(); 
// We are done ! 

5.9. Alte metode de sincronizare

In regula, veti spune, Java are aceasta sincronizare cu monitoare dar eu vreau sa am vechile mele semafoare. Nici o problema. Utilizind monitoare se poate implementa orice obiect sincronizat dorit inclusiv semafoare. Iata in continuare un exemplu de semafor in Java: 

class Semaphore { 
protected int value; 
Semaphore( int initialValue ) { 
value = initialValue; 

Semaphore() { 
value = 0; 

public synchronized void Get() { 
while (value<1) wait(); 
value–; 

public synchronized void Put() { 
value++; 
notify(); 

5.10 Starvation

Termenul de starvation (flaminzirea) caracterizeaza situatiile in care un thread este privat de resurse (accesul la un monitor). Spre deosebire de interblocare, in situatia de starvation calculele pot continua in sistem, doar ca thread-ul flaminzit nu mai poate continua. Starvation se poate produce atunci cind un thread de prioritate mai mare isi incepe executia si nu mai elibereaza
procesorul. De altfel toate thread-urile de prioritate mai mica sunt flaminzite.

5.11 Interblocarea (Deadlock)

Interblocarea se produce cind unul sau mai multe thread-uri asteapta schimbarea unei conditii in timp ce acea conditie este exclus sa se schimbe deoarece toate thread-urile care ar putea face acest lucru sunt in asteptare. Am vazut cum poate aparea un interblocaj atunci cind se folosesc bucle de test in interiorul unui monitor, asteptind ca un alt thread sa schimbe o conditie dar fara ai da posibilitatea de a obtine monitorul.

5.12. Asteptarea terminarii unui thread

Uneori este necesar a se astepta terminarea unui thread. De exemplu tread-ul principal (main) poate crea un al doilea thread pentru a executa ceva in interiorul lui. Java permite monitorizarea starii unui thread, altul decit cel in care se face aceasta operatie si suspendarea executiei pina ce acesta se termina. O metoda care poate fi folosita este metoda isAlive() care returneaza true daca thread-ul invocat nu este dead. Pentru asteptarea terminarii unui thread (fara a utiliza bucle) putem utiliza metoda join(). Aceasta metoda face ca un thread sa astepte pina cind un alt thread isi termina executia urmind sa-si reia executia in momentul in care thread-ul asteptat s-a terminat. Un thread poate fi intrerupt prin apelarea metodei interrupt(), caz in care metoda join() va "arunca" exceptia InterruptedException. Iata un exemplu de program in care un thread asteapta un alt thread sa se termine:

Class MainThread extends Thread { 
public void run() { 
SecondaryThread s = new SecondaryThread(); 
s.start(); 
// do my job… 
// wait for the secondary thread to die 
if (s.isAlive()) s.join(); 
// We are done ! 

5.13 Alte metode de sincronizare

In regula, veti spune, Java are aceasta sincronizare cu monitoare dar eu vreau sa am vechile mele semafoare. Nici o problema. Utilizind monitoare se poate implementa orice obiect sincronizat dorit inclusiv semafoare. Iata in continuare un exemplu de semafor in Java: 

class Semaphore { 
protected int value; 
Semaphore( int initialValue ) { 
value = initialValue; 

Semaphore() { 
value = 0; 

public synchronized void Get() { 
while (value<1) wait(); 
value–; 

public synchronized void Put() { 
value++; 
notify(); 

5.14. Starvation

Termenul de starvation (flaminzirea) caracterizeaza situatiile in care un thread este privat de resurse (accesul la un monitor). Spre deosebire de interblocare, in situatia de starvation calculele pot continua in sistem, doar ca thread-ul flaminzit nu mai poate continua. Starvation se poate produce atunci cind un thread de prioritate mai mare isi incepe executia si nu mai elibereaza
procesorul. De altfel toate thread-urile de prioritate mai mica sunt flaminzite.

5.15 Interblocarea (Deadlock)

Interblocarea se produce cind unul sau mai multe thread-uri asteapta schimbarea unei conditii in timp ce acea conditie este exclus sa se schimbe deoarece toate thread-urile care ar putea face acest lucru sunt in asteptare. Am vazut cum poate aparea un interblocaj atunci cind se folosesc bucle de test in interiorul unui monitor, asteptind ca un alt thread sa schimbe o conditie dar fara ai da posibilitatea de a obtine monitorul.

6. Arhitectura server.

6.1 Privire de ansamblu

Server –ul aplicatie CWS accepta conexiuni de la clienti cu sockets TCP, folosind port-ul 1979, este capabil sa deserveasca mai multi clienti in acelas timp, lansand pentru fiecare client in parte cate un fir de excecutie care sa faca comunicatia cu un anumit client. Server-ul contine o bucla while unde astepta conexiuni, daca un client face o cere de conectare se creaza un fir de executie care va schimba mesaje cu acel client. Noul client este inregistrat in baza de date si apoi se trimite un mesaj de tip broadcast care anunta toti clienti de inregistrarea noului client pe Chat. Se foloseste serializare obiectelor, descrisa in sectiunea 4, pentru trasmitrea de packete intre client si server, se creaza astfel un protocol propriu intre client si server, spre exemplu: daca clientul trasmite packet-ul 1 atunci server-ul va trimite si e packet-ul 1 la client dar va avea cu totul alta semnificatie packet-ul 1 de la server si packet-ul 1 pe partea de client.

Clasa Packet:

class Packet implements IPacket

{

int id=0;

Vector lada=new Vector();

//constructorul

Packet(int id)

{

this.id=id;

}

}

Se observa ca are o variabila membru: int id – indentificatorul packet-ului si data membru: lada de tip Vector, in care se poate pune absolut orice obiect dorim.

Desriere semnificatii packete:

6.2 Retea

Structura generala a unui server bazat pe conexiune:

while (true) {

accept a connection ;

create a thread to deal with the client ;

end while

Server-ul va crea pentru fiecare client in parte cate in fir de executie, un fir de executie moare in momentul in care, respectivul client, incheie sesiunea cu server-ul sau conexiunea dintre un client si server se intrerupe.

6.3. Baza de date cu canale si clienti

Baza de date cu canele si clientii de pe aceste canale arata ca in fig. de mai jos:

Baza de date apare sub forma unei liste, lista principala se numeste canale, care are 2 date membru: numeCanal si o referinta la o alta lista numita clienti. Lista clienti are si ea date membru: numele clientului, referinta la oos (pe unde se trimit date la client). Declararea listei canale din cadrul clasei Canale are modificatorul static.

Pentru adaugarea unui nou canal folosesc metoda :

/**

* ADAUGA UN CANAL

*/

public void addCanal(String numeCanal)

{

clienti=new RCL();

clienti.numeCanal=numeCanal;

synchronized(this)

{

canale.add(clienti);

}

}

Pentru adaugarea unui client pe un anumit canal folosec metoda:

/**

* ADAUGARE DE CLIENT PE UN CANAL

*/

public void addClientPeCanal(String numeCanal,//numele canalului

String numeClient,//numele de logare

ObjectOutputStream oos)//teava //NET

{

//adauga un client pe canal

//1. identific canalul

for (int i=0;i<canale.size();i++)

{

RCL canal=(RCL)canale.elementAt(i);

if (canal.numeCanal.equals(numeCanal))

{

//adaugarea

synchronized(this)

{

canal.addClient(numeClient,oos);

break;

}

}//if

}//for

}

Pentru a nu avea probleme cu transmiterea de date (Ex: doi clienti trasmit mesaj unui alt client in acelas timp) codul care trasmite mesajul este synchronized, adica bucata respectiva de cod este blocata de un fir de executie care intra in acea sectiune critica. Un alt fir de executie va trebui sa astepte pana cand respectivul fir paraseste acea sectiune critica.

Pentru extragerea de informatii din baza de date se folosesc metode ca:

public Vector listaCanale();

public Vector listaClientiDePeUnCanal(String numeCanal);

/**

* Returneaza lista de canale.

*/

public Vector listaCanale()

{

Vector listaCanale=new Vector();

//initializare canale

for (int i=0;i<canale.size();i++)

{

RCL canal=(RCL)canale.elementAt(i);

listaCanale.add(canal.numeCanal);

}//for

return listaCanale;

}//listaCanale

Metoda de mai sus extrage lista de canale si o returneaza sub forma unei liste de tip Vector.

/**

* Lista clientilor de pe un anumit canal.

* @param numeCanal – numele canalului

*/

public Vector listaClientiDePeUnCanal(String numeCanal)

{

//1.identificarea calului

for(int i=0;i<canale.size();i++)

{

RCL canal=(RCL)canale.elementAt(i);

if (canal.numeCanal.equals(numeCanal))

{

return canal.listaClienti();

}//if

}//for

return new Vector();

}//listaClietiDePeUnCanal

Metoda de mai sus extrage lista de clienti de pe un anumit canal si o returneaza sub forma unui liste, la fel ca si la listaCanale(), sub forma unui liste de tip Vector.

7. Arhitectura client

7.1 Privire de ansamblu

Clientul isi creaza un socket si incearca sa obtina o conexiune cu server-ul, daca conexiunea a fost stabilita se creaza la client un fir de executie care se va ocupa cu citirea mesajelor de la server. Clientul are si el o baza de date pentru a micsorarea volum de informatii trasmis intre client si server, este mult mai econom sa trasmiti cite un nume de canal, care a fost creat de un client sau un nume de client, nou conectat, decat sa trasmit toata lista de canale sau lista de clienti de pe un anumit canal.

Clasa Reciving este cea care se ocupa de primirea mesajelor de la server, liste de clienti, listei de canale. Instanta acestei clase creaza un fir si isi incheie viata in momentul in care se intrerupe conexiunea cu server-ul. Metoda run() contine o bucla in care se citesc pachetele de server. Aceste pachete sunt interpretate si in functie de tipul lor se produc anumite evenimente.

7.2. Retea

Clientul TCP are o arhitectura mult mai simpla fata de server-ul TCP. Clientul isi face un socket si apoi incearca sa se conecteze la server, daca conexiunea a fost stabilita clientul primeste un mesaj prin care este informat de starea conexiunii in caz contrar se arunca o exceptie Conection Reset by Peer sau Conection Refuzed.

// open a socket connection

socket = new Socket(L.adresaServer,1979);

// open I/O streams for objects

oos = new ObjectOutputStream(socket.getOutputStream());

ois = new ObjectInputStream(socket.getInputStream());

//conectare cu succes

-oos si ois sunt stream-uri de date care sunt create in urma conexiunii.

7.3. Baza de date

Este asemanatoare cu cea a serverului, a fost creata pentru restangerea trasmiterii de date reduntante pe retea. Arata ca in figura de mai jos:

Baza de date contine metode specifice pentru extragerea listei de clienti de pe un anumit canal sau a listei de canale. Deasemenea contine metode pentru adaugarea de noi canale sau adaugare de noi clienti pe canale.

Lista de clienti de pe un anumit canal:

/**

*Lista clientilor de pe un anumit canal.

*/

public Vector listaClientiDePeUnCanal(String numeCanal)

{

Vector listaClienti=new Vector();

//1. identific canalul de pe care etrag lista de clienti

for(int i=0;i<canale.size();i++)

{

RLCC canal=(RLCC)canale.elementAt(i);

if (canal.numeCanal.equals(numeCanal))

{

listaClienti=canal.listaClienti();

return listaClienti;

}//if

}//for

return listaClienti;

}//listaClientiDePeUnCanal

9. Tipuri de pachete transmise intre client si server

10. Comparatii cu alte produse soft existente pe piata in momenul de fata.

Pe piata de soft sunt nenumarate programele de chat si videoconferinta in timp real. Una dintre cele mai populare astfel de aplicatii este mIrc-ul, care are prin toata lumea servere si un site de unde client-ul de mIrc poate fi descarcat contra unei anumite sume.

Aplicatia mea, CWS, este implemntata in Java, ceia ce, ii confera un mare ajantaj, este independenta de platforma, are avea acelas Luck and fell pe orice platforma, o dimensiune forte mica, clientul are numai 54 Kocteti pe cand clientul de mIrc are 1,5 Mb. Clientul CWS-ului poate sa ruleze si in cadrul unei pagini Web, sub forma unui applet, si nu cere cunostinte vaste in utilizarea calculatoarelor pentru a putea utiliza acest program.

Un alt mare avantaj ar fi siguranta datelor, se stie ca appleturile lucreaza intr-un sandbox, care are multe restrictii, nu poate scrie fisiere pe HDD calculatorului, nu poate citii nici un fel de informatii despre utilizator, calculatorului, adresa de e-mail, etc. Alte aplicatii, Yahoo Messanger spre exemplu, sunt foarte des supuse atacurilor hackerilor dotorita breselor de securitate.

11. Concluzii

Folosirea limajului de programare Java ii confera aplicatie CWS mari ajantaje fata de alte programe de acest gen. Poate fi folosit de creatorii de pagini Web pentru inglobarea clientului de chat in paginile site-urilor care le creaza, deasemena poate lucra si ca o aplicatie independenta.

Nouatatea cu care vine acesta aplicatie ar sta in felul in care este proiectat interfata acestuia, clientul mIrc-ului zapaceste utilizatorul cand schimba mesaje cu mai multi clienti prin numarul de ferestre deschise, exista pericolul ca utilizatorul sa nu mai stie unde trimite mesaje pentru tot canalul sau care e fereasta cu un client anume, toate aceste erori de proiectarea interfetei au fost inlaturate in aplicatia CWS. Mesaje insotite de imagine pot usura viata unui client deorece acesta poate identifica cu o usurinta mai mare de la cine a primit un anumit mesaj.

Bibliografie

12.1 Referinte Internet

www.java.ro

www.java.sun.com

www.programare.ro

www.infoiasi.ro/~stefan

www.infoiasi.ro/~acf

12.2. Referinte (carti):

[BeP99] – Bell D., Perr M.: Java for Students, Second Edition,

Prentice Hall, 1999

[Bud2000] – Budd, T.: Understanding Object-Oriented Programming with

JAVA. Addison-Wesley, 2000

[CGI2000] – Chan,M.C., Griffith,S.W., Iasi,A.F.: JAVA. 1001 secrete

pentru programatori, Teora, 2000

[GrK97]- Grand, M., Knudsen, J.: JAVA. Fundamental Classes Reference,

O'Reilly, 1997

[HSH1999] – Hughes,M., Shoffner,M., Hamner, D.: JAVA. Network

Programming, Manning, 1999

[LaO99] – Lambert,K. A., Osborne,M.: Java. A Framework for

Programming and Problem Solving, PWS Publishing, 1999

Similar Posts

  • Portal Băile Felix

    Cuprins 1.Introducere…………………………………………………………………………………..4 2. Fundamentare teoretica……………………………………………………………………..6 2.1 Baze de date .……………………………………………………………………………7 2.1.1 Tipuri de date în MySQL…………………………………………………………7 2.1.2 Crearea unei tabele în baza de date……………………………………………….7 2.1.3 Inserarea datelor în baza de date………………………………………………….7 2.1.4 Modificarea tabelelor……………………………………………………………..8 2.1.5 Ștergerea de înregistrări din baza de date…………………………………………8 2.1.7 Ștergerea unei baze de date……………………………………………………….8 2.2 Aplicatii web……………………………………………………………………………8 3. Tehnologii utilizate…………………………………………………………………………11 3.1…

  • .sistemul Visualbanking

    CAPITOLUL I BANCILE VIITORULUI Analiza istorica a permis distingerea a trei factori esentiali ai evolutiei sistemului bancar: mijloacele de plata, activitatea economica si, indeosebi comerciala. La fel, si viitorul bancilor trebuie analizat pornind de la acesti factori determinanti. Dezvoltarea telematicii (totalitatea serviciilor informatice furnizate printr-o retea de telecomunicatii) si a cartelei cu memorie (cartela in…

  • Sistem de Control Acces

    Cuprins Cuprins 1 1. Introducere 3 2. Considerente teoretice 4 2.1. Portul paralel al Pc-ului 4 2.1.1. IEEE 1284 4 2.1.2. Conectoarele 6 2.1.3. Cablul de conectare la Port 8 2.1.4. Modul de Funcționare Electric 8 2.1.4.1. Modul de Compatibilitate 8 2.1.4.1.1. Liniile de Date 9 2.1.4.2. Modul pe Nibluri 10 2.1.4.3. Modul pe Octeți…

  • Metrici Soft Pentru Evaluarea Termenului Necesar Realizarii Unui Proiect

    Metrici soft pentru evaluarea termenului necesar realizării unui proiect 1. Introducere 1.1 Ce sunt metricile soft? În anii ’40 când a început era calculatorului, erau doar câteva calculatoare în funcțiune și cele mai multe aplicații erau mici, fiind proiecte de o singură persoană. O dată cu trecerea timpului, calculatoarele au devenit foarte răspândite, aplicațiile au…

  • Particularitatile Testarii Automate a Sistemelor Informationale

    CUPRINS Introducere Testarea reprezintă o activitate critică privind calitatea sistemelor, ea reprezentând o ultimă ocazie pentru revizuirea cerințelor sistemului, a specificațiilor de proiectare și a programelor sursă. Obiectivul general al fazei de testare îl reprezintă identificarea numărului maxim de erori cu efort minim. Programele trebuie testate pentru a avea încredere că vor funcționa cum trebuie….