In aceasta lucrare de laborator vor fi acoperite urmatoarele probleme:
- 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
Anexa: Modificarea programelor Java
pentru a lucra cu socket-uri flux – sistem
client-server
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.
Un navigator (browser)
Web este un exemplu de aplicatie
multi-filara (multithreaded). In browser
se poate parcurge pagina in timpul
descarcarii unei miniaplicatii Java (applet),
etc. JVM (Java Virtual Machine), permite
aplicatiilor sa aiba mai multe fire de executie in paralel.
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(); new Thread(r).start(); // nu exista variabila de tip Thread // 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 |
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.
Rezultatele a doua executii succesive:
In laborator: 1. Lansati mediul BlueJ. Inchideti
proiectele anterioare (Ctrl+W). Creati
un nou proiect fire (Project
-> New Project…, selectati D:/, apoi SwRTc2007, 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. |
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:
In laborator: Testati applet-ul CeasApplet folosind link-ul de mai sus. |
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:
|
|
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.
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:/, SwRTc2007, 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). |
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); } } } |
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). |
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. |
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. |
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. |
I. Codul final al clasei ServerRepetiv (cerut in paragraful 2.4.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!
Clasa Polinom reprezinta o varianta a clasei Polinom4 de la lucrarea anterioara. Initializarea atributelor este
realizata de:
- un constructor fara
parametri, care obtine gradul
polinomului si valorile coeficientilor de la utilizator, initializeaza atributele cu aceste
valori, si apoi le scrie
intr-un fisier pe disc (cu nume prestabilit "coef.txt"),
- un constructor cu un parametru de tip int[], care foloseste tabloul primit pentru a
initializa valorile atributelor, apoi le scrie intr-un fisier pe disc (cu nume prestabilit "coef.txt"),
- un constructor cu un parametru de tip String, care formeaza din el numele unui fisier de pe disc (cu nume
prestabilit "coef.txt"), din care citeste valorile
coeficientilor si cu ele initializeaza atributele.
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
import javax.swing.JOptionPane; import java.io.*; public class Polinom { protected int N; // gradul polinomului protected int[] C; // coeficientii polinomului // Constructor - initializeaza elementele
polinomului // cu valori obtinute de la utilizator,
si le scrie in fisier public Polinom()
throws IOException {
PrintWriter outFisier = new PrintWriter(new BufferedWriter(new
FileWriter("coef.txt"))); N =
Integer.parseInt(JOptionPane.showInputDialog( "Introduceti gradul polinomului")); C = new int[N+1]; for (int i=0; i<=N; i++) { C[i]
= Integer.parseInt(JOptionPane.showInputDialog( "Coeficientul de grad " + i)); outFisier.println(C[i]); }
outFisier.close();
System.out.println("A fost creat un obiect Polinom..."); } // Constructor - initializeaza elementele
polinomului // cu valori obtinute ca parametru, si le
scrie in fisier public Polinom(int[]
coeficienti) throws IOException {
PrintWriter outFisier = new PrintWriter(new BufferedWriter(new
FileWriter("coef.txt"))); N =
coeficienti.length-1; C =
new int[N+1]; for
(int i=0; i<=N; i++) { C[i] = coeficienti[i]; outFisier.println(C[i]); }
outFisier.close();
System.out.println("A fost creat un obiect Polinom..."); } // Constructor - initializeaza elementele
polinomului // cu valori citite din fisier public Polinom(int
grad) throws IOException {
BufferedReader inFisier = new BufferedReader(new
FileReader("coef.txt"));
N=grad; C =
new int[grad+1]; for
(int i=0; i<=grad; i++) { C[i]
= Integer.parseInt(inFisier.readLine()); }
System.out.println("A fost creat un obiect Polinom..."); } public
void afisarePolinom() { System.out.print("P(X) = " +
C[0]); for
(int i=1; i<=N; i++) {
System.out.print(" + " + C[i] + "*X^" + i); }
System.out.println(); }
// Metoda care calculeaza valoarea polinomului pentru o necunoscuta public int
valoarePolinom(int X) { int
P_X = 0; int
X_i = 1; // Termenii +Ci*X^i, unde 0=1,N for (int i=0; i<=N; i++) { P_X = P_X + C[i]*X_i; X_i = X_i * X; } return
(P_X); } // Metoda care returneaza tabloul
coeficientilor polinomului public
int[] coeficienti() { int[]
coef = new int[N+1];
System.arraycopy(C, 0, coef, 0, N+1); return
(coef); } public
String toString() { String
polinom = "" + C[0]; for
(int i=1; i<=N; i++) {
polinom = polinom + " + " + C[i] + "*X^" + i; }
System.out.println("N=" + N); return
(polinom); } } |
Metoda main() a clasei UtilizareDirectaPolinom contine un scenariu
de utilizare a clasei Polinom bazat
pe apelul primului constructor (care obtine valorile coeficientilor de la utilizator).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import
java.io.*; import
javax.swing.JOptionPane; public class UtilizareDirectaPolinom { public
static void main (String
args[]) throws IOException { // Apelul constructorului care initializeaza elementele polinomului Polinom pol = new Polinom(); // Obtinerea unei valori a necunoscutei
(X) int X =
Integer.parseInt(JOptionPane.showInputDialog(
"Introduceti valoarea necunoscutei")); // Afisarea valorii necunoscutei (X) JOptionPane.showMessageDialog(null, "X =
" + X); // Apelul metodei care calculeaza polinomul pentru necunoscuta X int P_X = pol.valoarePolinom(X); // Afisarea valorii P(X) JOptionPane.showMessageDialog(null,
"P(" + X + ") = " + P_X); System.exit(0); // Inchiderea interfetei grafice } } |
Programul TCPServerPolinom utilizeaza clasa Polinom. Programul UtilizareTCPClientPolinom (prin metoda sa principala) utilizeaza
clasa TCPClientPolinom, care comunica prin socket TCP cu un
obiect al clasei Polinom. Sistemul
client server astfel creat permite unui utilizator la distanta sa efectueze un
scenariu asemanator utilizarii directe a clasei Polinom.
Programul UtilizareTCPClientPolinom:
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 |
import java.io.*; import java.net.*; import javax.swing.JOptionPane; public class UtilizareTCPClientPolinom { public static void main (String args[]) throws IOException { // Apelul
constructorului care initializeaza elementele polinomului TCPClientPolinom pol = new TCPClientPolinom(); // Obtinerea unei valori a necunoscutei
(X) int X =
Integer.parseInt(JOptionPane.showInputDialog( "UtilizareClient:
Introduceti valoarea necunoscutei")); // Afisarea valorii necunoscutei (X) JOptionPane.showMessageDialog(null,
"UtilizareClient: X = " + X); // Apelul metodei care calculeaza
polinomul pentru necunoscuta X int P_X = pol.valoarePolinom(X); // Afisarea valorii P(X) JOptionPane.showMessageDialog(null,
"UtilizareClient: P(" + X + ") = " + P_X); // Apelul metodei care inchide sesiunea pol.close(); System.exit(0); // Inchiderea interfetei grafice } } |
Programul TCPClientPolinom:
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
import java.io.*; import java.net.*; import
javax.swing.JOptionPane; public
class TCPClientPolinom {
protected int N; //
gradul polinomului protected
int[] C; // coeficientii
polinomului
protected PrintStream outRetea;
protected BufferedReader inRetea; // Constructor -
initializeaza polinomul cu valori obtinute de la utilizator public TCPClientPolinom() throws IOException { String adresaServer = JOptionPane.showInputDialog( "Client: Introduceti adresa IP a serverului"); int portServer = Integer.parseInt(JOptionPane.showInputDialog( "Client:
Introduceti numarul de port al serverului")); // Crearea socketului (implicit este
realizata conexiunea) Socket conexiuneTCP = new Socket(adresaServer,
portServer); // Crearea fluxurilor
de caractere conectate la fluxurile de octeti // obtinute de la socketul TCP outRetea = new PrintStream(conexiuneTCP.getOutputStream()); inRetea = new BufferedReader(
new InputStreamReader(conexiuneTCP.getInputStream())); //
Obtinerea gradului polinomului (N) N =
Integer.parseInt(JOptionPane.showInputDialog( "Client: Introduceti gradul polinomului")); // Crearea tabloului coeficientilor (de
dimensiune N+1) C = new int[N+1]; // Obtinerea coeficientilor Ci, unde
i=0,N for (int i=0; i<=N; i++) { C[i] = Integer.parseInt(JOptionPane.showInputDialog( "Client:
Introduceti coeficientul de grad " + i)); } // Comanda creare polinom String comanda = "CREARE_POLINOM"; // Trimiterea comenzii ca String prin
fluxul de iesire TCP outRetea.println(comanda);
outRetea.flush(); // Trimiterea gradului polinomului prin
fluxul de iesire TCP outRetea.println(N); outRetea.flush(); // Trimiterea coeficientilor polinomului prin fluxul de iesire TCP for (int i = 0; i < (N+1); i++) { outRetea.println(C[i]); outRetea.flush(); } // Obtinerea polinomului String polinom = inRetea.readLine();
// Afisarea polinomului JOptionPane.showMessageDialog(null,
"Client: P(X) = " + polinom); } // Metoda care calculeaza valoarea
polinomului pentru o necunoscuta public int valoarePolinom(int
X) throws IOException { // Comanda creare polinom String comanda = "CALCUL_POLINOM"; // Trimiterea comenzii ca String prin
fluxul de iesire TCP outRetea.println(comanda);
outRetea.flush(); // Trimiterea gradului polinomului prin
fluxul de iesire TCP outRetea.println(N); outRetea.flush(); // Trimiterea valorii necunoscutei prin
fluxul de iesire TCP outRetea.println(X); outRetea.flush(); // Obtinerea valorii polinomului pentru
necunoscuta data String valoare = inRetea.readLine(); return
(Integer.parseInt(valoare));
} //
Metoda care anunta inchiderea sesiunii public
void close() throws
IOException { // Comanda inchidere sesiune String comanda = "BYE"; //
Trimiterea comenzii ca String prin fluxul de iesire TCP outRetea.println(comanda);
outRetea.flush(); } } |
Programul TCPServerPolinom:
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
import java.io.*; import java.net.*; import
javax.swing.JOptionPane; public
class TCPServerPolinom { public
static void main (String
args[]) throws IOException { int
portServer = Integer.parseInt(JOptionPane.showInputDialog( "Server:
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) {
JOptionPane.showMessageDialog(null, "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(); JOptionPane.showMessageDialog(null,
"Server: 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 comanda = inRetea.readLine();
// Afisarea liniei citite la
consola de iesire JOptionPane.showMessageDialog(null,
"Server: Mesaj primit: "
+ comanda); // Comanda creare polinom if (comanda.equals("CREARE_POLINOM")) {
// Obtinerea gradului polinomului
String gradS = inRetea.readLine();
int grad = Integer.parseInt(gradS);
JOptionPane.showMessageDialog(null, "Server: grad = "
+ grad); // Obtinerea coeficientilor polinomului String coefS; int[] C = new int[grad+1];
for (int i = 0; i < (grad+1); i++) {
coefS = inRetea.readLine();
C[i] = Integer.parseInt(coefS);
JOptionPane.showMessageDialog(null, "Server: C[" + i
+ "] = " + C[i]); } // Apelul metodei care
initializeaza elementele polinomului Polinom pol = new Polinom(C); String polinom = pol.toString(); // Trimiterea polinomului ca
String prin fluxul de iesire TCP outRetea.println(polinom);
outRetea.flush(); } // Comanda creare polinom else if (comanda.equals("CALCUL_POLINOM")) {
// Obtinerea gradului polinomului
String gradS = inRetea.readLine();
int grad = Integer.parseInt(gradS);
JOptionPane.showMessageDialog(null, "Server: grad = "
+ grad);
// Obtinerea necunoscutei
String necS = inRetea.readLine();
int nec = Integer.parseInt(necS);
JOptionPane.showMessageDialog(null, "Server: nec = "
+ nec);
// Utilizare Polinom
Polinom polCopy = new Polinom(grad);
int valoare = polCopy.valoarePolinom(nec);
// Trimiterea valorii polinomului prin fluxul de iesire TCP
outRetea.println(valoare);
outRetea.flush(); } //
Testarea conditiei de oprire a servirii
else if (comanda.equals("BYE")) break; } //
Inchiderea socketului (si implicit a fluxurilor)
conexiuneTCP.close();
System.out.println("Bye!"); } } } |