POO – Laborator 6

Notiuni de proiectare orientata spre obiecte (2)

 

6.1. Descrierea laboratorului

 

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

 

6.2. Sisteme de comunicatie bazat pe socketuri TCP, interfete grafice Swing si fire de executie

6.2.1. Sistem de comunicatie client-server (unu-la-unu) bazat pe socket TCP, interfata grafica Swing si fire de executie

 

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:

 

 

\

 


 

6.2.2. Sistem de comunicatie client-server (N-la-N) bazat pe socket TCP, interfata grafica Swing si fire de executie

 

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.

 

 

 

 

6.3. Precizari privind "mini-proiectul" pentru examen      

 

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)