// 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--;
}
}
}
}
}
}
|