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