Java/Network Protocol/Server

Материал из Java эксперт
Перейти к: навигация, поиск

A generic framework for a flexible, multi-threaded server

 
/*
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.ru/javaexamples2.
 */
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
/**
 * This class is a generic framework for a flexible, multi-threaded server. It
 * listens on any number of specified ports, and, when it receives a connection
 * on a port, passes input and output streams to a specified Service object
 * which provides the actual service. It can limit the number of concurrent
 * connections, and logs activity to a specified stream.
 */
public class Server {
  /**
   * A main() method for running the server as a standalone program. The
   * command-line arguments to the program should be pairs of servicenames and
   * port numbers. For each pair, the program will dynamically load the named
   * Service class, instantiate it, and tell the server to provide that
   * Service on the specified port. The special -control argument should be
   * followed by a password and port, and will start special server control
   * service running on the specified port, protected by the specified
   * password.
   */
  public static void main(String[] args) {
    try {
      if (args.length < 2) // Check number of arguments
        throw new IllegalArgumentException("Must specify a service");
      // Create a Server object that uses standard out as its log and
      // has a limit of ten concurrent connections at once.
      Server s = new Server(System.out, 10);
      // Parse the argument list
      int i = 0;
      while (i < args.length) {
        if (args[i].equals("-control")) { // Handle the -control arg
          i++;
          String password = args[i++];
          int port = Integer.parseInt(args[i++]);
          // add control service
          s.addService(new Control(s, password), port);
        } else {
          // Otherwise start a named service on the specified port.
          // Dynamically load and instantiate a Service class
          String serviceName = args[i++];
          Class serviceClass = Class.forName(serviceName);
          Service service = (Service) serviceClass.newInstance();
          int port = Integer.parseInt(args[i++]);
          s.addService(service, port);
        }
      }
    } catch (Exception e) { // Display a message if anything goes wrong
      System.err.println("Server: " + e);
      System.err.println("Usage: java Server "
          + "[-control <password> <port>] "
          + "[<servicename> <port> ... ]");
      System.exit(1);
    }
  }
  // This is the state for the server
  Map services; // Hashtable mapping ports to Listeners
  Set connections; // The set of current connections
  int maxConnections; // The concurrent connection limit
  ThreadGroup threadGroup; // The threadgroup for all our threads
  PrintWriter logStream; // Where we send our logging output to
  /**
   * This is the Server() constructor. It must be passed a stream to send log
   * output to (may be null), and the limit on the number of concurrent
   * connections.
   */
  public Server(OutputStream logStream, int maxConnections) {
    setLogStream(logStream);
    log("Starting server");
    threadGroup = new ThreadGroup(Server.class.getName());
    this.maxConnections = maxConnections;
    services = new HashMap();
    connections = new HashSet(maxConnections);
  }
  /**
   * A public method to set the current logging stream. Pass null to turn
   * logging off
   */
  public synchronized void setLogStream(OutputStream out) {
    if (out != null)
      logStream = new PrintWriter(out);
    else
      logStream = null;
  }
  /** Write the specified string to the log */
  protected synchronized void log(String s) {
    if (logStream != null) {
      logStream.println("[" + new Date() + "] " + s);
      logStream.flush();
    }
  }
  /** Write the specified object to the log */
  protected void log(Object o) {
    log(o.toString());
  }
  /**
   * This method makes the server start providing a new service. It runs the
   * specified Service object on the specified port.
   */
  public synchronized void addService(Service service, int port)
      throws IOException {
    Integer key = new Integer(port); // the hashtable key
    // Check whether a service is already on that port
    if (services.get(key) != null)
      throw new IllegalArgumentException("Port " + port
          + " already in use.");
    // Create a Listener object to listen for connections on the port
    Listener listener = new Listener(threadGroup, port, service);
    // Store it in the hashtable
    services.put(key, listener);
    // Log it
    log("Starting service " + service.getClass().getName() + " on port "
        + port);
    // Start the listener running.
    listener.start();
  }
  /**
   * This method makes the server stop providing a service on a port. It does
   * not terminate any pending connections to that service, merely causes the
   * server to stop accepting new connections
   */
  public synchronized void removeService(int port) {
    Integer key = new Integer(port); // hashtable key
    // Look up the Listener object for the port in the hashtable
    final Listener listener = (Listener) services.get(key);
    if (listener == null)
      return;
    // Ask the listener to stop
    listener.pleaseStop();
    // Remove it from the hashtable
    services.remove(key);
    // And log it.
    log("Stopping service " + listener.service.getClass().getName()
        + " on port " + port);
  }
  /**
   * This nested Thread subclass is a "listener". It listens for connections
   * on a specified port (using a ServerSocket) and when it gets a connection
   * request, it calls the servers addConnection() method to accept (or
   * reject) the connection. There is one Listener for each Service being
   * provided by the Server.
   */
  public class Listener extends Thread {
    ServerSocket listen_socket; // The socket to listen for connections
    int port; // The port we"re listening on
    Service service; // The service to provide on that port
    volatile boolean stop = false; // Whether we"ve been asked to stop
    /**
     * The Listener constructor creates a thread for itself in the
     * threadgroup. It creates a ServerSocket to listen for connections on
     * the specified port. It arranges for the ServerSocket to be
     * interruptible, so that services can be removed from the server.
     */
    public Listener(ThreadGroup group, int port, Service service)
        throws IOException {
      super(group, "Listener:" + port);
      listen_socket = new ServerSocket(port);
      // give it a non-zero timeout so accept() can be interrupted
      listen_socket.setSoTimeout(600000);
      this.port = port;
      this.service = service;
    }
    /***********************************************************************
     * This is the polite way to get a Listener to stop accepting
     * connections
     **********************************************************************/
    public void pleaseStop() {
      this.stop = true; // Set the stop flag
      this.interrupt(); // Stop blocking in accept()
      try {
        listen_socket.close();
      } // Stop listening.
      catch (IOException e) {
      }
    }
    /**
     * A Listener is a Thread, and this is its body. Wait for connection
     * requests, accept them, and pass the socket on to the addConnection
     * method of the server.
     */
    public void run() {
      while (!stop) { // loop until we"re asked to stop.
        try {
          Socket client = listen_socket.accept();
          addConnection(client, service);
        } catch (InterruptedIOException e) {
        } catch (IOException e) {
          log(e);
        }
      }
    }
  }
  /**
   * This is the method that Listener objects call when they accept a
   * connection from a client. It either creates a Connection object for the
   * connection and adds it to the list of current connections, or, if the
   * limit on connections has been reached, it closes the connection.
   */
  protected synchronized void addConnection(Socket s, Service service) {
    // If the connection limit has been reached
    if (connections.size() >= maxConnections) {
      try {
        // Then tell the client it is being rejected.
        PrintWriter out = new PrintWriter(s.getOutputStream());
        out.print("Connection refused; "
            + "the server is busy; please try again later.\n");
        out.flush();
        // And close the connection to the rejected client.
        s.close();
        // And log it, of course
        log("Connection refused to "
            + s.getInetAddress().getHostAddress() + ":"
            + s.getPort() + ": max connections reached.");
      } catch (IOException e) {
        log(e);
      }
    } else { // Otherwise, if the limit has not been reached
      // Create a Connection thread to handle this connection
      Connection c = new Connection(s, service);
      // Add it to the list of current connections
      connections.add(c);
      // Log this new connection
      log("Connected to " + s.getInetAddress().getHostAddress() + ":"
          + s.getPort() + " on port " + s.getLocalPort()
          + " for service " + service.getClass().getName());
      // And start the Connection thread to provide the service
      c.start();
    }
  }
  /**
   * A Connection thread calls this method just before it exits. It removes
   * the specified Connection from the set of connections.
   */
  protected synchronized void endConnection(Connection c) {
    connections.remove(c);
    log("Connection to " + c.client.getInetAddress().getHostAddress() + ":"
        + c.client.getPort() + " closed.");
  }
  /** Change the current connection limit */
  public synchronized void setMaxConnections(int max) {
    maxConnections = max;
  }
  /**
   * This method displays status information about the server on the specified
   * stream. It can be used for debugging, and is used by the Control service
   * later in this example.
   */
  public synchronized void displayStatus(PrintWriter out) {
    // Display a list of all Services that are being provided
    Iterator keys = services.keySet().iterator();
    while (keys.hasNext()) {
      Integer port = (Integer) keys.next();
      Listener listener = (Listener) services.get(port);
      out.print("SERVICE " + listener.service.getClass().getName()
          + " ON PORT " + port + "\n");
    }
    // Display the current connection limit
    out.print("MAX CONNECTIONS: " + maxConnections + "\n");
    // Display a list of all current connections
    Iterator conns = connections.iterator();
    while (conns.hasNext()) {
      Connection c = (Connection) conns.next();
      out.print("CONNECTED TO "
          + c.client.getInetAddress().getHostAddress() + ":"
          + c.client.getPort() + " ON PORT "
          + c.client.getLocalPort() + " FOR SERVICE "
          + c.service.getClass().getName() + "\n");
    }
  }
  /**
   * This class is a subclass of Thread that handles an individual connection
   * between a client and a Service provided by this server. Because each such
   * connection has a thread of its own, each Service can have multiple
   * connections pending at once. Despite all the other threads in use, this
   * is the key feature that makes this a multi-threaded server
   * implementation.
   */
  public class Connection extends Thread {
    Socket client; // The socket to talk to the client through
    Service service; // The service being provided to that client
    /**
     * This constructor just saves some state and calls the superclass
     * constructor to create a thread to handle the connection. Connection
     * objects are created by Listener threads. These threads are part of
     * the server"s ThreadGroup, so all Connection threads are part of that
     * group, too.
     */
    public Connection(Socket client, Service service) {
      super("Server.Connection:"
          + client.getInetAddress().getHostAddress() + ":"
          + client.getPort());
      this.client = client;
      this.service = service;
    }
    /**
     * This is the body of each and every Connection thread. All it does is
     * pass the client input and output streams to the serve() method of the
     * specified Service object. That method is responsible for reading from
     * and writing to those streams to provide the actual service. Recall
     * that the Service object has been passed from the Server.addService()
     * method to a Listener object to the addConnection() method to this
     * Connection object, and is now finally being used to provide the
     * service. Note that just before this thread exits it always calls the
     * endConnection() method to remove itself from the set of connections
     */
    public void run() {
      try {
        InputStream in = client.getInputStream();
        OutputStream out = client.getOutputStream();
        service.serve(in, out);
      } catch (IOException e) {
        log(e);
      } finally {
        endConnection(this);
      }
    }
  }
  /**
   * Here is the Service interface that we have seen so much of. It defines
   * only a single method which is invoked to provide the service. serve()
   * will be passed an input stream and an output stream to the client. It
   * should do whatever it wants with them, and should close them before
   * returning.
   * 
   * All connections through the same port to this service share a single
   * Service object. Thus, any state local to an individual connection must be
   * stored in local variables within the serve() method. State that should be
   * global to all connections on the same port should be stored in instance
   * variables of the Service class. If the same Service is running on more
   * than one port, there will typically be different Service instances for
   * each port. Data that should be global to all connections on any port
   * should be stored in static variables.
   * 
   * Note that implementations of this interface must have a no-argument
   * constructor if they are to be dynamically instantiated by the main()
   * method of the Server class.
   */
  public interface Service {
    public void serve(InputStream in, OutputStream out) throws IOException;
  }
  /**
   * A very simple service. It displays the current time on the server to the
   * client, and closes the connection.
   */
  public static class Time implements Service {
    public void serve(InputStream i, OutputStream o) throws IOException {
      PrintWriter out = new PrintWriter(o);
      out.print(new Date() + "\n");
      out.close();
      i.close();
    }
  }
  /**
   * This is another example service. It reads lines of input from the client,
   * and sends them back, reversed. It also displays a welcome message and
   * instructions, and closes the connection when the user enters a "." on a
   * line by itself.
   */
  public static class Reverse implements Service {
    public void serve(InputStream i, OutputStream o) throws IOException {
      BufferedReader in = new BufferedReader(new InputStreamReader(i));
      PrintWriter out = new PrintWriter(new BufferedWriter(
          new OutputStreamWriter(o)));
      out.print("Welcome to the line reversal server.\n");
      out.print("Enter lines.  End with a "." on a line by itself.\n");
      for (;;) {
        out.print("> ");
        out.flush();
        String line = in.readLine();
        if ((line == null) || line.equals("."))
          break;
        for (int j = line.length() - 1; j >= 0; j--)
          out.print(line.charAt(j));
        out.print("\n");
      }
      out.close();
      in.close();
    }
  }
  /**
   * This service is an HTTP mirror, just like the HttpMirror class
   * implemented earlier in this chapter. It echos back the client"s HTTP
   * request
   */
  public static class HTTPMirror implements Service {
    public void serve(InputStream i, OutputStream o) throws IOException {
      BufferedReader in = new BufferedReader(new InputStreamReader(i));
      PrintWriter out = new PrintWriter(o);
      out.print("HTTP/1.0 200 \n");
      out.print("Content-Type: text/plain\n\n");
      String line;
      while ((line = in.readLine()) != null) {
        if (line.length() == 0)
          break;
        out.print(line + "\n");
      }
      out.close();
      in.close();
    }
  }
  /**
   * This service demonstrates how to maintain state across connections by
   * saving it in instance variables and using synchronized access to those
   * variables. It maintains a count of how many clients have connected and
   * tells each client what number it is
   */
  public static class UniqueID implements Service {
    public int id = 0;
    public synchronized int nextId() {
      return id++;
    }
    public void serve(InputStream i, OutputStream o) throws IOException {
      PrintWriter out = new PrintWriter(o);
      out.print("You are client #: " + nextId() + "\n");
      out.close();
      i.close();
    }
  }
  /**
   * This is a non-trivial service. It implements a command-based protocol
   * that gives password-protected runtime control over the operation of the
   * server. See the main() method of the Server class to see how this service
   * is started.
   * 
   * The recognized commands are: password: give password; authorization is
   * required for most commands add: dynamically add a named service on a
   * specified port remove: dynamically remove the service running on a
   * specified port max: change the current maximum connection limit. status:
   * display current services, connections, and connection limit help: display
   * a help message quit: disconnect
   * 
   * This service displays a prompt, and sends all of its output to the user
   * in capital letters. Only one client is allowed to connect to this service
   * at a time.
   */
  public static class Control implements Service {
    Server server; // The server we control
    String password; // The password we require
    boolean connected = false; // Whether a client is already connected
    /**
     * Create a new Control service. It will control the specified Server
     * object, and will require the specified password for authorization
     * Note that this Service does not have a no argument constructor, which
     * means that it cannot be dynamically instantiated and added as the
     * other, generic services above can be.
     */
    public Control(Server server, String password) {
      this.server = server;
      this.password = password;
    }
    /**
     * This is the serve method that provides the service. It reads a line
     * the client, and uses java.util.StringTokenizer to parse it into
     * commands and arguments. It does various things depending on the
     * command.
     */
    public void serve(InputStream i, OutputStream o) throws IOException {
      // Setup the streams
      BufferedReader in = new BufferedReader(new InputStreamReader(i));
      PrintWriter out = new PrintWriter(o);
      String line; // For reading client input lines
      // Has the user has given the password yet?
      boolean authorized = false;
      // If there is already a client connected to this service, display
      // a message to this client and close the connection. We use a
      // synchronized block to prevent a race condition.
      synchronized (this) {
        if (connected) {
          out.print("ONLY ONE CONTROL CONNECTION ALLOWED.\n");
          out.close();
          return;
        } else
          connected = true;
      }
      // This is the main loop: read a command, parse it, and handle it
      for (;;) { // infinite loop
        out.print("> "); // Display a prompt
        out.flush(); // Make it appear right away
        line = in.readLine(); // Get the user"s input
        if (line == null)
          break; // Quit if we get EOF.
        try {
          // Use a StringTokenizer to parse the user"s command
          StringTokenizer t = new StringTokenizer(line);
          if (!t.hasMoreTokens())
            continue; // if input was empty
          // Get first word of the input and convert to lower case
          String command = t.nextToken().toLowerCase();
          // Now compare to each of the possible commands, doing the
          // appropriate thing for each command
          if (command.equals("password")) { // Password command
            String p = t.nextToken(); // Get the next word
            if (p.equals(this.password)) { // Is it the password?
              out.print("OK\n"); // Say so
              authorized = true; // Grant authorization
            } else
              out.print("INVALID PASSWORD\n"); // Otherwise fail
          } else if (command.equals("add")) { // Add Service command
            // Check whether password has been given
            if (!authorized)
              out.print("PASSWORD REQUIRED\n");
            else {
              // Get the name of the service and try to
              // dynamically load and instantiate it.
              // Exceptions will be handled below
              String serviceName = t.nextToken();
              Class serviceClass = Class.forName(serviceName);
              Service service;
              try {
                service = (Service) serviceClass.newInstance();
              } catch (NoSuchMethodError e) {
                throw new IllegalArgumentException(
                    "Service must have a "
                        + "no-argument constructor");
              }
              int port = Integer.parseInt(t.nextToken());
              // If no exceptions occurred, add the service
              server.addService(service, port);
              out.print("SERVICE ADDED\n"); // acknowledge
            }
          } else if (command.equals("remove")) { // Remove service
            if (!authorized)
              out.print("PASSWORD REQUIRED\n");
            else {
              int port = Integer.parseInt(t.nextToken());
              server.removeService(port); // remove the service
              out.print("SERVICE REMOVED\n"); // acknowledge
            }
          } else if (command.equals("max")) { // Set connection limit
            if (!authorized)
              out.print("PASSWORD REQUIRED\n");
            else {
              int max = Integer.parseInt(t.nextToken());
              server.setMaxConnections(max);
              out.print("MAX CONNECTIONS CHANGED\n");
            }
          } else if (command.equals("status")) { // Status Display
            if (!authorized)
              out.print("PASSWORD REQUIRED\n");
            else
              server.displayStatus(out);
          } else if (command.equals("help")) { // Help command
            // Display command syntax. Password not required
            out.print("COMMANDS:\n" + "\tpassword <password>\n"
                + "\tadd <service> <port>\n"
                + "\tremove <port>\n"
                + "\tmax <max-connections>\n" + "\tstatus\n"
                + "\thelp\n" + "\tquit\n");
          } else if (command.equals("quit"))
            break; // Quit command.
          else
            out.print("UNRECOGNIZED COMMAND\n"); // Error
        } catch (Exception e) {
          out.print("ERROR WHILE PARSING OR EXECUTING COMMAND:\n" + e
              + "\n");
        }
      }
      connected = false;
      out.close();
      in.close();
    }
  }
}





