import java.io.*; import java.net.*; import java.util.*; /** * TalkServer - Server class for the NetTalk application. This class supports * a TCP/IP based server that uses the Java client/server socket paradigm to * communicate with a number of clients. * * @author Jayme Manning * @author Paul Kronenwetter * * @version * $Revision: 1.1 $
* $Date: 1998/12/21 06:37:43 $

* *

 * $Log: TalkServer.java,v $
 * Revision 1.1  1998/12/21 06:37:43  cps606
 * TalkServer - This is the new Server Class for the NetTalk application.
 * This is a multi-threaded server, where the thread is represented by an inner
 * class that extends the base thread class.  This class implements the following
 * Client/Server protocol:
 *
 * Server Send
 * ---------------
 * COMMAND: CLEARUSERS     - Signal the client that a new list of users
 *                           is coming. (See COMMAND: USER:)
 * COMMAND: RMUSER: {user} - Specify one user has been removed from the
 *                           clients vector.
 * COMMAND: USER: {user}   - Specify one user will be added to the
 *                           clients vector.
 * MESSAGE: {user}: {text} - The message received modified to add the
 *                           user who sent it.
 *
 * Server Receive
 * ---------------
 * COMMAND: SENDUSERS      - Process a user list refresh.
 * MESSAGE: {text}         - The message to be sent to all.
 * MESSAGETO: {user}: {text} - The message to be sent to a particular
 *                           person.
 *
 * Client Send
 * ---------------
 * COMMAND: SENDUSERS      - Request the list of users.
 * MESSAGE: {text}         - The message to be sent to all.
 * MESSAGETO: {user}: {text} - The message to be sent to a particular
 *                           person.
 *
 * Client Receive
 * ---------------
 * COMMAND: CLEARUSERS     - Nuke the list of users in anticipation of
 *                           receiving a new list. (See COMMAND: USER:)
 * COMMAND: RMUSER: {user} - Delete the specified person from the user list.
 * COMMAND: USER: {user}   - One of the users attached to the server.
 *                           This will most likely be followed by
 *                           additional USER: lines so treat each as an
 *                           addition to the list.
 * MESSAGE: {text}         - The message sent by another user.
 *
 * 
* *

* Paul Kronenwetter
* Jayme Manning
* Trade Secrets and/or Confidential
* Commercial or Financial Information
* Exempt From Disclosure Under the Freedom
* of Information Act (5 USC 552(b)(3)
* and 5 USC 552(b)(4)) and Under 18 USC 1905
*

*

* Proprietary Information Notice
* * This document contains information proprietary to, and is the * sole property of, Paul Kronenwetter and Jayme Manning. It shall not * be reproduced, used or disclosed in any manner or for any purpose * not authorized in writing by both Paul Kronenwetter and * Jayme Manning, and except, as retention may be so authorized, it * shall be returned to Paul Kronenwetter or Jayme Manning upon * request.

*

© 1998 by Paul Kronenwetter and Jayme * Manning
All Rights Reserved

*/ public class TalkServer { private Vector clients; private BufferedReader in; private Socket clientSocket; private ServerSocket serverSocket; private int SERVER_PORT; public static int index = 0; public static final double VERSION = 1.0; /** * TalkServer - Constructor for the server portion of the * NetTalk application. * * @param tmpPort Integer representing the port that the server * is starting up on. */ public TalkServer(int tmpPort) { // Allocate the Vector required to hold the client sockets. clients = new Vector(); SERVER_PORT = tmpPort; startServer(); } /** * registerClient - Method used to register our ClientThread * within our storage Vector. Registered clients will receive * appropriate message that deal with application control, etc... * * @param newClient ClientThread to register for operations. */ public void registerClient(ClientThread newClient) { clients.addElement(newClient); } /** * removeClient - Method used to remove a client from our active list. * This method should be called either when a client disconnects * intentionally, or the connection is somehow broken. * * @param remIndex - The index of the client to remove. */ public void removeClient(int remIndex) { // Debug Messages // System.out.println("Removing Client at index: " + remIndex); Vector x = new Vector(); Object[] y = new Object[clients.size()]; clients.copyInto(y); ClientThread z = null; // Parse the client vector for(int i = 0; i < y.length; i++) { if(((ClientThread)y[i]).getIndex() != remIndex) { x.addElement(y[i]); // Debug Messages // System.out.println("Keeping: " + // ((ClientThread)y[i]).getUser()); } else { z = ((ClientThread)y[i]); // Debug messages // System.out.println("Dumping: " + // ((ClientThread)y[i]).getUser()); } } // Exchange vectors, and null old vector. clients = null; clients = x; // Be sure to remove user from client lists. if(z != null) { sendAll("COMMAND: RMUSER: " + z.getUser()); z = null; } } /** * startServer - Method used to begin server operations. */ private void startServer() { boolean listening = true; // Setup the server socket try { System.out.println( "NetTalk Java Server - Version: " + VERSION); System.out.println("Listening on Port: " + SERVER_PORT); serverSocket = new ServerSocket(SERVER_PORT); } catch (IOException ioe) { System.out.println( "TalkCoreServer: IOexception in startServer."); System.out.println(ioe); System.out.println("Exiting..."); System.exit(1); } // Main event loop (Think daemon) while(listening) { Socket clientSocket = null; try { // Make connection to any incomming requests clientSocket = serverSocket.accept(); } catch (IOException ioe1) { System.out.println("Socket Accept Failed: " + SERVER_PORT + "-" + ioe1.getMessage()); System.out.println(ioe1.getMessage()); continue; } // Begin new Thread new ClientThread(clientSocket).start(); } try { // Close socket serverSocket.close(); } catch (IOException ioe2) { // Error hanging up System.out.println("Couldn't Close Socket: " + SERVER_PORT); System.out.println(ioe2.getMessage()); System.exit(1); } } /** * processMessage - Method used to distribute messages amongst our * clients. This method takes a single parameter (the message * string), and parses any control comments from the string before * actually distributing it to the appropriate clients. * * @param message The String that was read from a client that needs * to be distributed. */ public void processMessage(String message, String from) { if(message == null) { return; } // If the message is a command, send it to everyone if(message.startsWith("COMMAND: SENDUSERS")) { updateUserList(from); } else if(message.startsWith("COMMAND:")) { sendAll(message); } else if(message.startsWith("MESSAGE:")) { // If the message is a broadcast message, send it to // everyone that is connected. sendAll(message.substring(9),from); } else if(message.startsWith("MESSAGETO:")) { // If the message is to a specific user, send to that // user only. sendSpecific(message.substring(11),from); } } /** * sendAll - Method that is used to propagate a command message to all * connected clients. * * @param message The String that is to be sent to all clients. */ public void sendAll(String message) { // Retrieve references to all of our clients. ClientThread[] x = new ClientThread[clients.size()]; clients.copyInto(x); // Loop through the clients list, and send the same message // to each connected client. for(int i=0; i < x.length; i++) { // Debug Messages // System.out.println("Sending Message To: " + // x[i].getUser()); x[i].sendMessage(message); } } /** * sendAll - Method that is used to propagate an user message to all * connected clients. * * @param message The String that is to be sent to all clients. * @param from The user that originated the message. */ public void sendAll(String message, String from) { // Retrieve references to all of our clients. ClientThread[] x = new ClientThread[clients.size()]; clients.copyInto(x); // Loop through the clients list, and send the same message // to each connected client. for(int i=0; i < x.length; i++) { // Debug Messages // System.out.println("Sending Message To: " + // x[i].getUser()); x[i].sendMessage("MESSAGE: " + from + ": " + message); } } /** * sendSpecific - Method used to send a message to an individual * user. This method will parse the destination from the String. * Because this message is to be parsed to glean the user name to * send to, this method is used only with the MESSAGETO: token. * * @param message The message to be parsed and sent. * @param from The originator of the message. */ public void sendSpecific(String message, String from) { // First find user to send message to. int x = message.indexOf(":"); // Malformed message call no-joy and skip. if( x == -1 ) { return; } // The user we are looking for should be here String t1 = message.substring(0,x); // The remainder of the message should be here. String t2 = message.substring(x+2); // The message to be sent should then be: String t3 = "MESSAGETO: " + from + ": " + t2; // Retrieve references to all of our clients. ClientThread[] y = new ClientThread[clients.size()]; clients.copyInto(y); // Loop through the clients list, and send the message // to only the requested individual for(int i=0; i < y.length; i++) { if((y[i].getUser()).equals(t1)) { // Debug messages // System.out.println("Sending Message To: " + // y[i].getUser()); y[i].sendMessage(t3); } } } /** * sendSpecificCommand - Method used to send a command message to * an individual user. * * @param message The command message. * @param user The user to send the message to. */ public void sendSpecificCommand(String message, String user) { // Retrieve references to all of our clients. ClientThread[] x = new ClientThread[clients.size()]; clients.copyInto(x); // Loop through the clients list, and send the message // to only the requested individual for(int i=0; i < x.length; i++) { if((x[i].getUser()).equals(user)) { // Debug Messages // System.out.println("Sending Message To: " + // x[i].getUser()); x[i].sendMessage(message); } } } /** * updateUserList - Method used to send the given user a complete * update of the list of connected users. * * @param userName The user that we send the update list. */ public void updateUserList(String userName) { // Retrieve references to all of our clients. ClientThread[] x = new ClientThread[clients.size()]; clients.copyInto(x); // Loop through the clients list, and send the message // to only the requested individual for(int i=0; i < x.length; i++) { if((x[i].getUser()).equals(userName)) { // Debug Messages // System.out.println( // "Sending Updated List To: " + // x[i].getUser()); x[i].sendMessage("COMMAND: CLEARUSERS"); // Push the list to the client for(int j=0;j < x.length; j++) { x[i].sendMessage("COMMAND: USER: " + x[j].getUser()); } } } } /** * ClientThread - Class that is used to manage client interaction. * This class is responsible for managing individual clients and * directing their messages to the server for distribution. */ public class ClientThread extends Thread { String userName; Socket clientSocket; BufferedReader inReader; PrintWriter outWriter; int clientIndex; /** * ClientThread - Basic constructor used to initiate the * client connection process. * * @param mySocket This is the client socket that was * generated by the corresponding * ServerSocket.accept() process. */ public ClientThread(Socket mySocket) { clientSocket = mySocket; clientIndex = index; index = index + 1; // Debug Messages // System.out.println("New Client Index = " + // clientIndex); // System.out.println("New Master Index = " + index); try { outWriter = new PrintWriter( clientSocket.getOutputStream()); inReader = new BufferedReader(new InputStreamReader( clientSocket.getInputStream())); // The first string we read better be the // user name... userName = inReader.readLine(); registerClient(this); processMessage( ("COMMAND: USER: " + userName),null); updateUserList(userName); } catch(IOException ioe) { System.out.println( "Unable to open client data connection."); } } /** * run - This is what we do when we have been started. * The client thread simply waits for data from the client * and then passes it on for processing. If we read a null * from the client, we remove ourselves as a registered client * because our connection has been closed. */ public void run() { String temp; // Our data gathering loop try { while((temp = inReader.readLine()) != null) { processMessage(temp,userName); } } catch (IOException ioe) { System.out.println( "Error Reading From Client no. " + clientIndex); } removeClient(clientIndex); } /** * getUser - Method used to return the name of the user * that is associated with this connection instance. * * @return String - The name of the user on this connection. */ public String getUser() { return userName; } /** * getIndex - Method used to return the index value from * the client thread. */ public int getIndex() { return clientIndex; } /** * sendMessage - Method used to send a message to an Individual * client. This is called by the enclosing class in order * to send a message to the client. * * @param message String containing the message for the client. */ public void sendMessage(String message) { outWriter.println(message); outWriter.flush(); } } /** * main - Method used to start the server itself from the command * line. There should be only one command line option. * * @param args[0] This should be an integer representing a port * number that the server will start up on. */ public static void main(String[] args) { TalkServer x; int tmpPort; // Debug Messages // System.out.println("Args: " + args.length); if(args.length == 1) { tmpPort = Integer.valueOf(args[0]).intValue(); x = new TalkServer(tmpPort); } else { x = new TalkServer(25097); } } }