4.4) Multiplexing di messaggi

Finora abbiamo sviluppato stream di messaggi piuttosto semplici, che non aggiungono particolari funzionalità ai normali stream, pur presentando già alcuni vantaggi quali:

Comunque, gli stream visti finora formano la base necessaria sulla quale svilupparne di più sofisticati.

In particolare, ora vedremo come sviluppare degli stream di messaggi che siano in grado di effettuare il multiplexing, su un unico canale di comunicazione, di vari flussi di dati eterogenei fra loro, tipicamente generati da componenti diverse di una applicazione in maniera del tutto trasparente a tali componenti, che in genere ignorano l'esistenza le une delle altre.

Lo scopo si raggiunge incapsulando i messaggi dentro pacchetti nei quali la Control Information è costituita da un' etichetta che caratterizza l'origine dei dati.

Quando tali pacchetti giungono a destinazione, i messaggi vengono instradati alla corretta componente grazie a un meccanismo corrispondente di demultiplexing, che opera sulla base di tale Control Information.

In particolare, dopo aver introdotto le necessarie classi di utilità, svilupperemo un'applicazione client-server che implementa una chatline più raffinata di quella vista precedentemente.

Il client infatti è costituito da due parti indipendenti fra loro, che consentono l'una di inviare testo e l'altra disegni condividendo lo stesso canale fisico di comunicazione.


Figura 4-14: Chatline grafica e testuale

Il server ha il compito di effettuare il broadcast dei pacchetti che riceve da un client a tutti gli altri.

Come vedremo, esso non entra nel merito di ciò che riceve, e quindi rimarrà inalterato anche in caso si aggiungano ulteriori moduli funzionali ai client.

4.4.1) Classe MultiplexOutputStream

Questa classe estende MessageOutput, però si attacca a un altro MessageOutput e non a un semplice OutputStream.

Ciò consente di effettuare il multiplexing su un qualunque stream di messaggi (ad esempio su un QueueOutputStream oppure un MessageOutputStream).

Questo meccanismo ci consente di gestire più livelli di incapsulamento, uno dentro l'altro.


Figura 4-15: Incapsulamento multiplo

In sostanza, aggiungiamo un meccanismo di etichettatura all'OutputStream attaccato.

Dall'altra parte del canale, tale etichezza sarà usata per determinare l'origine dei dati.

Nel client che vedremo fra breve useremo questa classe per fare il multiplexing di due flussi di dati (che provengono dalle due componenti separate dell'applicazione) su un unico canale di comunicazione.


Figura 4-16: Multiplexing dei messaggi della chatline

La definizione della classe è la seguente (dal libro "Java Network Programming" di Merlin Hughes et al., Mannig Publications Co, Greenwich CT, 1997).

/* Copyright (c) 1996, 1997 Prominence Dot Com, Inc.        *
 * See the file legal.txt in the txt directory for details. */

package prominence.msg;

import java.io.*;

/**
 * A <tt>MessageOutput</tt> that attaches to an existing <tt>MessageOutput</tt>
 * and attaches a multiplexing label to the header of each message that
 * is transmitted.
 * <p>The label is specified in the constructor and so one stream always
 * attaches the same label.
 *
 * @version 1.0 1 Nov 1996
 * @author Merlin Hughes
 * @see prominence.msg.MultiplexInputStream
 */
public class MultiplexOutputStream extends MessageOutput {
  /**
   * The <tt>MessageOutput</tt> to which this is attached.
   */
  protected MessageOutput o;
  /**
   * A <tt>ByteArrayOutputStream</tt> used to buffer the current message
   * contents.
   */
  protected ByteArrayOutputStream byteO;
  /**
   * The multiplexing label.
   */
  protected String label;

  /**
   * Creates a new <tt>MultiplexOutputStream</tt>.
   * @param o The <tt>MessageOutput</tt> to which to send messages
   * @param label The multiplexing label to be used by this stream
   */
  public MultiplexOutputStream (MessageOutput o, String label) {
    super (new ByteArrayOutputStream ());
    byteO = (ByteArrayOutputStream) out;
    this.o = o;
    this.label = label;
  }

  /**
   * Sends the current message with a multiplexing label header to the
   * attached <tt>MessageOutput</tt>.
   * @exception IOException Occurs if there is a problem sending the
   * message.
   */
  public void send () throws IOException {
    synchronized (o) {
      o.writeUTF (label);
      byteO.writeTo (o); 
      o.send ();
    }
    byteO.reset ();
  }

  /**
   * Sends the current message with a multiplexing label header to the
   * attached <tt>MessageOutput</tt>.
   * <p>If the attached <tt>MessageOutput</tt> supports targeted sending
   * then this method will succeed; otherwise an appropriate
   * <tt>IOException</tt> will be thrown.
   * @param dst The list of intended recipients
   * @exception IOException Occurs if there is a problem sending the
   * message or the targeted <tt>send()</tt> method is not supported by
   * the attached <tt>MessageOutput</tt>.
   */
  public void send (String[] dst) throws IOException {
    synchronized (o) {
      o.writeUTF (label);
      byteO.writeTo (o); 
      o.send (dst);
    }
    byteO.reset ();
  }
}


Note

Costruttore

Metodi



4.4.2) Classe MultiplexInputStream

