FileDocCategorySizeDatePackage
Server.javaAPI DocExample8038Thu Aug 08 12:44:04 BST 1996None

Server.java

// This example is from the book _Java in a Nutshell_ by David Flanagan.
// Written by David Flanagan.  Copyright (c) 1996 O'Reilly & Associates.
// You may study, use, modify, and distribute this example for any purpose.
// This example is provided WITHOUT WARRANTY either expressed or implied.

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

public class Server extends Thread {
    public final static int DEFAULT_PORT = 6789;
    protected int port;
    protected ServerSocket listen_socket;
    protected ThreadGroup threadgroup;
    protected List connection_list;
    protected Vector connections;
    protected Vulture vulture;
    
    // Exit with an error message, when an exception occurs.
    public static void fail(Exception e, String msg) {
        System.err.println(msg + ": " +  e);
        System.exit(1);
    }
    
    // Create a ServerSocket to listen for connections on;  start the thread.
    public Server(int port) {
        // Create our server thread with a name.
        super("Server");
        if (port == 0) port = DEFAULT_PORT;
        this.port = port;
        try { listen_socket = new ServerSocket(port); }
        catch (IOException e) { fail(e, "Exception creating server socket"); }
        // Create a threadgroup for our connections
        threadgroup = new ThreadGroup("Server Connections");

        // Create a window to display our connections in
        Frame f = new Frame("Server Status");
        connection_list = new List();
        f.add("Center", connection_list);
        f.resize(400, 200);
        f.show();

        // Initialize a vector to store our connections in
        connections = new Vector();

        // Create a Vulture thread to wait for other threads to die.
        // It starts itself automatically.
        vulture = new Vulture(this);

        // Start the server listening for connections
        this.start();
    }
    
    // The body of the server thread.  Loop forever, listening for and
    // accepting connections from clients.  For each connection, 
    // create a Connection object to handle communication through the
    // new Socket.  When we create a new connection, add it to the
    // Vector of connections, and display it in the List.  Note that we
    // use synchronized to lock the Vector of connections.  The Vulture
    // class does the same, so the vulture won't be removing dead 
    // connections while we're adding fresh ones.
    public void run() {
        try {
            while(true) {
                Socket client_socket = listen_socket.accept();
                Connection c = new Connection(client_socket, threadgroup,
                                  3, vulture);
                // prevent simultaneous access.
                synchronized (connections) {
                    connections.addElement(c);
                    connection_list.addItem(c.toString());
                }
            }
        }
        catch (IOException e) {
            fail(e, "Exception while listening for connections");
        }
    }
    
    // Start the server up, listening on an optionally specified port
    public static void main(String[] args) {
        int port = 0;
        if (args.length == 1) {
            try { port = Integer.parseInt(args[0]); }
            catch (NumberFormatException e) { port = 0; }
        }
        new Server(port);
    }
}

// This class is the thread that handles all communication with a client
// It also notifies the Vulture when the connection is dropped.
class Connection extends Thread {
    static int connection_number = 0;
    protected Socket client;
    protected Vulture vulture;
    protected DataInputStream in;
    protected PrintStream out;

    // Initialize the streams and start the thread
    public Connection(Socket client_socket, ThreadGroup threadgroup,
              int priority, Vulture vulture) 
    {
        // Give the thread a group, a name, and a priority.
        super(threadgroup, "Connection-" + connection_number++);
        this.setPriority(priority);
        // Save our other arguments away
        client = client_socket;
        this.vulture = vulture;
        // Create the streams
        try { 
            in = new DataInputStream(client.getInputStream());
            out = new PrintStream(client.getOutputStream());
        }
        catch (IOException e) {
            try { client.close(); } catch (IOException e2) { ; }
            System.err.println("Exception while getting socket streams: " + e);
            return;
        }
        // And start the thread up
        this.start();
    }
    
    // Provide the service.
    // Read a line, reverse it, send it back.  
    public void run() {
        String line;
        StringBuffer revline;
        int len;

        // Send a welcome message to the client
        out.println("Line Reversal Server version 1.0");
        out.println("A service of O'Reilly & Associates");

        try {
            for(;;) {
                // read in a line
                line = in.readLine();
                if (line == null) break;
                // reverse it
                len = line.length();
                revline = new StringBuffer(len);
                for(int i = len-1; i >= 0; i--) 
                    revline.insert(len-1-i, line.charAt(i));
                // and write out the reversed line
                out.println(revline);
            }
        }
        catch (IOException e) { ; }
        // When we're done, for whatever reason, be sure to close
        // the socket, and to notify the Vulture object.  Note that
        // we have to use synchronized first to lock the vulture
        // object before we can call notify() for it.
        finally {
            try { client.close(); } catch (IOException e2) { ; }
            synchronized (vulture) { vulture.notify(); }
        }
    }

    // This method returns the string representation of the Connection.
    // This is the string that will appear in the GUI List.
    public String toString() {
        return this.getName() + " connected to: " 
            + client.getInetAddress().getHostName()
            + ":" + client.getPort();
    }
}

// This class waits to be notified that a thread is dying (exiting)
// and then cleans up the list of threads and the graphical list.
class Vulture extends Thread {
    protected Server server;

    protected Vulture(Server s) {
        super(s.threadgroup, "Connection Vulture");
        server = s;
        this.start();
    }

    // This is the method that waits for notification of exiting threads
    // and cleans up the lists.  It is a synchronized method, so it
    // acquires a lock on the `this' object before running.  This is 
    // necessary so that it can call wait() on this.  Even if the 
    // the Connection objects never call notify(), this method wakes up
    // every five seconds and checks all the connections, just in case.
    // Note also that all access to the Vector of connections and to
    // the GUI List component are within a synchronized block as well.
    // This prevents the Server class from adding a new conenction while
    // we're removing an old one.
    public synchronized void run() {
        for(;;) {
            try { this.wait(5000); } catch (InterruptedException e) { ; }
            // prevent simultaneous access
            synchronized(server.connections) {
                // loop through the connections
                for(int i = 0; i < server.connections.size(); i++) {
                    Connection c;
                    c = (Connection)server.connections.elementAt(i);
                    // if the connection thread isn't alive anymore, 
                    // remove it from the Vector and List.
                    if (!c.isAlive()) {
                        server.connections.removeElementAt(i);
                        server.connection_list.delItem(i);
                        i--;
                    }
                }
            }
        }
    }
}