A multithreaded server

 
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MultiThreadServer implements Runnable {
  Socket csocket;
  MultiThreadServer(Socket csocket) {
    this.csocket = csocket;
  }
  public static void main(String args[]) throws Exception {
    ServerSocket ssock = new ServerSocket(1234);
    System.out.println("Listening");
    while (true) {
      Socket sock = ssock.accept();
      System.out.println("Connected");
      new Thread(new MultiThreadServer(sock)).start();
    }
  }
  public void run() {
    try {
      PrintStream pstream = new PrintStream(csocket.getOutputStream());
      for (int i = 100; i >= 0; i--) {
        pstream.println(i + " bottles of beer on the wall");
      }
      pstream.close();
      csocket.close();
    } catch (IOException e) {
      System.out.println(e);
    }
  }
}





A server can use specialized streams to deliver typed data

 
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class DataServer {
  public static void main(String args[]) throws Exception {
    ServerSocket ssock = new ServerSocket(1234);
    while (true) {
      System.out.println("Listening");
      Socket sock = ssock.accept();
      DataOutputStream dstream = new DataOutputStream(sock
          .getOutputStream());
      dstream.writeFloat(3.14159265f);
      dstream.close();
      sock.close();
    }
  }
}
class DataClient {
  public static void main(String[] args) throws Exception {
    Socket sock = new Socket(args[0], 1234);
    DataInputStream dis = new DataInputStream(sock.getInputStream());
    float f = dis.readFloat();
    System.out.println("PI=" + f);
    dis.close();
    sock.close();
  }
}