Questo è lo stream di input corrispondente a MultiplexOutputStream.

Questa classe estende MessageInput, si attacca a un altro MessageInput (non a un semplice InputStream) ed estrae l'etichetta da ogni messaggio che viene ricevuto.


Figura 4-17: MultiplexInputStream

L'etichetta è resa accessibile all'esterno, rendendo così possibile determinare come deve essere elaborata l'informazione contenuta nel messaggio.


Figura 4-18: Demultiplexing dei messaggi della chatline

La definizione della classe è la seguente (dal libro "Java Network Programming" di Merlin Hughes et al., Mannig Publications Co, Greenwich CT, 1997).

/* Copyright (c) 1996, 1997 Prominence Dot Com, Inc.        *
 * See the file legal.txt in the txt directory for details. */

package prominence.msg;

import java.io.*;

/**
 * A <tt>MessageInput</tt> that attaches to an existing <tt>MessageInput</tt>
 * and strips the multiplexing label from each message that is received.
 * <p>The label is made publicly accessible in the <tt>label</tt> variable.
 *
 * @version 1.0 1 Nov 1996
 * @author Merlin Hughes
 * @see prominence.msg.MultiplexOutputStream
 */
public class MultiplexInputStream extends MessageInput {
  /**
   * The multiplexing label of the most recently received message.
   */
  public String label;
  /**
   * The <tt>MessageInput</tt> to which this is attached.
   */
  protected MessageInput i;

  /**
   * Creates a new <tt>MultiplexInputStream</tt>.
   * @param i The <tt>MessageInput</tt> from which messages should
   * be received
   */
  public MultiplexInputStream (MessageInput i) {
    super (i);
    this.i = i;
  }

  /**
   * Receives a new message from <tt>i</tt> and strips the multiplexing
   * label. The label is accessible in the <tt>label</tt> variable; the
   * contents of the message can be read through the usual superclass
   * <tt>read()</tt> methods.
   * @exception IOException Occurs if there is a problem receiving a
   * message or extracting the multiplexing label.
   */
  public void receive () throws IOException {
    i.receive ();
    label = i.readUTF ();
  }
}


Note

Costruttore

Metodi


4.4.3) Classe Demultiplexer

Questa è la classe che si incarica di effettuare il demultiplexing vero e proprio.

Riceve i messaggi da un MultiplexInputStream e, sulla base della loro etichetta, li consegna ad uno dei MessageOutput a cui è connesso.

In sostanza, è un thread copiatore che ha in più la capacità di effettuare il demultiplexing dei messaggi in arrivo. Include dei metodi per registrare i (e cancellare la registrazione dei) MessageOutput che devono essere associati, in qualità di destinatari, alle etichette.


