PortRedirector.java

package fr.umlv.ji.tcp.redirector;
import java.io.*;
import java.net.*;
import java.util.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.logging.*;
/**
 * Classe du redirecteur de port TCP.
 */
public class PortRedirector implements Runnable {
  /** Port par défaut d'attachement du relais. */
  public static int DEFAULT_PORT = 8080;
  /** Journaliseur. */
  private final static Logger logger = 
    Logger.getLogger("fr.umlv.ji.tcp.redirector");
  /** Sélecteur pour les entrées-sorties non bloquantes. */
  private Selector selector;
  /** Affiche l'usage de la commande et termine le programme. */
  public static void usage() {
    System.err.println("Usage: java fr.umlv.ji.tcp.PortRedirector " +
               "<destAddress> <destPort> [<localPort>]");
    System.exit(1);
  }
  /** Démarre un relais qui redirige tout son trafic vers
      une socket dont l'adresse est passée en argument. */
  public static void main(String[] args)
    throws UnknownHostException, IOException {
    if (args.length>3 || args.length<2) usage();
    try {
      // Récupère l'adresse de la socket de redirection 
      int port = Integer.parseInt(args[1]);
      SocketAddress server = new InetSocketAddress(args[0], port);
      // Récupère le port local d'attachement
      int localPort = DEFAULT_PORT;
      if (args.length==3)
    localPort = Integer.parseInt(args[2]);
      // Initialisation du relais
      PortRedirector proxy = new PortRedirector(localPort, server);
      // Démarrage du relais
      proxy.run();
    } catch (NumberFormatException e) { usage(); }
  }
  /** Constructeur du relais. Attend les connexions sur le port
      localPort et redirige tout le trafic vers server.
      @param localPort port d'attachement local.
      @param server adresse de la socket de redirection. */
  public PortRedirector(int localPort, SocketAddress server)
    throws IOException {
    // Création du canal pour accepter les connexions
    ServerSocketChannel ssc = ServerSocketChannel.open();
    ServerSocket ss = ssc.socket();
    // Permet la réutilisation rapide du port d'attachement
    ss.setReuseAddress(true);
    // Attachement de la socket locale
    ss.bind(new InetSocketAddress(localPort));
    logger.info("Proxy started on " + ss.getLocalSocketAddress());
    // Place le canal en mode non bloquant
    ssc.configureBlocking(false);
    // Création du sélecteur
    selector = Selector.open();
    // Enregistre la socket auprès du sélecteur pour l'acceptation des
    // connexions, en lui attachant un objet représentant le traitement
    ssc.register(selector, SelectionKey.OP_ACCEPT,
         new AcceptHandler(server));
  }
  /** Méthode de lancement du relais. */
  public void run() {
    // Récupération de la vue des clefs sélectionnées
    Set keys = selector.selectedKeys();
    while (true) {
      try {
    // Appel bloquant sur le sélecteur
    selector.select();
    // Parcours des canaux prêts
    for (Iterator ki=keys.iterator(); ki.hasNext(); ) {
      SelectionKey sk = (SelectionKey) ki.next();
      // Récupération de l'objet de traitement
      Handler t = (Handler) sk.attachment();
      // Appel du traitement
      t.performs(sk);
    }
    keys.clear();    // Vide l'ensemble des canaux prêts
      } catch (Exception e) {
    logger.log(Level.WARNING,"run",e);
      }
    }
  }
}