Base class to build multithreaded servers easily

 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
class MTServerBase extends Thread {
  protected Socket socket;
  public void run() {
    try {
      String s = "Server.";
      socket.getOutputStream().write(s.getBytes());
      socket.close();
    } catch (Exception e) {
      System.out.println(e);
    }
  }
  static public void startServer(int port, Class clobj) {
    ServerSocket ssock;
    Socket sock;
    try {
      ssock = new ServerSocket(port);
      while (true) {
        Socket esock = null;
        try {
          esock = ssock.accept();
          MTServerBase t = (MTServerBase) clobj.newInstance();
          t.socket = esock;
          t.start();
        } catch (Exception e) {
          try {
            esock.close();
          } catch (Exception ec) {
          }
        }
      }
    } catch (IOException e) {
    }
  }
}
public class UCServer extends MTServerBase {
  public void run() {
    try {
      InputStream is = socket.getInputStream();
      OutputStream os = socket.getOutputStream();
      while (true) {
        char c = (char) is.read();
        // end on Control+C or Control+D
        if (c == "\003" || c == "\004")
          break;
        os.write(Character.toUpperCase(c));
      }
      socket.close();
    } catch (Exception e) {
      System.out.println(e);
    }
  }
  public static void main(String[] args) {
    int n = Integer.parseInt(args[0]);
    System.out.println("Starting server on port " + n);
    startServer(n, UCServer.class);
  }
}