Figura 4-19: Demultiplexer

Tipicamente i MessageOutput sono dei QueueOutputStream, per le ragioni che abbiamo esposto in precedenza, oppure dei DeliveryOutputStream (che vedremo fra poco).

La definizione della classe è la seguente (dal libro "Java Network Programming" di Merlin Hughes et al., Mannig Publications Co, Greenwich CT, 1997).
/* Copyright (c) 1996, 1997 Prominence Dot Com, Inc.        *
 * See the file legal.txt in the txt directory for details. */

package prominence.msg;

import java.io.*;
import java.util.*;
/**
 * A class that reads messages from a <tt>MultiplexInputStream</tt>
 * and forwards them on to the <tt>MessageOutput</tt> identified by the
 * message label.
 *
 * @version 1.0 1 Nov 1996
 * @author Merlin Hughes
 * @see prominence.msg.MessageCopier
 */
public class Demultiplexer extends Thread {
  /**
   * The <tt>MultiplexInputStream</tt> from which messages are read.
   */
  protected MultiplexInputStream i;
  /**
   * The message routing table. Maps from message labels to
   * <tt>MessageOutput</tt>s.
   */
  protected Hashtable routes;

  /**
   * Current <tt>Demultiplexer</tt> ID.
   */
  static private int plexerNumber;
  /**
   * Assigns unique <tt>Demultiplexer</tt> ID's.
   * @return An unique <tt>Demultiplexer</tt> ID.
   */
  static private synchronized int nextPlexerNum () { return plexerNumber ++; }

  /**
   * Creates a new <tt>Demultiplexer</tt> reading from a specified
   * stream.
   * @param i The <tt>MultiplexInputStream</tt> from which mess. should be read
   */
  public Demultiplexer (MultiplexInputStream i) {
    super ("Demultiplexer-" + nextPlexerNum ());
    this.i = i;
    routes = new Hashtable ();
  }

  /**
   * Registers a <tt>MessageOutput</tt> as the destination for messages
   * with a particular label.
   * @param label The message label that is to be routed
   * @param o The destination for such messages
   */
  public void register (String label, MessageOutput o) {
    routes.put (label, o);
  }

  /**
   * Deregisters a particular message label.
   * @param label The label that is to be deregistered
   */
  public void deregister (String label) {
    routes.remove (label);
  }

  /**
   * Routes messages from the <tt>MultiplexInputStream</tt> to the
   * <tt>MessageOutput</tt> identified by their labels. <pre>This method
   * is called by a
   * new thread when the superclass <tt>start()</tt> method is called.
   * @see java.lang.Thread#start
   */
  public void run () {
    try {
      while (true) {
        i.receive ();
        MessageOutput o = (MessageOutput) routes.get (i.label);
        if (o != null) {
          byte[] message = new byte[i.available ()];
          i.readFully (message);
          synchronized (o) {
            o.write (message);
            o.send ();
          }
        }
      }
    } catch (IOException ex) {
      ex.printStackTrace ();
    }
  }
}


Costruttore

Metodi


4.4.4) Classe DeliveryOutputStream e Interfaccia Recipient

Consegnare i messaggi in una coda e aspettarsi che l'applicazione cerchi attivamente di prelevarli da lì, il che richiede in genere un thread separato, non è talvolta necessario, sopratutto per una piccola applicazione.

Una alternativa è definire un diverso tipo di MessageOutput, che consegna i messaggi attivamente al destinatario invece di scriverli su uno stream (che nel caso sopracitato è la coda).

Ciò si può implementare facendo sì che questo nuovo tipo di MessageOutput, quando deve spedire un messaggio con send(), chiami direttamente un apposito metodo receive() (che deve quindi essere presente) del destinatario.

In tal modo, quando un messaggio è spedito viene immediatamente consegnato al destinatario per l'elaborazione.

