come modellare una connessione a una risorsa, con elaborazione di eventi rudimentali

1

Per il mio semplice client MUD sto usando Apache Telnet (no, non ssh). Un cliente di fango è una strana bestia:

Generally, a MUD client is a very basic telnet client that lacks VT100 terminal emulation and the capability to perform telnet negotiations. ... Standard features seen in most MUD clients include ANSI color support, aliases, triggers and scripting. - Wikipedia

Vorrei come per un Controller non ancora scritto per avere un riferimento a TelnetConnection e usarlo solo per un livello più alto di astrazione per I / O, ma tutti i tentativi di premio questa classe a parte non riescono per me. TelnetConnection sembra assumersi la responsabilità per tutto, e poi alcuni, e continua a crescere.

package telnet;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Deque;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Logger;
import org.apache.commons.net.telnet.TelnetClient;
import player.GameAction;
import player.DataFromRegex;
import player.Regex;

public class TelnetConnection implements Observer {

    private static Logger log = Logger.getLogger(TelnetConnection.class.getName());
    private TelnetClient telnetClient = new TelnetClient();
    private InputOutput inputOutput = new InputOutput();
    private Regex regexParser = new Regex();
    private DataFromRegex data = null;
    private Logic logic = new Logic();

    public TelnetConnection() {
        init();
    }

    private void init() {
        try {
            int port = 3000;
            InetAddress host = InetAddress.getByName("rainmaker.wunderground.com");
            telnetClient.connect(host, port);
            inputOutput.readWriteParse(telnetClient.getInputStream(), telnetClient.getOutputStream());
        } catch (SocketException ex) {
        } catch (IOException ex) {
        }
        inputOutput.addObserver(this);
    }

    private void sendAction(GameAction action) throws IOException {
        log.info(action.toString());
        byte[] actionBytes = action.getAction().getBytes();
        OutputStream outputStream = telnetClient.getOutputStream();
        outputStream.write(actionBytes);
        outputStream.write(13);
        outputStream.write(10);
        outputStream.flush();
    }

    private void sendActions(Deque<GameAction> gameActions) {
        while (!gameActions.isEmpty()) {
            GameAction action = gameActions.remove();
            try {
                sendAction(action);
            } catch (IOException ex) {
            }
        }
    }

    @Override
    public void update(Observable o, Object arg) {
        String line = null;
        if (o instanceof InputOutput) {
            line = inputOutput.getLine();
            log.fine(line);
            data = regexParser.parse(line);
            Deque<GameAction> gameActions = logic.getActions(data);
            sendActions(gameActions);
        }
    }

    public static void main(String[] args) {
        new TelnetConnection();
    }

}

Parte della difficoltà si trova nella classe InputOutput , che è adattata da IOUtil da Apache. (Per qualche ragione, non posso includere il link al codice sorgente di Apache.)

Una volta inviato il InputStream a InputOutput , è necessario richiamare utilizzando Observable per poi inviare i dati di stringa per la corrispondenza e la ricerca? E poi torna cosa dopo aver trovato e cercato il testo?

Il trucco è, in termini di I / O:

  1. input della console di acquisizione
  2. stampa l'output remoto dal server MUD alla console come un normale telnet
  3. un po 'di regex / etc sull'output remoto, concomitante con flussi aperti non terminati.

Quest'ultima esigenza sembra creare una strana bestia per la quale non posso prevedere il flusso di controllo e dati.

    
posta Thufir 16.09.2013 - 15:18
fonte

1 risposta

1

Se volevi semplificare i dettagli dell'implementazione nitty gritty in una classe chiamata TelnetConnection , penso che sarebbe senz'altro una saggia idea, tuttavia potrebbe essere che non puoi farlo con una sola classe. Potrebbe essere necessario avere una classe secondaria che si occupa dell'aspetto asincrono di questo. Chiamiamo questa classe TelnetObserver che implementa Observer .

In questo modo, è ancora asincrono, il che rende la vita difficile, quindi rendiamola sincrona. Crea TelnetObserver implementa Future<String> . L'idea è che chiami "get ()" ogni volta che sei pronto per la riga successiva di input (in attesa che l'input sia disponibile se necessario).

Ovviamente ciò non accade da solo, dobbiamo renderlo sincrono usando un'istanza di CountDownLatch . Lo scopo di CountDownLatch è di fare in modo che un thread attenda il completamento di un lavoro asincrono entro un determinato periodo di tempo. Ho trovato un esempio eccellente di questa implementazione qui .

Una volta ottenuto ciò, TelnetConnection gestirà la logica di connessione. Da lì, TelnetConnection potrebbe anche gestire l'analisi delle singole righe, ma non la consiglierei. Vorrei creare un'altra classe TelnetParser che lo fa per te, dato l'input da TelnetObserver . Il risultante Object verrebbe quindi restituito a qualsiasi listener per l'input. In alternativa sull'output, si utilizza lo stesso TelnetParser per convertire da Object a una stringa che TelnetObserver potrebbe inviare.

L'interfaccia di base per TelnetParser sarebbe probabilmente simile a questa:

public class TelnetParser {
    public Object parseFromString(String) {
        ...
    }
    public String parseToString(Object) {
        ...
    }
}

Il risultato finale è che si dispone di un oggetto modello che è possibile chiamare per ricevere input o inviare output. Questo restituisce il controllo al chiamante o, in questo caso, alla classe Controller ! Spero che aiuti. Buona fortuna per il tuo impegno!

    
risposta data 16.09.2013 - 15:47
fonte