In aceasta lucrare de laborator va fi tratata:
- Realizarea unui sistem
software mai complex care sa include elemente de comunicatie prin socket TCP,
interfata grafica Swing, fire de executie multiple
Clasa DialogUtilizator3 incapsuleaza elementele grafice Swing necesare
interfetei cu utilizatorul atat la client cat si la server:
import javax.swing.JOptionPane; import java.awt.*; import java.awt.event.*; import javax.swing.*;
public class DialogUtilizator03 extends JFrame { private JTextField inTextGrafic; // Intrare - linie de text grafica (JtextField) private JTextArea outTextGrafic; // Iesire - zona de text grafica (JtextArea) private JScrollBar vertical; private ConexiuneRetea03 conexiune; private String sirCitit;
// Initializari grafice public DialogUtilizator03(String nume) { // constructor super(nume); // Stabilire titlu fereastra (JFrame) Container containerCurent = this.getContentPane(); containerCurent.setLayout(new BorderLayout()); // Zona de text non-editabila de iesire (cu posibilitati de defilare) outTextGrafic = new JTextArea(5, 40); JScrollPane scrollPane = new JScrollPane(outTextGrafic, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); vertical = scrollPane.getVerticalScrollBar(); containerCurent.add("Center", scrollPane); outTextGrafic.setEditable(false); // Camp de text editabil de intrare inTextGrafic = new JTextField(40); containerCurent.add("South", inTextGrafic); // Inregistrarea "ascultatorului" de "evenimente actionare" la // "obiectul sursa" intrare de text inTextGrafic.addActionListener(new AscultatorInText()); // Iesire din program la inchiderea ferestrei setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); // Impachetarea (compactarea) componentelor in container setVisible(true); // Fereastra devine vizibila inTextGrafic.requestFocus(); // Cerere focus pe intrarea de text din fereastra curenta } public String nextLine() { String linie = inTextGrafic.getText(); // Citirea unei linii din intrarea text inTextGrafic.setText(""); // Golirea intrarii text return linie; } public int nextInt() { String linie = inTextGrafic.getText(); // Citirea unei linii din intrarea text inTextGrafic.setText(""); // Golirea intrarii text return Integer.parseInt(linie); } public void printLine(String text) { outTextGrafic.append(text + "\n"); vertical.setValue(vertical.getMaximum() - vertical.getVisibleAmount()); validate(); repaint(); } public void setConexiune(ConexiuneRetea03 conexiune) { this.conexiune = conexiune; }
// Clasa interna "ascultator" de "evenimente actionare" // implementeaza interfata ActionListener class AscultatorInText implements ActionListener { // Tratarea actionarii intrarii de text (introducerii unui "Enter") public void actionPerformed(ActionEvent ev) { sirCitit = nextLine(); printLine(conexiune.getLocalAddress() + sirCitit); // Scriere in zona text conexiune.printLine(sirCitit); if (sirCitit.equals(new String("BYE"))) System.exit(0); // Conditie terminare } } } |
Clasa ConexiuneRetea03 incapsuleaza elementele conexiunii TCP necesare
comunicatiei intre client si server:
import java.net.*; import java.io.*; import java.util.Scanner;
public class ConexiuneRetea03 { private Socket socket; // camp (atribut) private Scanner scannerTCP; private PrintStream printerTCP; public ConexiuneRetea03(Socket socket) throws IOException { // constructor this.socket = socket; this.scannerTCP = new Scanner(socket.getInputStream()); this.printerTCP = new PrintStream(socket.getOutputStream()); } public String nextLine() { return this.scannerTCP.nextLine(); } public int nextInt() { return this.scannerTCP.nextInt(); } public void printLine(String text) { this.printerTCP.println(text); this.printerTCP.flush(); } public String getLocalAddress() { return socket.getLocalAddress().getHostAddress(); } public String getRemoteAddress() { return socket.getInetAddress().getHostAddress(); } } |
Clasa ServerChat121 reprezinta serverul TCP cu interfata grafica:
import java.net.*; import java.io.*; import javax.swing.JOptionPane;
public class ServerChat121 extends Thread { private DialogUtilizator03 dialog; private ConexiuneRetea03 conexiune; private ServerSocket serverTCP; private Socket socketTCP; private int portTCP;
public ServerChat121() throws IOException { dialog = new DialogUtilizator03("SERVER CHAT 121"); portTCP = Integer.parseInt(JOptionPane.showInputDialog( "Introduceti numarul de port al serverului")); serverTCP = new ServerSocket(portTCP); // Creare socket server socketTCP = serverTCP.accept(); // Creare socket conexiune = new ConexiuneRetea03(socketTCP); dialog.setConexiune(conexiune); dialog.printLine("\nPentru oprire introduceti \"BYE\" si <Enter>\n"); } public static void main (String args[]) throws IOException { ServerChat121 server = new ServerChat121(); server.start(); } public void run() { // Fir executie receptie prin socket String mesaj; while(true) { mesaj = this.conexiune.nextLine(); this.dialog.printLine(conexiune.getLocalAddress() + mesaj); if (mesaj.equals("BYE")) break; // Testarea conditiei de oprire } try { this.socketTCP.close(); // Inchidere socket (implicit fluxuri) } catch (IOException ioe) {}; this.dialog.printLine("Bye!"); System.exit(0); } } |
Clasa ClientChat121 reprezinta clientul TCP cu interfata grafica:
import java.net.*; import java.io.*; import javax.swing.JOptionPane;
public class ClientChat121 extends Thread { private DialogUtilizator03 dialog; private ConexiuneRetea03 conexiune; private Socket socketTCP; private int portTCP; private InetAddress adresaIP;
public ClientChat121() throws IOException { dialog = new DialogUtilizator03("CLIENT CHAT 121"); portTCP = Integer.parseInt(JOptionPane.showInputDialog( "Introduceti numarul de port al serverului")); adresaIP = InetAddress.getByName(JOptionPane.showInputDialog( "Introduceti adresa IP a serverului")); socketTCP = new Socket(adresaIP, portTCP); // Creare socket conexiune = new ConexiuneRetea03(socketTCP); dialog.setConexiune(conexiune); dialog.printLine("\nPentru oprire introduceti \"BYE\" si <Enter>\n"); } public static void main (String args[]) throws IOException { ClientChat121 client = new ClientChat121(); client.start(); } public void run() { // Fir executie receptie prin socket String mesaj; while(true) { mesaj = this.conexiune.nextLine(); this.dialog.printLine(conexiune.getLocalAddress() + mesaj); if (mesaj.equals("BYE")) break; // Testarea conditiei de oprire } try { this.socketTCP.close(); // Inchidere socket (implicit fluxuri) } catch (IOException ioe) {}; this.dialog.printLine("Bye!"); System.exit(0); } } |
In laborator: 1. Lansati in
executie BlueJ. Inchideti proiectele anterioare (Ctrl+W). Creati un proiect chat121 (Project->New Project…, selectati D:/, Software2006, numarul
grupei, si scrieti chat121). 2. Creati cele 4 clase ale caror coduri sunt date mai sus. Compilati codurile. |
In laborator: 1. La unul dintre calculatoare right-click pe clasa ServerChat121.
2. Selectati si executati main().
Folositi numarul de port 3000. 3. La un alt calculator
right-click pe clasa Client Chat121, selectati si executati main().
4. Folositi adresa primului calculator
(pe care se executa ServerChat121) si numarul de port 3000. 5. Urmariti efectul
pe cele doua calculatoare. |
Etape succesive ale utilizarii clientului si serverului:
\
Clasa DialogUtilizator4 incapsuleaza elementele grafice Swing necesare
interfetei cu utilizatorul atat la client cat si la server:
import javax.swing.JOptionPane; import java.awt.*; import java.awt.event.*; import javax.swing.*;
public class DialogUtilizator04 extends JFrame { private JTextArea outTextGrafic; // Iesire - zona de text grafica (JtextArea) private JScrollBar vertical; private String prompt;
// Initializari grafice public DialogUtilizator04(String nume) { // constructor // Stabilire titlu fereastra (JFrame) super(nume); this.prompt = nume + ": "; Container containerCurent = this.getContentPane(); containerCurent.setLayout(new BorderLayout());
// Zona de text non-editabila de iesire (cu posibilitati de defilare) outTextGrafic = new JTextArea(5, 40); JScrollPane scrollPane = new JScrollPane(outTextGrafic, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); vertical = scrollPane.getVerticalScrollBar(); containerCurent.add("Center", scrollPane); outTextGrafic.setEditable(false);
// Iesire din program la inchiderea ferestrei setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); // Impachetarea (compactarea) componentelor in container setVisible(true); // Fereastra devine vizibila outTextGrafic.requestFocus(); // Cerere focus pe fereastra curenta } public String nextLine(String text) { return JOptionPane.showInputDialog(this.prompt + text); } public int nextInt(String text) { return Integer.parseInt(JOptionPane.showInputDialog(this.prompt + text)); } public void printLine(String text) { outTextGrafic.append(text + "\n"); vertical.setValue(vertical.getMaximum() - vertical.getVisibleAmount()); validate(); repaint(); } } |
Clasa ConexiuneRetea03 incapsuleaza elementele conexiunii TCP necesare
comunicatiei intre client si server:
import java.net.*; import java.io.*; import java.util.Scanner;
public class ConexiuneRetea03 { private Socket socket; // camp (atribut) private Scanner scannerTCP; private PrintStream printerTCP; public ConexiuneRetea03(Socket socket) throws IOException { // constructor this.socket = socket; this.scannerTCP = new Scanner(socket.getInputStream()); this.printerTCP = new PrintStream(socket.getOutputStream()); } public String nextLine() { return this.scannerTCP.nextLine(); } public int nextInt() { return this.scannerTCP.nextInt(); } public void printLine(String text) { this.printerTCP.println(text); this.printerTCP.flush(); } public String getLocalAddress() { return socket.getLocalAddress().getHostAddress(); } public String getRemoteAddress() { return socket.getInetAddress().getHostAddress(); } } |
Clasa ServerChatN2N reprezinta serverul TCP care poate trata mai multi
clienti:
import java.net.*; import java.io.*; import javax.swing.JOptionPane;
public class ServerChatN2N extends Thread { private static int numarClienti = 0; private static DialogUtilizator04 dialog; private static int portTCP; private static ServerSocket serverTCP; private static Socket socketTCP; public static final int NUMAR_MAXIM_CLIENTI = 10; private static ConexiuneRetea03[] conexiuni; private int numarulConexiuniiCurente; // incepe cu 0 public ServerChatN2N(int numarulConexiuniiCurente) throws IOException { this.numarulConexiuniiCurente = numarulConexiuniiCurente; }
public static void main (String args[]) throws IOException { dialog = new DialogUtilizator04("SERVER CHAT N2N"); conexiuni = new ConexiuneRetea03[NUMAR_MAXIM_CLIENTI]; portTCP = Integer.parseInt(JOptionPane.showInputDialog( "Introduceti numarul de port al serverului")); serverTCP = new ServerSocket(portTCP); // Creare socket server while (true) { socketTCP = serverTCP.accept(); // Creare socket conexiuni[numarClienti] = new ConexiuneRetea03(socketTCP); ServerChatN2N server = new ServerChatN2N(numarClienti++); server.start(); } }
public void run() { // Fir executie receptie prin socket String mesaj; while(true) { mesaj = this.conexiuni[numarulConexiuniiCurente].nextLine(); // this.dialog.printLine(mesaj); for (int i=0; i<numarClienti; i++) { conexiuni[i].printLine(mesaj); } // Testarea conditiei de oprire if (mesaj.equals("BYE")) { removeConnexion(numarulConexiuniiCurente); this.dialog.printLine("Bye!"); this.stop(); } } }
public void removeConnexion(int crt) { for (int i=crt; i<numarClienti-1; i++) { conexiuni[i] = conexiuni[i+1]; } numarClienti--; } } |
Clasa ClientChatN2N reprezinta serverul TCP cu interfata grafica:
import java.net.*; import java.io.*; import javax.swing.JOptionPane;
public class ClientChatN2N extends Thread { private DialogUtilizator04 dialog; private ConexiuneRetea03 conexiune; private Socket socketTCP; private int portTCP; private InetAddress adresaIP;
public ClientChatN2N() throws IOException { dialog = new DialogUtilizator04("CLIENT CHAT N2N"); portTCP = Integer.parseInt(JOptionPane.showInputDialog( "Introduceti numarul de port al serverului")); adresaIP = InetAddress.getByName(JOptionPane.showInputDialog( "Introduceti adresa IP a serverului")); socketTCP = new Socket(adresaIP, portTCP); // Creare socket conexiune = new ConexiuneRetea03(socketTCP); dialog.printLine("\nPentru oprire introduceti \"BYE\" si <Enter>\n"); }
public static void main (String args[]) throws IOException { ClientChatN2N client = new ClientChatN2N(); client.start(); String mesaj; while(true) { mesaj = client.dialog.nextLine("Introduceti mesajul"); client.conexiune.printLine(mesaj); if (mesaj.equals("BYE")) break; // Testarea conditiei de oprire } try { client.socketTCP.close(); // Inchidere socket (implicit fluxuri) } catch (IOException ioe) {}; client.dialog.printLine("Bye!"); System.exit(0); }
public void run() { // Fir executie receptie prin socket String mesaj; while(true) { mesaj = this.conexiune.nextLine(); this.dialog.printLine(conexiune.getLocalAddress() + mesaj); if (mesaj.equals("BYE")) break; // Testarea conditiei de oprire } try { this.socketTCP.close(); // Inchidere socket (implicit fluxuri) } catch (IOException ioe) {}; this.dialog.printLine("Bye!"); System.exit(0); } } |
In laborator: 1. Lansati in
executie BlueJ. Inchideti proiectele anterioare (Ctrl+W). Creati un proiect chatn2n (Project->New Project…, selectati D:/, Software2006,
numarul
grupei, si scrieti chatn2n). 2. Creati cele 4 clase ale caror coduri sunt date mai sus. Compilati codurile. |
In laborator: 1. La unul dintre calculatoare right-click pe clasa ServerChatN2N.
2. Selectati si executati main().
Folositi numarul de port 4000. 3. La alte 2-3 calculatoare
right-click pe clasa Client ChatN2N, selectati si executati main().
4. Folositi adresa primului calculator
(pe care se executa ServerChatN2N) si numarul de port 4000. 5. Urmariti efectul
pe cele doua calculatoare. |
Pornind de la codurile de mai sus, sa se proiecteze si implementeze in
limbajul de programare Java un sistem de comunicatie distribuit, suport pentru
conversatie textuala. Mini-proiectul va fi realizat de grupuri de
2-3 studenti.
Primul exemplu (chat 121) are o interfata grafica mai avansata (atat intrare de text JtextField cat si
iesire de text JTextArea) pe cand al doilea exemplu are o interfata grafica mai
simpla (foloseste JoprionPane.showInputDialog() pentru intrari).
Al doilea exemplu (chat N2N) in schimb are un
server care poate servi mai multi clienti, utilizatorii acestora putand
astfel purta o conversatie N la N (spre deosebire de primul exemplu care are o
topologie 1 la 1).
Mini-proiectul va fi considerat complet daca va combina interfata grafica din primul exemplu cu topologia din al doilea exemplu.
Alte imbunatatiri posibile:
- utilizarea clasei Vector in locul tabloului conexiunilor (in exemplu N2N)
- tratarea mai avansata a situatiilor exceptionale
- adaugarea altor elemente grafice (dintre cele mai simple ar fi un buton
cu ajutorul caruia se trimite mesajul editat in JtextField, ca alternativa la
folosirea unui <enter>)
Mini-proiectul va contine (pentru fiecare grup de studenti):
- dosarul cu documentatia mini-proiectului
(5-10 pagini), incluzand:
- titlul mini-proiectului si autorii – cu
indicarea rolului fiecaruia (prima
pagina)
- documentarea solutiilor tehnice care nu apar in exemple, a problemelor
intampinate,
- descrierea modului de utilizare al sistemului (cu screenshot-uri)
- listingurile codurilor sursa
atasate ca anexa la dosar
- mini-proiectul in
forma electronica (pe
discheta sau CD, intr-un plic atasat de dosar), adica:
-
documentatia si
-
codurile sursa.
Sustinerea mini-proiectului presupune:
- prezentarea executiei
sistemului (demonstrarea functionarii lui)
- prezentarea rolului fiecarui
autor la realizarea sistemului si a documentarii lui
- prezentarea solutiilor tehnice
specifice
- raspunsuri la intrebari privind
detalii ale mini-proiectului (din documentatie si listinguri)
- predarea dosarului cu
documentatia mini-proiectului (avand atasata forma electronica)