La contropartita è che il thread che invia il messaggio con send() (tipicamente, un thread copiatore quale il Demultiplexer) deve aspettare il ritorno della receive() del destinatario per poter proseguire la propria elaborazione e ricevere quindi il prossimo messaggio.

La classe DeliveryOutputStream implementa un MessageOutput del tipo descritto.


Figura 4-20: DeliveryOutputStream

La interfaccia Recipient dichiara il metodo receive(), e deve essere implementata da ogni destinatario che si voglia attaccare ad un DeliveryOutputStream.

4.4.4.1) Classe DeliveryOutputStream

E' un MessageOutput che si attacca a un oggetto che implementa l'interfaccia Recipient.

I messaggi sono costruiti dentro un ByteArrayOutputStream e sono consegnati al destinatario immediatamente, dopo averli spostati in un ByteArrayInputStream dal quale il destinatario provvederà a leggerli.


Figura 4-21: Implementazione di DeliveryOutputStream

La definizione della classe è la seguente (dal libro "Java Network Programming" di Merlin Hughes et al., Mannig Publications Co, Greenwich CT, 1997).

/* Copyright (c) 1996, 1997 Prominence Dot Com, Inc.        *
 * See the file legal.txt in the txt directory for details. */

package prominence.msg;

import java.io.*;

/**
 * A <tt>MessageOutput</tt> that immediately delivers its
 * contents to a recipient specified in the constructor when
 * send() is called.
 * <p>The message is delivered through the <tt>Recipient</tt> interface.
 *
 * @version 1.0 1 Nov 1996
 * @author Merlin Hughes
 * @see prominence.msg.Recipient
 */
public class DeliveryOutputStream extends MessageOutput {
  /**
   * A <tt>ByteArrayOutputStream</tt> used to buffer the message contents.
   */
  protected ByteArrayOutputStream byteO;
  /**
   * The recipient of messages sent to this stream.
   */
  protected Recipient r;

  /**
   * Creates a new <tt>DeliveryOutputStream</tt> with a specified recipient.
   * @param r The recipient for messages sent to this stream.
   */
  public DeliveryOutputStream (Recipient r) {
    super (new ByteArrayOutputStream ());
    byteO = (ByteArrayOutputStream) out;
    this.r = r;
  } 

  /**
   * Delivers the current message contents to the designated recipient.
   */
  public void send () {
    byte buffer[] = byteO.toByteArray ();
    ByteArrayInputStream bI = new ByteArrayInputStream (buffer);
    r.receive (new DataInputStream (bI));
    byteO.reset ();
  }
}


Costruttore

Metodi



4.4.4.2) Interfaccia Recipient

E' una semplice interfaccia che dichiara un solo metodo, receive().

Esso ha come parametro un DataInputStream (che di fatto costituisce il corpo del messaggio) dal quale, con opportune letture, potrà essere acquisito il messaggio.

La definizione dell'interfaccia è la seguente (dal libro "Java Network Programming" di Merlin Hughes et al., Mannig Publications Co, Greenwich CT, 1997).

/* Copyright (c) 1996, 1997 Prominence Dot Com, Inc.        *
 * See the file legal.txt in the txt directory for details. */

package prominence.msg;

import java.io.*;

/**
 * The interface through which the <tt>DeliveryOutputStream</tt> delivers
 * messages.
 *
 * @version 1.0 1 Nov 1996
 * @author Merlin Hughes
 * @see prominence.msg.DeliveryOutputStream
 */
public interface Recipient {
  /**
   * Delivers a new message to the recipient.
   * @param in A <tt>DataInputStream</tt> from which the message contents can
   * be read
   */
  public void receive (DataInputStream in);
}


4.4.5) Client per la chatline grafica e testuale

Vediamo ora come è fatto il client per la chatline grafica e testuale; in seguito vedremo il server, che peraltro è molto semplice.

Come abbiamo già detto, il client è composto di due componenti distinte ed indipendenti, che comunicano con le loro controparti (grazie al server) per mezzo di multiplexing di messaggi.