Client and Server Demo

 
/* From http://java.sun.ru/docs/books/tutorial/index.html */
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class EchoClient {
    public static void main(String[] args) throws IOException {
        Socket echoSocket = null;
        PrintWriter out = null;
        BufferedReader in = null;
        try {
            echoSocket = new Socket("taranis", 7);
            out = new PrintWriter(echoSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(
                                        echoSocket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Don"t know about host: taranis.");
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Couldn"t get I/O for "
                               + "the connection to: taranis.");
            System.exit(1);
        }
  BufferedReader stdIn = new BufferedReader(
                                   new InputStreamReader(System.in));
  String userInput;
  while ((userInput = stdIn.readLine()) != null) {
      out.println(userInput);
      System.out.println("echo: " + in.readLine());
  }
  out.close();
  in.close();
  stdIn.close();
  echoSocket.close();
    }
}
////////////////////////////////////////////////////////////

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class KnockKnockServer {
  public static void main(String[] args) throws IOException {
    ServerSocket serverSocket = null;
    try {
      serverSocket = new ServerSocket(4444);
    } catch (IOException e) {
      System.err.println("Could not listen on port: 4444.");
      System.exit(1);
    }
    Socket clientSocket = null;
    try {
      clientSocket = serverSocket.accept();
    } catch (IOException e) {
      System.err.println("Accept failed.");
      System.exit(1);
    }
    PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
    BufferedReader in = new BufferedReader(new InputStreamReader(
        clientSocket.getInputStream()));
    String inputLine, outputLine;
    KnockKnockProtocol kkp = new KnockKnockProtocol();
    outputLine = kkp.processInput(null);
    out.println(outputLine);
    while ((inputLine = in.readLine()) != null) {
      outputLine = kkp.processInput(inputLine);
      out.println(outputLine);
      if (outputLine.equals("Bye."))
        break;
    }
    out.close();
    in.close();
    clientSocket.close();
    serverSocket.close();
  }
}
class KnockKnockProtocol {
  private static final int WAITING = 0;
  private static final int SENTKNOCKKNOCK = 1;
  private static final int SENTCLUE = 2;
  private static final int ANOTHER = 3;
  private static final int NUMJOKES = 5;
  private int state = WAITING;
  private int currentJoke = 0;
  private String[] clues = { "Turnip", "Little Old Lady", "Atch", "Who",
      "Who" };
  private String[] answers = { "Turnip the heat, it"s cold in here!",
      "I didn"t know you could yodel!", "Bless you!",
      "Is there an owl in here?", "Is there an echo in here?" };
  public String processInput(String theInput) {
    String theOutput = null;
    if (state == WAITING) {
      theOutput = "Knock! Knock!";
      state = SENTKNOCKKNOCK;
    } else if (state == SENTKNOCKKNOCK) {
      if (theInput.equalsIgnoreCase("Who"s there?")) {
        theOutput = clues[currentJoke];
        state = SENTCLUE;
      } else {
        theOutput = "You"re supposed to say \"Who"s there?\"! "
            + "Try again. Knock! Knock!";
      }
    } else if (state == SENTCLUE) {
      if (theInput.equalsIgnoreCase(clues[currentJoke] + " who?")) {
        theOutput = answers[currentJoke] + " Want another? (y/n)";
        state = ANOTHER;
      } else {
        theOutput = "You"re supposed to say \"" + clues[currentJoke]
            + " who?\"" + "! Try again. Knock! Knock!";
        state = SENTKNOCKKNOCK;
      }
    } else if (state == ANOTHER) {
      if (theInput.equalsIgnoreCase("y")) {
        theOutput = "Knock! Knock!";
        if (currentJoke == (NUMJOKES - 1))
          currentJoke = 0;
        else
          currentJoke++;
        state = SENTKNOCKKNOCK;
      } else {
        theOutput = "Bye.";
        state = WAITING;
      }
    }
    return theOutput;
  }
}
////////////////////////////////////////////////////////////

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class KKMultiServer {
  public static void main(String[] args) throws IOException {
    ServerSocket serverSocket = null;
    boolean listening = true;
    try {
      serverSocket = new ServerSocket(4444);
    } catch (IOException e) {
      System.err.println("Could not listen on port: 4444.");
      System.exit(-1);
    }
    while (listening)
      new KKMultiServerThread(serverSocket.accept()).start();
    serverSocket.close();
  }
}
class KKMultiServerThread extends Thread {
  private Socket socket = null;
  public KKMultiServerThread(Socket socket) {
    super("KKMultiServerThread");
    this.socket = socket;
  }
  public void run() {
    try {
      PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
      BufferedReader in = new BufferedReader(new InputStreamReader(socket
          .getInputStream()));
      String inputLine, outputLine;
      KnockKnockProtocol kkp = new KnockKnockProtocol();
      outputLine = kkp.processInput(null);
      out.println(outputLine);
      while ((inputLine = in.readLine()) != null) {
        outputLine = kkp.processInput(inputLine);
        out.println(outputLine);
        if (outputLine.equals("Bye"))
          break;
      }
      out.close();
      in.close();
      socket.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
class KnockKnockProtocol {
  private static final int WAITING = 0;
  private static final int SENTKNOCKKNOCK = 1;
  private static final int SENTCLUE = 2;
  private static final int ANOTHER = 3;
  private static final int NUMJOKES = 5;
  private int state = WAITING;
  private int currentJoke = 0;
  private String[] clues = { "Turnip", "Little Old Lady", "Atch", "Who",
      "Who" };
  private String[] answers = { "Turnip the heat, it"s cold in here!",
      "I didn"t know you could yodel!", "Bless you!",
      "Is there an owl in here?", "Is there an echo in here?" };
  public String processInput(String theInput) {
    String theOutput = null;
    if (state == WAITING) {
      theOutput = "Knock! Knock!";
      state = SENTKNOCKKNOCK;
    } else if (state == SENTKNOCKKNOCK) {
      if (theInput.equalsIgnoreCase("Who"s there?")) {
        theOutput = clues[currentJoke];
        state = SENTCLUE;
      } else {
        theOutput = "You"re supposed to say \"Who"s there?\"! "
            + "Try again. Knock! Knock!";
      }
    } else if (state == SENTCLUE) {
      if (theInput.equalsIgnoreCase(clues[currentJoke] + " who?")) {
        theOutput = answers[currentJoke] + " Want another? (y/n)";
        state = ANOTHER;
      } else {
        theOutput = "You"re supposed to say \"" + clues[currentJoke]
            + " who?\"" + "! Try again. Knock! Knock!";
        state = SENTKNOCKKNOCK;
      }
    } else if (state == ANOTHER) {
      if (theInput.equalsIgnoreCase("y")) {
        theOutput = "Knock! Knock!";
        if (currentJoke == (NUMJOKES - 1))
          currentJoke = 0;
        else
          currentJoke++;
        state = SENTKNOCKKNOCK;
      } else {
        theOutput = "Bye.";
        state = WAITING;
      }
    }
    return theOutput;
  }
}
////////////////////////////////////////////////////////////

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class KnockKnockClient {
  public static void main(String[] args) throws IOException {
    Socket kkSocket = null;
    PrintWriter out = null;
    BufferedReader in = null;
    try {
      kkSocket = new Socket("taranis", 4444);
      out = new PrintWriter(kkSocket.getOutputStream(), true);
      in = new BufferedReader(new InputStreamReader(kkSocket
          .getInputStream()));
    } catch (UnknownHostException e) {
      System.err.println("Don"t know about host: taranis.");
      System.exit(1);
    } catch (IOException e) {
      System.err
          .println("Couldn"t get I/O for the connection to: taranis.");
      System.exit(1);
    }
    BufferedReader stdIn = new BufferedReader(new InputStreamReader(
        System.in));
    String fromServer;
    String fromUser;
    while ((fromServer = in.readLine()) != null) {
      System.out.println("Server: " + fromServer);
      if (fromServer.equals("Bye."))
        break;
      fromUser = stdIn.readLine();
      if (fromUser != null) {
        System.out.println("Client: " + fromUser);
        out.println(fromUser);
      }
    }
    out.close();
    in.close();
    stdIn.close();
    kkSocket.close();
  }
}





Client estimates the speed of the network connection to the server

 
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Calendar;
public class UdpEchoClient {
  static final String testString = "Greeks bearing gifts";
  public static void main(String[] args) {
    InetAddress address;
    try {
      address = InetAddress.getByName(args[0]);
    } catch (UnknownHostException host) {
      System.out.println(host);
      return;
    }
    DatagramPacket pack = new DatagramPacket(testString.getBytes(),
        testString.length(), address, 7);
    DatagramPacket incoming = new DatagramPacket(new byte[256], 256);
    DatagramSocket sock = null;
    try {
      Calendar start, end;
      sock = new DatagramSocket();
      start = Calendar.getInstance();
      sock.send(pack);
      sock.setSoTimeout(5000);
      sock.receive(incoming);
      end = Calendar.getInstance();
      String reply = new String(incoming.getData());
      reply = reply.substring(0, testString.length());
      if (reply.equals(testString)) {
        System.out.println("Success");
        System.out.println("Time = "
            + (end.getTime().getTime() - start.getTime().getTime())
            + "mS");
      } else
        System.out.println("Reply data did not match");
    } catch (SocketException socke) {
      System.out.println(socke);
    } catch (IOException ioe) {
      System.out.println(ioe);
    } finally {
      sock.close();
    }
  }
}





Logging Server based on SocketServer

 
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Main {
  public static void main(String args[]) throws Exception {
    ServerSocket serverSocket = new ServerSocket(8080);
    Socket socket = serverSocket.accept();
    InputStream inStream = socket.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
    String str = null;
    while ((str = reader.readLine()) != null) {
      System.out.println(str);
    }
  }
}





Manage a pool of threads for clients

 
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class PoolServerBase extends Thread {
  protected Socket socket;
  static PoolServerBase head = null;
  protected PoolServerBase next = null;
  static protected boolean init(Class clobj, int threads) {
    try {
      for (int i = 0; i < threads; i++) {
        PoolServerBase thread = (PoolServerBase) clobj.newInstance();
        thread.next = head;
        head = thread;
        thread.start();
      }
    } catch (Exception e) {
      return false;
    }
    return true;
  }
  static synchronized protected void addToList(PoolServerBase me) {
    me.next = head;
    head = me;
  }
  synchronized protected void waitForSignal() {
    try {
      wait();
    } catch (InterruptedException e) {
    }
  }
  public void run() {
    while (true) {
      waitForSignal();
      doClientProcessing();
      addToList(this);
    }
  }
  synchronized protected void handleClient(Socket s) {
    socket = s;
    notify();
  }
  protected void doClientProcessing() {
    try {
      String s = "Server.";
      s += "Thread: " + this.toString();
      socket.getOutputStream().write(s.getBytes());
      sleep(10000); // simulate processing
      s = "Complete";
      socket.getOutputStream().write(s.getBytes());
      socket.close();
    } catch (Exception e) {
      System.out.println(e);
    }
  }
  static synchronized protected boolean listEmpty() {
    return head == null;
  }
  static protected void assignThread(Socket sock) {
    PoolServerBase t = head;
    head = head.next;
    t.socket = sock;
    synchronized (t) {
      t.notify();
    }
  }
  static public void startServer(int port) {
    ServerSocket ssock;
    Socket sock;
    try {
      ssock = new ServerSocket(port);
      while (true) {
        Socket esock = null;
        try {
          esock = ssock.accept();
          while (listEmpty())
            yield();
          assignThread(esock);
        } catch (Exception e) {
          try {
            esock.close();
          } catch (Exception ec) {
          }
        }
      }
    } catch (IOException e) {
    }
  }
  static public void main(String args[]) {
    init(PoolServerBase.class, 3);
    System.out.println("Starting server on port 808");
    startServer(808);
  }
}





Quote Server

 
/* From http://java.sun.ru/docs/books/tutorial/index.html */
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Date;
public class QuoteServer {
  public static void main(String[] args) throws IOException {
    new QuoteServerThread().start();
  }
}
class QuoteServerThread extends Thread {
  private DatagramSocket socket = null;
  private DataInputStream in = null;
  private boolean moreQuotes = true;
  public QuoteServerThread() throws IOException {
    super("QuoteServerThread");
    socket = new DatagramSocket(4445);
    try {
      in = new DataInputStream(new FileInputStream("one-liners.txt"));
    } catch (FileNotFoundException e) {
      System.err
          .println("Could not open quote file. Serving time instead.");
    }
  }
  public void run() {
    while (moreQuotes) {
      try {
        byte[] buf = new byte[256];
        // receive request
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);
        // figure out response
        String dString = null;
        if (in == null)
          dString = new Date().toString();
        else
          dString = getNextQuote();
        dString.getBytes(0, dString.length(), buf, 0);
        // send the response to the client at "address" and "port"
        InetAddress address = packet.getAddress();
        int port = packet.getPort();
        packet = new DatagramPacket(buf, buf.length, address, port);
        socket.send(packet);
      } catch (IOException e) {
        e.printStackTrace();
        moreQuotes = false;
      }
    }
    socket.close();
  }
  private String getNextQuote() {
    String returnValue = null;
    try {
      if ((returnValue = in.readLine()) == null) {
        in.close();
        moreQuotes = false;
        returnValue = "No more quotes. Goodbye.";
      }
    } catch (IOException e) {
      returnValue = "IOException occurred in server.";
    }
    return returnValue;
  }
}
/////////////////////////////////////////////////
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class QuoteClient {
  public static void main(String[] args) throws IOException {
    if (args.length != 1) {
      System.out.println("Usage: java QuoteClient <hostname>");
      return;
    }
    // get a datagram socket
    DatagramSocket socket = new DatagramSocket();
    // send request
    byte[] buf = new byte[256];
    InetAddress address = InetAddress.getByName(args[0]);
    DatagramPacket packet = new DatagramPacket(buf, buf.length, address,
        4445);
    socket.send(packet);
    // get response
    packet = new DatagramPacket(buf, buf.length);
    socket.receive(packet);
    // display response
    String received = new String(packet.getData());
    System.out.println("Quote of the Moment: " + received);
    socket.close();
  }
}
/////////////////////////////////////////////////





Reflector

 
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
public class Reflector implements Runnable {
  // valid names found in the config file:
  public static final String MODE = "Mode";
  public static final String SOURCE_IP = "SourceIP";
  public static final String SOURCE_PORT = "SourcePort";
  public static final String DEST_IP = "DestIP";
  public static final String DEST_PORT = "DestPort";
  // valid modes in the config file, unicast to
  // multicast or multicast to unicast
  public static final String INPUT_UNITOMULTI = "UNI_TO_MULTI";
  public static final String INPUT_MULTITOUNI = "MULTI_TO_UNI";
  // possible modes the reflector can be set to:
  public static final int MODE_NONE = 0;
  public static final int MODE_UNI_TO_MULTI = 1;
  public static final int MODE_MULTI_TO_UNI = 2;
  // variables to indicate source or destination
  public static final int SOURCE = 1;
  public static final int DEST = 2;
  // min and max network ports allowed
  public static final int MIN_PORT = 1024;
  public static final int MAX_PORT = 65095;
  // which mode the reflector is being run in:
  private int mode = 0;
  // source and destination hold variables:
  private Address source;
  private Hashtable dest;
  private Address hold_dest = null;
  // logging toggle and logger class
  boolean logging = true;
  Logger logger;
  public Reflector() {
  }
  public void run() {
    // validate the config file
    if (readConfig() != 0) {
      System.err.println("Error parsing config file\n");
      System.exit(-1);
    }
    // start the logger
    logger = new Logger(logging);
    // spawn a thread to listen for packets
    ReflectorListener listener = new ReflectorListener(source, mode, logger);
    System.out.println("Listening on " + source.toString());
    // spawn threads for each source address packets
    // are to be forwarded on. Register each thread as
    // PacketListenerInterface with the listener thread.
    System.out.println("Sending on:");
    for (Enumeration e = dest.elements(); e.hasMoreElements();) {
      Address a = (Address) e.nextElement();
      ReflectorSender sender = new ReflectorSender(a, mode, logger);
      sender.start();
      listener.addPacketListener((PacketListenerInterface) sender);
      System.out.println("           " + a.toString());
    }
    // start the listener
    listener.start();
  }
  public int readConfig() {
    // validate the contents of the config file
    BufferedReader input = null;
    String name, value, inputLine = null;
    dest = new Hashtable();
    // open and read the config file
    try {
      input = new BufferedReader(new FileReader("reflector.conf"));
      inputLine = input.readLine();
    } catch (IOException e) {
      System.err.println("Error reading reflector.conf.");
      return (-1);
    }
    // loop until entire config file is read
    while (inputLine != null) {
      // skip comments:
      if (inputLine.charAt(0) != "#") {
        // extract a name/value pair, and branch
        // based on the name:
        StringTokenizer tokenizer = new StringTokenizer(inputLine, "=");
        name = tokenizer.nextToken();
        value = tokenizer.nextToken();
        if (name == null) {
          System.out.println("no name");
          continue;
        } else if (name.equals(MODE)) {
          if (setMode(value) != 0) {
            System.err.println("Error setting mode to " + value);
            return (-1);
          }
        } else if (name.equals(SOURCE_IP)) {
          if (setSourceIP(value) != 0) {
            System.err.println("Error setting src IP address to "
                + value);
            return (-1);
          }
        } else if (name.equals(SOURCE_PORT)) {
          if (setSourcePort(value) != 0) {
            System.err
                .println("Error setting src port to " + value);
            return (-1);
          }
        } else if (name.equals(DEST_IP)) {
          if (setDestIP(value) != 0) {
            System.err.println("Error setting dest IP address to "
                + value);
            return (-1);
          }
        } else if (name.equals(DEST_PORT)) {
          if (setDestPort(value) != 0) {
            System.err.println("Error setting dest port to "
                + value);
            return (-1);
          }
        } else {
          System.err.println("Skipping invalid config file value: "
              + name);
        }
      }
      // read next line in the config file
      try {
        inputLine = input.readLine();
      } catch (IOException e) {
        System.err.println("Error reading reflector.conf.");
        return (-1);
      }
    }
    // close the config file
    try {
      input.close();
    } catch (IOException e) {
      System.err.println("Error closing reflector.conf.");
      return (-1);
    }
    // validate that the combined contents of the config file
    // make sense
    if (!isConfigValid()) {
      System.err.println("Configuration file is not complete.");
      return (-1);
    }
    return (0);
  }
  private int setMode(String value) {
    // validate and set the mode from the config file
    if (value.equals(INPUT_UNITOMULTI)) {
      mode = MODE_UNI_TO_MULTI;
      return (0);
    } else if (value.equals(INPUT_MULTITOUNI)) {
      mode = MODE_MULTI_TO_UNI;
      return (0);
    } else {
      return (-1);
    }
  }
  private int setSourceIP(String value) {
    // validate and set the source IP from the config file
    // call modeToAddress to validate IP address
    InetAddress inet = modeToAddress(value, SOURCE);
    if (inet == null)
      return -1;
    if (source != null) {
      if (source.getAddress() != null)
        System.err.println("Warning: overwriting src address "
            + source.getAddress().getHostAddress() + " with "
            + inet.getHostAddress() + ".");
      source.setAddress(inet);
    } else {
      source = new Address(inet);
    }
    return (0);
  }
  private int setSourcePort(String value) {
    // validate and set the source port from the config file
    int port;
    try {
      port = Integer.parseInt(value);
    } catch (NumberFormatException nfe) {
      return (-1);
    }
    if ((port < MIN_PORT) || (port > 65095))
      return (-1);
    if (source != null) {
      if (source.getPort() != 0)
        System.err.println("Warning: overwriting src port "
            + source.getPort() + " with port " + port + ".");
      source.setPort(port);
    } else {
      source = new Address(port);
    }
    return (0);
  }
  private int setDestIP(String value) {
    // validate and set the dest IP from the config file
    // call modeToAddress to validate IP address
    InetAddress inet = modeToAddress(value, DEST);
    if (inet == null)
      return -1;
    if (hold_dest != null) {
      if (hold_dest.getAddress() != null)
        System.err.println("Warning: overwriting dest address "
            + hold_dest.getAddress().getHostAddress() + " with "
            + inet.getHostAddress() + ".");
      hold_dest.setAddress(inet);
      if (hold_dest.isComplete())
        return (addDest());
    } else {
      hold_dest = new Address(inet);
    }
    return (0);
  }
  private int setDestPort(String value) {
    // validate and set the dest port from the config file
    int port;
    try {
      port = Integer.parseInt(value);
    } catch (NumberFormatException nfe) {
      return (-1);
    }
    if ((port < MIN_PORT) || (port > MAX_PORT))
      return (-1);
    if (hold_dest != null) {
      if (hold_dest.getPort() != 0)
        System.err.println("Warning: overwriting dest port "
            + hold_dest.getPort() + " with port " + port + ".");
      hold_dest.setPort(port);
      if (hold_dest.isComplete())
        return (addDest());
    } else {
      hold_dest = new Address(port);
    }
    return (0);
  }
  private int addDest() {
    // once both a dest IP and port have been read, add them
    // to our vector of all destinations.
    switch (mode) {
    case MODE_UNI_TO_MULTI:
      if (!dest.isEmpty()) {
        System.err.println("Warning: dest address overwritten");
        dest.clear();
      }
      dest.put(hold_dest.toString(), hold_dest);
      break;
    case MODE_MULTI_TO_UNI:
      dest.put(hold_dest.toString(), hold_dest);
      break;
    default:
      // no mode set
      System.err.println("Destination " + hold_dest.toString()
          + " skipped because no mode set.");
      hold_dest = null;
      return (-1);
    }
    hold_dest = null;
    return (0);
  }
  private InetAddress modeToAddress(String value, int type) {
    // validate the IP Address based on its text value, its
    // type (DEST or SOURCE), and the mode (UNI_TO_MULTI or
    // MULTI_TO_UNI). Returns an InetAddress if succesfull and
    // null on failure.
    InetAddress inet;
    if ((type != DEST) && (type != SOURCE)) {
      System.err.println("Invalid type passed to modeToAddress (" + type
          + ")");
      return (null);
    }
    switch (mode) {
    case MODE_UNI_TO_MULTI:
      if (type == DEST)
        inet = returnValidMCIP(value);
      else
        inet = returnValidIP(value);
      break;
    case MODE_MULTI_TO_UNI:
      if (type == DEST)
        inet = returnValidIP(value);
      else
        inet = returnValidMCIP(value);
      break;
    default:
      // no mode set
      System.err.println("Error: No Mode Selected.");
      return (null);
    }
    if (inet == null)
      System.err.println("Invalid dest IP address (" + value + ").");
    return (inet);
  }
  private InetAddress returnValidIP(String IP) {
    // return InetAddress if IP is valid, null otherwise
    InetAddress inet;
    try {
      inet = InetAddress.getByName(IP);
    } catch (UnknownHostException e) {
      return (null);
    }
    return (inet);
  }
  private InetAddress returnValidMCIP(String IP) {
    // return InetAddress if IP is valid multicast addr,
    // null otherwise
    InetAddress inet = returnValidIP(IP);
    if (inet.isMulticastAddress()) {
      return (inet);
    } else {
      return (null);
    }
  }
  public boolean isConfigValid() {
    // validate that the mode, source IP/port, and
    // dest IP(s)/port(s) are all valid and a valid
    // combination.
    if (mode == MODE_NONE) {
      System.err.println("No mode selected.");
      return (false);
    }
    if (!source.isComplete()) {
      if ((source.getPort() != 0) && (mode == MODE_UNI_TO_MULTI)) {
        // if source is unicast local IP is implied
        try {
          source.setAddress(InetAddress.getLocalHost());
        } catch (UnknownHostException e) {
          System.err.println("Incomplete source address.");
          return (false);
        }
      } else {
        System.err.println("Incomplete source address.");
        return (false);
      }
    }
    if (dest.isEmpty()) {
      System.err.println("No destination addresses.");
      return (false);
    }
    for (Enumeration e = dest.elements(); e.hasMoreElements();) {
      Address a = (Address) e.nextElement();
      if (!a.isComplete()) {
        System.err.println("Incompete destination address.");
        return (false);
      }
    }
    return (true);
  }
  public static void main(String args[]) {
    Reflector r = new Reflector();
    r.run();
  }
}
//Address class is used to store an IP address and port
//combination.
class Address {
  private InetAddress address = null;
  private int port = 0;
  public Address(InetAddress address, int port) {
    this.address = address;
    this.port = port;
  }
  public Address(InetAddress address) {
    this.address = address;
  }
  public Address(int port) {
    this.port = port;
  }
  public InetAddress getAddress() {
    return (address);
  }
  public int getPort() {
    return (port);
  }
  public void setPort(int port) {
    this.port = port;
  }
  public void setAddress(InetAddress address) {
    this.address = address;
  }
  public boolean isComplete() {
    // return true if both IP and port are populated,
    // false otherwise.
    if ((address != null) && (port != 0))
      return (true);
    else
      return (false);
  }
  public String toString() {
    // return a string representation of the IP/port.
    String str;
    if (address == null)
      str = "";
    else
      str = address.getHostAddress();
    str = str + "/" + port;
    return (str);
  }
}
//Logger class opens and writes to the log file
//if boolean true is passed as constructor argument.
class Logger {
  private boolean logging;
  private FileWriter logfile;
  public Logger(boolean logging) {
    this.logging = logging;
    if (logging) {
      try {
        // open logfile for append
        logfile = new FileWriter("reflector.log", true);
      } catch (IOException e) {
        System.err.println("Error opening log file.");
      }
      log("Reflector started: " + new Date());
    }
  }
  public void log(String str) {
    // write string to logfile
    // if logging disabled return
    if (!logging)
      return;
    try {
      logfile.write(str + "\n");
      logfile.flush();
    } catch (IOException e) {
      System.err.println("Error writing to log file.");
    }
  }
}
//ReflectorSender creates a unicast or multicast socket
//and is registered to receive incoming packet notifications
//via the PacketListenerInterface. Incoming packets
//are forwarded on the outgoing socket.
class ReflectorSender extends Thread implements PacketListenerInterface {
  private InetAddress sendAddr;
  private int sendPort;
  private int mode;
  private DatagramSocket ds = null;
  private Logger logger;
  public ReflectorSender(Address a, int mode, Logger logger) {
    sendAddr = a.getAddress();
    sendPort = a.getPort();
    this.mode = mode;
    this.logger = logger;
  }
  public void run() {
    // initialize a DatagramSocket or MulticastSocket
    // based on the mode:
    switch (mode) {
    case Reflector.MODE_MULTI_TO_UNI:
      ds = initUnicastSocket();
      break;
    case Reflector.MODE_UNI_TO_MULTI:
      ds = (DatagramSocket) initMulticastSocket();
      break;
    default:
      break;
    }
  }
  private MulticastSocket initMulticastSocket() {
    // initialize a MulticastSocket
    MulticastSocket mc;
    try {
      mc = new MulticastSocket(sendPort);
    } catch (Exception e) {
      e.printStackTrace();
      return (null);
    }
    return (mc);
  }
  private DatagramSocket initUnicastSocket() {
    // initialize a DatagramSocket
    DatagramSocket ds;
    try {
      ds = new DatagramSocket(sendPort);
    } catch (Exception e) {
      e.printStackTrace();
      return (null);
    }
    return (ds);
  }
  public void packetReceived(DatagramPacket packet) {
    // An incoming packet has been received. Override
    // the old packet addressing to the new outgoing
    // addressing, send it and log it.
    try {
      packet.setAddress(sendAddr);
      packet.setPort(sendPort);
      ds.send(packet);
      logger.log("Packet forwarded to "
          + packet.getAddress().getHostAddress() + "/"
          + packet.getPort() + ", " + packet.getLength() + " bytes");
    } catch (IOException e) {
      System.err.println("Error sending packet");
      e.printStackTrace();
    }
  }
}
//ReflectorListener thread listens for packets
//and notifies one or more interested threads
//who register as PacketListenerInterfaces.
class ReflectorListener extends Thread {
  private InetAddress listenAddr;
  private int listenPort;
  private int mode;
  private Vector packetListeners;
  private Logger logger;
  private static final int MAX_PACKET_SIZE = 1500;
  public ReflectorListener(Address a, int mode, Logger logger) {
    listenAddr = a.getAddress();
    listenPort = a.getPort();
    this.mode = mode;
    this.logger = logger;
    packetListeners = new Vector();
  }
  public void run() {
    // create a unicast or multicast socket
    // depending on the mode and listen for packets.
    switch (mode) {
    case Reflector.MODE_UNI_TO_MULTI:
      DatagramSocket ds = initUnicastSocket();
      if (ds != null)
        listen(ds);
      break;
    case Reflector.MODE_MULTI_TO_UNI:
      MulticastSocket mc = initMulticastSocket();
      if (mc != null)
        listen((DatagramSocket) mc);
      break;
    default:
      break;
    }
  }
  private MulticastSocket initMulticastSocket() {
    // initialize a MulticastSocket and join the group
    MulticastSocket mc;
    try {
      mc = new MulticastSocket(listenPort);
      mc.joinGroup(listenAddr);
    } catch (Exception e) {
      System.err.println("Failed to create MulticastSocket on " + "port "
          + listenPort);
      return (null);
    }
    return (mc);
  }
  private DatagramSocket initUnicastSocket() {
    // initialize a DatagramSocket
    DatagramSocket ds;
    try {
      ds = new DatagramSocket(listenPort);
    } catch (Exception e) {
      System.err.println("Failed to create DatagramSocket on " + "port "
          + listenPort);
      return (null);
    }
    return (ds);
  }
  private void listen(DatagramSocket ds) {
    // loop forever listening to packets, when they
    // arrive log them and notify all interested threads.
    byte[] buffer;
    DatagramPacket packet;
    while (true) {
      try {
        buffer = new byte[MAX_PACKET_SIZE];
        packet = new DatagramPacket(buffer, buffer.length);
        ds.receive(packet);
        logger.log("Packet received, " + packet.getLength() + " bytes");
        eventNotify(packet);
      } catch (IOException e) {
        System.err.println("Error receiving packet\n");
        e.printStackTrace();
      }
    }
  }
  public void addPacketListener(PacketListenerInterface pl) {
    // add interested thread to listeners vector
    packetListeners.addElement(pl);
  }
  public void removePacketListener(PacketListenerInterface pl) {
    // remove thread to listeners vector
    packetListeners.removeElement(pl);
  }
  private void eventNotify(DatagramPacket packet) {
    // notify all registered threads that a packet has arrived
    // using the packetReceived(DatagramPacket) method.
    for (Enumeration e = packetListeners.elements(); e.hasMoreElements();) {
      PacketListenerInterface pl = (PacketListenerInterface) e
          .nextElement();
      pl.packetReceived(packet);
    }
  }
}
//PacketListenerInterface used by threads that need to
//be notified of datagram packet receipt. A single
//interface function packetReceived passes the packet
//information to the thread requiring the information.
interface PacketListenerInterface {
  public void packetReceived(DatagramPacket packet);
}





Serve entire objects using ObjectOutputStream

 
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Hashtable;
public class ObjServer {
  public static void main(String args[]) throws Exception {
    ServerSocket ssock = new ServerSocket(1234);
    Hashtable hash = new Hashtable();
    hash.put("Java Source and Support", "www.jexp.ru");
    while (true) {
      System.out.println("Listening");
      Socket sock = ssock.accept();
      ObjectOutputStream ostream = new ObjectOutputStream(sock
          .getOutputStream());
      ostream.writeObject(hash);
      ostream.close();
      sock.close();
    }
  }
}
// A Java-specific client can read objects from Java servers.
class ObjClient {
  public static void main(String[] args) throws Exception {
    Socket sock = new Socket(args[0], 1234);
    ObjectInputStream ois = new ObjectInputStream(sock.getInputStream());
    Hashtable hash = (Hashtable) ois.readObject();
    System.out.println(hash);
    ois.close();
    sock.close();
  }
}





Server allows connections on socket 6123

 
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketDemo {
  public static void main(String[] args) {
    try {
      ServerSocket server = new ServerSocket(6123);
      while (true) {
        System.out.println("Listening");
        Socket sock = server.accept();
        InetAddress addr = sock.getInetAddress();
        System.out.println("Connection made to " + addr.getHostName()
            + " (" + addr.getHostAddress() + ")");
        pause(5000);
        sock.close();
      }
    } catch (IOException e) {
      System.out.println("Exception detected: " + e);
    }
  }
  private static void pause(int ms) {
    try {
      Thread.sleep(ms);
    } catch (InterruptedException e) {
    }
  }
}





The client can specify information to control the output of a server

 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class AnotherBeerServer {
  public static void main(String args[]) throws Exception {
    ServerSocket ssock = new ServerSocket(1234);
    System.out.println("Listening");
    Socket sock = ssock.accept();
    ssock.close(); // no more connects
    PrintStream ps = new PrintStream(sock.getOutputStream());
    // ask for count
    ps.print("count? ");
    BufferedReader input = new BufferedReader(new InputStreamReader(sock
        .getInputStream()));
    // read and parse it
    String line = input.readLine();
    ps.println("");
    int count = Integer.parseInt(line);
    for (int i = count; i >= 0; i--) {
      ps.println(i + " Java Source and Support.");
    }
    ps.close();
    sock.close();
  }
}





This server displays messages to a single client

 
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class BeerServer {
  public static void main(String args[]) throws Exception {
    ServerSocket ssock = new ServerSocket(1234);
    System.out.println("Listening");
    Socket sock = ssock.accept();
    ssock.close(); // no more connects
    PrintStream ps = new PrintStream(sock.getOutputStream());
    for (int i = 100; i >= 0; i--) {
      ps.println(i + " from Java Source and Support.");
    }
    ps.close();
    sock.close();
  }
}





This server retrieves the time using the RFC867 protocol.

 
/*
Chapter 3 - Simple Protocols
Java 2 Network Protocols Black Book
by Al Williams    
Paraglyph Press 2001
*/

import java.net.*;
import java.io.*;
public class Time867 {
    public static void main(String[] args) {
        String hostname = "tock.usno.navy.mil";
        if (args.length==1) hostname=args[0];
        try {
        int c;
        Socket sock=new Socket(hostname,13);
        InputStream is = sock.getInputStream();
        do {
          c=is.read();
          if (c!=-1) System.out.print((char)c);
           } while (c!=-1);
        }
        catch (IOException e) { System.out.println(e); }
    }
}





URL Connection Test

 
/**
 * version 1.00 1999-08-27 author Cay Horstmann
 */
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLConnectionTest {
  public static void main(String[] args) {
    try {
      String urlName;
      if (args.length > 0)
        urlName = args[0];
      else
        urlName = "http://java.sun.ru";
      URL url = new URL(urlName);
      URLConnection connection = url.openConnection();
      // set username, password if specified on command line
      if (args.length > 2) {
        String username = args[1];
        String password = args[2];
        String input = username + ":" + password;
        String encoding = base64Encode(input);
        connection.setRequestProperty("Authorization", "Basic "
            + encoding);
      }
      connection.connect();
      // print header fields
      int n = 1;
      String key;
      while ((key = connection.getHeaderFieldKey(n)) != null) {
        String value = connection.getHeaderField(n);
        System.out.println(key + ": " + value);
        n++;
      }
      // print convenience functions
      System.out.println("----------");
      System.out
          .println("getContentType: " + connection.getContentType());
      System.out.println("getContentLength: "
          + connection.getContentLength());
      System.out.println("getContentEncoding: "
          + connection.getContentEncoding());
      System.out.println("getDate: " + connection.getDate());
      System.out.println("getExpiration: " + connection.getExpiration());
      System.out.println("getLastModifed: "
          + connection.getLastModified());
      System.out.println("----------");
      BufferedReader in = new BufferedReader(new InputStreamReader(
          connection.getInputStream()));
      // print first ten lines of contents
      String line;
      n = 1;
      while ((line = in.readLine()) != null && n <= 10) {
        System.out.println(line);
        n++;
      }
      if (line != null)
        System.out.println(". . .");
    } catch (IOException exception) {
      System.out.println("Error: " + exception);
    }
  }
  public static String base64Encode(String s) {
    ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    Base64OutputStream out = new Base64OutputStream(bOut);
    try {
      out.write(s.getBytes());
      out.flush();
    } catch (IOException exception) {
    }
    return bOut.toString();
  }
}
/*
 * BASE64 encoding encodes 3 bytes into 4 characters.
 * |11111122|22223333|33444444| Each set of 6 bits is encoded according to the
 * toBase64 map. If the number of input bytes is not a multiple of 3, then the
 * last group of 4 characters is padded with one or two = signs. Each output
 * line is at most 76 characters.
 */
class Base64OutputStream extends FilterOutputStream {
  public Base64OutputStream(OutputStream out) {
    super(out);
  }
  public void write(int c) throws IOException {
    inbuf[i] = c;
    i++;
    if (i == 3) {
      super.write(toBase64[(inbuf[0] & 0xFC) >> 2]);
      super.write(toBase64[((inbuf[0] & 0x03) << 4)
          | ((inbuf[1] & 0xF0) >> 4)]);
      super.write(toBase64[((inbuf[1] & 0x0F) << 2)
          | ((inbuf[2] & 0xC0) >> 6)]);
      super.write(toBase64[inbuf[2] & 0x3F]);
      col += 4;
      i = 0;
      if (col >= 76) {
        super.write("\n");
        col = 0;
      }
    }
  }
  public void flush() throws IOException {
    if (i == 1) {
      super.write(toBase64[(inbuf[0] & 0xFC) >> 2]);
      super.write(toBase64[(inbuf[0] & 0x03) << 4]);
      super.write("=");
      super.write("=");
    } else if (i == 2) {
      super.write(toBase64[(inbuf[0] & 0xFC) >> 2]);
      super.write(toBase64[((inbuf[0] & 0x03) << 4)
          | ((inbuf[1] & 0xF0) >> 4)]);
      super.write(toBase64[(inbuf[1] & 0x0F) << 2]);
      super.write("=");
    }
  }
  private static char[] toBase64 = { "A", "B", "C", "D", "E", "F", "G", "H",
      "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
      "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h",
      "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
      "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7",
      "8", "9", "+", "/" };
  private int col = 0;
  private int i = 0;
  private int[] inbuf = new int[3];
}