Salta el contingut
 

Pràctica 3: Sòcols i serveis

Autor: Joan Puigcerver Ibáñez

Correu electrònic: j.puigcerveribanez@edu.gva.es

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

Entrega

L'entrega ha de complir els següents requisits:

  • Package: ud3.practices
  • El nom de la classe on comença l'execució (main()) de cada exercici és el títol de l'exercici.
  • El format de la eixida del programa ha de ser consistent amb el format demanat.
  • S'ha d'entregar un fitxer .zip amb el contingut del package ud3.practices.
  • El codi ha d'estar pujat a GitHub en el vostre repositori de l'assignatura.
  • Tag GitHub: PracticeSocket (StackOverflow: Create a tag in a GitHub repository)

LyricsPlayer

  • Package: ud3.practices.lyrics

Adapta el codi de la Pràctica 2: LyricsPlayer perquè tinga una estructura client/servidor i es connecte a un servidor per a rebre les línies de la cançó.

El protocol de comunicació entre el client i el servidor s'ha d'implementar mitjançant l'enviament d'objectes.

Codi de la solució de la Pràctica 2
LyricsPlayer.java
package ud2.practices.lyrics.solution;

import java.util.ArrayList;
import java.util.List;

public class LyricsPlayer {
    private final Player player;
    private final Loader loader;
    private final List<String> lines;
    private boolean end;

    public LyricsPlayer(String filename) {
        player = new Player(this);
        loader = new Loader(this, filename);
        lines = new ArrayList<>();
        end = false;
    }

    public synchronized void setEnd(boolean end) {
        this.end = end;
    }
    public synchronized boolean ended(int i) {
        return end && i >= lines.size();
    }

    public synchronized boolean isLineAvailable(int i){
        return i < lines.size();
    }

    public synchronized String getLine(int i) throws InterruptedException {
        while (!isLineAvailable(i)) wait();

        return lines.get(i);
    }

    public synchronized int addLine(String line){
        lines.add(line);
        notifyAll();
        return lines.size() - 1;
    }


    public void start(){
        player.start();
        loader.start();
    }
    public void join() throws InterruptedException {
        player.join();
        loader.join();
    }
    public void interrupt() {
        player.interrupt();
        loader.interrupt();
    }

    public static void main(String[] args) {
        LyricsPlayer lyricsPlayer = new LyricsPlayer("files/ud2/lyrics.txt");
        lyricsPlayer.start();

        try {
            lyricsPlayer.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
Player.java
package ud2.practices.lyrics.solution;

public class Player extends Thread {
    private final LyricsPlayer lyricsPlayer;
    private int lyricsIndex;

    public Player(LyricsPlayer lyricsPlayer) {
        this.lyricsPlayer = lyricsPlayer;
        lyricsIndex = 0;
    }

    public void playLine(int i) throws InterruptedException {
        String[] line = lyricsPlayer.getLine(i).split(" ");

        for (int j = 0; j < line.length; j++) {
            Thread.sleep(500);
            if(j == 0)
                System.out.printf("%d: ", i);
            else
                System.out.print(" ");
            System.out.print(line[j]);
        }
        System.out.println();
    }

    @Override
    public void run() {
        try {
            while(!lyricsPlayer.ended(lyricsIndex)){
                this.playLine(lyricsIndex);
                lyricsIndex++;
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
            lyricsPlayer.interrupt();
        }
    }
}
Loader.java
package ud2.practices.lyrics.solution;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.concurrent.ThreadLocalRandom;

public class Loader extends Thread {
    private LyricsPlayer lyricsPlayer;
    private String filename;

    public Loader(LyricsPlayer lyricsPlayer, String filename) {
        this.lyricsPlayer = lyricsPlayer;
        this.filename = filename;
    }

    @Override
    public void run() {
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = br.readLine()) != null) {
                Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 10000));
                int i = this.lyricsPlayer.addLine(line);
                System.err.printf(" (Line %d loaded) ", i);
            }
            this.lyricsPlayer.setEnd(true);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            lyricsPlayer.interrupt();
        }
    }
}

Client

  • Package: ud3.practices.lyrics.client

Originalment, la classe Loader llegeix les línies, una a una, des d'un fitxer de text.

Adapta el codi perquè, en compte de llegir-les del fitxer, es connecte a un servidor que estarà disponible en localhost en el port 1234 per demanar-li les línies.

Important

Heu d'implementar els missatges com a objectes.

El client començarà per preguntar-li al servidor quants línies té la cançó amb un missatge del tipus NUM_LINES

A continuació, el client enviarà una petició del tipus GET indicant l'índex de la línia que vol rebre.

Servidor: LyricsServer

  • Package: ud3.practices.lyrics.server

Crea el servidor LyricsServer en Java que escolte en el port 1234. El servidor proporcionarà la lletra de la cançó disponible a files/ud2/lyrics.txt

El servidor ha de ser capaç de gestionar diferents clients a la vegada, per tant, haurà d'implementar la classe LyricsServerHandler per comunicar-se amb cada client.

Important

Heu d'implementar els missatges com a objectes.

El servidor gestionarà les peticions dels clients seguint el protocol següent:

  • NUM_LINES: El servidor respondrà amb un missatge del tipus SUCCESS amb el nombre de línies de la cançó.
  • GET + i:
    • Si i està dins del rang de línies de la cançó, el servidor respondrà amb un missatge del tipus SUCCESS amb la línia corresponent.
    • Si i està fora del rang, el servidor respondrà amb un missatge del tipus ERROR amb el missatge La línia solicitada no existeix.

Exemple d'execució

En aquest exemple, la cançó serà:

Ramonet, si vas a l'hort
porta figues, porta figues

  1. La classe Loader (client) es connectarà amb el servidor.
  2. El client preguntarà al servidor quants línies té la cançó amb un missatge NUM_LINES.
  3. El servidor contestarà amb SUCCESS 2.
  4. El client sol·licitarà la primera línia amb un missatge GET 0.
  5. El servidor rebrà GET 0.
  6. El servidor contestarà amb Ramonet, si vas a l'hort
  7. El client rebrà la línia de la lletra i l'afegirà a la cua.
  8. El client enviarà el missatge GET 1.
  9. El servidor rebrà GET 1.
  10. El servidor contestarà amb porta figues, porta figues.
  11. El client rebrà la línia de la lletra i l'afegirà a la cua.
  12. Com que ja no queden linies, el client acabarà la connexió.
sequenceDiagram
    actor Usuari
    Client->>Servidor: Connexió
    Servidor-->>Client: Connexió acceptada
    Client->>Servidor: NUM_LINES
    Servidor-->>Client: SUCCESS n
    loop i = 0 to n
        Client->>Servidor: GET i
        alt Línia 'i' existeix
            Servidor-->>Client: Línia i
            Client->>Usuari: Reprodueix línia
        else
            Servidor-->>Client: ERROR: La línia solicitada no existeix
            Client->>Client: Acaba el bucle i set 'end = true'
        end
        end
    Client->>Client: Acaba el bucle i set 'end = true'