Il client consiste di tre classi:


Figura 4-22: Interfaccia utente del client


4.4.5.1) Classe CollabTool

E' la classe principale (contiene il main()) ed ha diverse incombenze:

La definizione della classe è la seguente.

import java.io.*;
import java.awt.*;
import java.net.*;
import prominence.msg.*;

public class CollabTool extends Frame {
  protected static int id = 0;

  public CollabTool (InputStream i, OutputStream o) {
    super ("Collaborative Tool " + (id ++));
    setResizable(false);
    Whiteboard wb = new Whiteboard ();
    Chatboard cb = new Chatboard ();
    setLayout (new GridLayout (2, 1));
    add (wb);
    add (cb);
    resize (400, 500);

    MessageOutputStream mO = new MessageOutputStream (o);
    MessageInputStream mI = new MessageInputStream (i);

    cb.setMessageOutput (new MultiplexOutputStream (mO, "chat"));
    wb.setMessageOutput (new MultiplexOutputStream (mO, "wb"));

    Demultiplexer d = new Demultiplexer (new MultiplexInputStream (mI));
    d.register ("chat", cb.getMessageOutput ());
    d.register ("wb", wb.getMessageOutput ());
    d.start ();
  }

  static public void main (String args[]) throws IOException {
  
  Socket socket = new Socket(args[0], 5000);
  
    new CollabTool (socket.getInputStream(),socket.getOutputStream()).show ();
  }
}


Note

Costruttore


4.4.5.2) Classe ChatBoard

Questa classe implementa la componente testuale della chatline.

E' costituita da una TextArea in cui appaiono i messaggi precedenti e da un TextField in cui si immette di volta in volta un nuovo messaggio. Esso viene spedito non appena si preme il tasto Return dentro il TextField.

Nel contesto del client, questa componente dialoga direttamente con le corrispondenti componenti degli altri client collegati, ed ignora completamente l'esistenza della componente grafica.

Implementa l'interfaccia Runnable perché un thread gestisce l'interazione con l'utente (quello principale) mentre un thread separato (avviato con run()) si occupa di ricevere i messaggi che arrivano dalla connessione di rete.

La definizione della classe è la seguente (dal libro "Java Network Programming" di Merlin Hughes et al., Mannig Publications Co, Greenwich CT, 1997).

/* Copyright (c) 1996, 1997 Prominence Dot Com, Inc.        *
 * See the file legal.txt in the txt directory for details. */

import java.io.*;
import java.awt.*;
import prominence.msg.*;
import prominence.util.Queue;

public class Chatboard extends Panel implements Runnable {
  protected TextArea output;
  protected TextField input;
  protected Queue q;
  protected Thread exec;

  public Chatboard () {
    setLayout (new BorderLayout ());
    add ("Center", output = new TextArea ());
    output.setEditable (false);
    add ("South", input = new TextField ());
    q = new Queue ();
    exec = new Thread (this);
    exec.start ();
  }

  protected MessageOutput o;

  public void setMessageOutput (MessageOutput o) {
    this.o = o;
  }

  public MessageOutput getMessageOutput () {
    return new QueueOutputStream (q);
  }

  public boolean action (Event e, Object arg) {
    if (e.target == input) {
      try {
        o.writeUTF (input.getText ());
        o.send ();
      } catch (IOException ex) {
        ex.printStackTrace ();
      }
      output.appendText (input.getText () + "\n");
      input.setText ("");
      return true;
    }
    return super.action (e, arg);
  }

  public void sendMessage (String s) {
      try {
        o.writeUTF (s);
        o.send ();
      } catch (IOException ex) {
        ex.printStackTrace ();
      }
  }

  public void run () {
    QueueInputStream qI = new QueueInputStream (q);
    while (true) {
      try {
        qI.receive ();
        String msg = qI.readUTF ();
        output.appendText ("-- " + msg + "\n");
      } catch (IOException ex) {
        ex.printStackTrace ();
      }
    }
  }
}


Costruttore

Metodi


Figura 4-23: Collegamento degli stream per l'input di ChatBoard

4.4.5.3) Classe WhiteBoard

Questa classe implementa la componente grafica della chatline.

Offre una superficie sulla quale l'utente può disegnare a mano libera. Quando l'utente:

Nel contesto del client, anche questa componente dialoga con le corrispondenti componenti degli altri client ed ignora l'esistenza della componente ChatBoard.

Implementa l'interfaccia Recipient, e quindi implementa un metodo receive() per la ricezione immediata dei messaggi.

La definizione della classe è la seguente (dal libro "Java Network Programming" di Merlin Hughes et al., Mannig Publications Co, Greenwich CT, 1997).

/* Copyright (c) 1996, 1997 Prominence Dot Com, Inc.        *
 * See the file legal.txt in the txt directory for details. */

import java.io.*;
import java.awt.*;
import prominence.msg.*;

public class Whiteboard extends Canvas implements Recipient {
  public Whiteboard () {
    setBackground (new Color (255, 255, 204));
  }

  protected MessageOutput o;

  public void setMessageOutput (MessageOutput o) {
    this.o = o;
  }

  public boolean mouseDown (Event e, int x, int y) {
    transmit (x, y);
    return super.mouseDown (e, x, y);
  }

  public boolean mouseDrag (Event e, int x, int y) {
    scribble (x, y);
    transmit (x, y);
    return super.mouseDrag (e, x, y);
  }

  public boolean mouseUp (Event e, int x, int y) {
    scribble (x, y);
    transmit (x, y);
    try {
      o.send ();
    } catch (IOException ex) {
      ex.printStackTrace ();
    }
    return super.mouseUp (e, x, y);
  }

  protected int oX, oY;

  protected void transmit (int x, int y) {
    try {
      o.writeInt (x);
      o.writeInt (y);
    } catch (IOException ex) {
      ex.printStackTrace ();
    }
    oX = x;
    oY = y;
  }

  protected void scribble (int x, int y) {
    Graphics g = getGraphics ();
    g.drawLine (oX, oY, x, y); 
    g.dispose ();
  }

  public MessageOutput getMessageOutput () {
    return new DeliveryOutputStream (this);
  }

  public void receive (DataInputStream dI) {
    Graphics g = getGraphics ();
    try {
      int x0 = dI.readInt (), y0 = dI.readInt ();
      while (dI.available () > 0) {
        int x1 = dI.readInt (), y1 = dI.readInt ();
        g.drawLine (x0, y0, x1, y1);
        x0 = x1;
        y0 = y1;
      }
    } catch (IOException ex) {
      ex.printStackTrace ();
    }
    g.dispose ();
  }
}


Costruttore

Metodi


Figura 4-24: Collegamento degli stream per l'input di WhiteBoard

4.4.6) Server per la chatline grafica e testuale

E' il server a cui tutti i client si connettono. Invia in broadcast a tutti (tranne che al mittente) tutto ciò che riceve da uno qualunque di essi.

Concettualmente (e anche strutturalmente) è identico al server dell'esempio 9, solo che comunica attraverso l'uso di messaggi.

Si noti che i messaggi sono a loro volta pacchetti composti da:

Questo però il server lo ignora, e non ne preoccupa. Quindi, tale server continuerà a funzionare correttamente anche anche nel caso in cui ai client si dovessero aggiungere ultreriori componenti funzionali, purché anch'esse comunichino fra loro facendo uso di stream per il multiplexing dei messaggi.

Il codice è costituito da due classi. La prima, CollabServer, accetta richieste di connessione sul port 5000 e, ogni volta che ne arriva una, istanzia un oggetto della classe CollabHandler che si occupa di gestirla.

import java.net.*;
import java.io.*;
import java.util.*;

public class CollabServer {

//--------------------------------------------------

  public CollabServer() throws IOException {
  
    ServerSocket server = new ServerSocket(5000);
    System.out.println ("Accepting connections...");
    while(true) {
      Socket client = server.accept();
      System.out.println ("Accepted from " + client.getInetAddress());
      new CollabHandler(client).start();
    }
  }

//--------------------------------------------------

  public static void main(String args[]) throws IOException {
  
    new CollabServer();
  }

}


La seconda si occupa della gestione di una singola connessione e dell'invio a tutte le altre, in broadcast, dei dati provenienti da tale connessione.

import java.net.*;
import java.io.*;
import java.util.*;
import prominence.msg.*;

public class CollabHandler extends Thread {

  protected static Vector handlers = new Vector();
  protected Socket socket;
  protected MessageInputStream is;
  protected MessageOutputStream os;

//--------------------------------------------------

  public CollabHandler(Socket socket) throws IOException {

    this.socket = socket;
    is = new MessageInputStream(socket.getInputStream());
    os = new MessageOutputStream(socket.getOutputStream());
  }

//--------------------------------------------------

  public void run() {

    try {
      handlers.addElement(this); //e' un metodo sincronizzato
      while (true) {
        is.receive();
        byte[] buffer = new byte[is.available()];
        is.readFully(buffer);
        broadcast(this, buffer);
      }
    } catch(IOException ex) {
      ex.printStackTrace();
    } finally {
      handlers.removeElement(this); //e' un metodo sincronizzato
      try {
        socket.close();
      } catch(Exception ex) {
        ex.printStackTrace();
      }
    }
  }

//--------------------------------------------------

  protected static void broadcast(CollabHandler sender, byte[] buffer) {

    synchronized (handlers) { //ora nessuno puo' aggiungersi o abbandonare 
      Enumeration e = handlers.elements();
      while (e.hasMoreElements()) {
        CollabHandler c = (CollabHandler) e.nextElement();
        if (c != sender) {
          try {
            c.os.write(buffer);
            c.os.send();
          } catch(Exception ex) {
            c.stop();
          }
        }
      }
    }
  }
}


Note


4.5) Ulteriori estensioni di funzionalità tramite messaggi

Abbiamo visto come l'uso e la concatenazione di message stream costituisca un potente e versatile meccanismo per lo sviluppo di applicazioni di rete.

Con tali conoscenze in mente, è relativamente semplice procedere ad ulteriori estensioni di funzionalità, quali ad esempio una trasmissione di tipo multicast, ossia una trasmissione nella quale si specifica il sottoinsieme dei destinatari che deve ricevere il messaggio.

Tale funzionalità può essere ottenuta con una classe RoutingOutputStream, che avrà il compito di incapsulare ogni messaggio dotandolo di una CI costituita da un vettore di stringhe che specificano ciascuna un destinatario.


Figura 4-25: Uso di RoutingOutputStream per trasmissioni multicast,

All'altra estremità, tipicamente sul server, a valle di un MessageInputStream si potrà predisporre una corrispondente classe RoutingInputStream che estrae la lista di destinatari e la rende disponibile all'esterno, e una classe Router (che ha la funzione di thread copiatore) incaricata di inviare una copia di ogni messaggio solamente ai corrispondenti destinatari.


Figura 4-26: Router

Con riferimento alla specifica concatenazione illustrata nella figura 4-25, il messaggio che viene instradato dal Router è in realtà un pacchetto contenente la CI del MultiplexOutputStream, e cioè l'etichetta della componente applicativa che deve ricevere i dati.

Di conseguenza, a valle di ogni MessageOutput in uscita dal Router ci potrà essere un MessageInput, quindi un MultiplexInputStream ed infine un Demultiplexer che esamina tale etichetta e smista il messaggio alla corretta componente applicativa.

In tal modo è possibile costruire applicazioni di tipo client-server costituite da componenti indipendenti capaci di effettuare trasmissioni multicast, il che consente di soddisfare praticamente qualunque esigenza applicativa.


Torna al sommario