// client program for simple chat program CS479 Fall 2008 import java.io.*; // need for I/O streams import java.net.*; // need for networking import java.util.Scanner; /** * Class ChatClient client program for simple chat program * * @author Dan Hyde * @version 1.0 */ public class ChatClient { // Select a port for Gnat Chat. // MUST be the same port number as your server private final static int port1 = 5000; /** * main method is the client code * * @param args a String[] value */ public static final void main(final String[] args) { ObjectOutputStream out = null; ObjectInputStream in = null; StartUp up = new StartUp(); ShutDown s = new ShutDown(); // Read server host name from command line if (args.length != 1) { System.err.println("\nUsage: java Client host"); } else { try { System.out.println("\nClient: Trying to connect to server ..."); // Attempt to connect to the specified host on port port1 Socket s1 = new Socket(args[0], port1); // open in and out streams out = new ObjectOutputStream(s1.getOutputStream()); in = new ObjectInputStream(s1.getInputStream()); // set up the Receive thread Runnable inThread = new Receive(in, out, up, s); Thread threadReceive = new Thread(inThread); // start the Receive thread threadReceive.start(); // set up the Send thread Runnable outThread = new Send(out, up, s); Thread threadSend = new Thread(outThread); // start the Send thread threadSend.start(); // wait for shut down try { threadReceive.join(); threadSend.join(); } catch (InterruptedException e3) { System.err.println(e3.getMessage()); } // All done, close the streams then close the socket out.close(); in.close(); s1.close(); System.out.println("\nClient: Disconnected from server!"); } catch (UnknownHostException e1) { System.err.println("\nClient: Server not running?"); } catch (IOException e2) { System.err.println(e2.getMessage()); } } } } class Receive implements Runnable { private ObjectInputStream in; private ObjectOutputStream out; private StartUp up; private ShutDown s; public Receive(ObjectInputStream in1, ObjectOutputStream inOut, StartUp inUp, ShutDown inShutDown) { in = in1; out = inOut; up = inUp; s = inShutDown; } // a runnable object MUST have a void run() method. public void run() { Message mess = new Message(); String line = ""; String senderName = ""; try { // get the first message for start up mess = (Message) in.readObject(); } catch (ClassNotFoundException e4) { System.err.println("Error in startup2" + e4); System.exit(0); } catch (IOException e5) { System.err.println("Error in startup2" + e5); System.exit(0); } senderName = mess.getSenderName(); // send senderName to Send thread through shared variable up.setNameNotify(senderName); // receive loop until shut down do { try { // receive from server - must cast to Message object mess = (Message) in.readObject(); senderName = mess.getSenderName(); line = mess.getMessage(); if (line.equals("quit")) { line = "*** " + senderName + " has quit! Please press Return to exit! ***"; System.out.print("\n" + senderName + ": " + line); } else { System.out.println("\n" + senderName + ": " + line); } } catch (ClassNotFoundException e3){ System.err.println("Class not found " + e3); System.exit(0); } catch (IOException e4){ System.err.println("Error in connection " + e4); System.exit(0); } } while(!mess.getShutDown()); s.setShutDown(true); // If I have received a shut message but not from "System", // send one out to close down the other side of communication if (!senderName.equals("System")) { try { out.writeObject(new Message("Shutting down...", "System", true)); } catch (IOException e5) { System.err.println("Error in shutting down" + e5); } } } } class Send implements Runnable { private ObjectOutputStream out; private StartUp up; private ShutDown s; public Send(ObjectOutputStream inOut, StartUp inUp, ShutDown inShutDown) { out = inOut; up = inUp; s = inShutDown; } // a runnable object MUST have a void run() method. public void run() { Scanner input = new Scanner(System.in); String name = ""; String line = ""; String senderName; Message mess = new Message(); System.out.print("\n --- Welcome to Gnat Chat ---" + "\n By Dan Hyde" + "\n\nEnter your first name: "); name = input.nextLine(); // start up by sending one message to partner try { // Important to always send a new object out.writeObject(new Message("Starting up...", name, false)); } catch (IOException e5) { System.err.println("Error in startup" + e5); System.exit(0); } System.out.print("*** Please WAIT for partner to start ***"); // wait for other thread to send partner's name via shared object senderName = up.getNameWait(); System.out.println("\n" + senderName + " has started. You may proceed." ); System.out.print("Enter a line of text after prompt (quit to stop):" ); // main sending loop until shut down do { System.out.print("\nEnter: " ); line = input.nextLine(); if (!s.getShutDown()) { try { // send to server a message if (line.equals("quit")) { // shut down out.writeObject(new Message(line, name, true)); } else { out.writeObject(new Message(line, name, false)); } } catch (IOException e4){ System.err.println("Error in connection " + e4 ); System.exit(0); } } } while(!line.equals("quit") && !s.getShutDown()); } } // A shared variable class used during start up. // Forces the Send thread to wait until Receive thread shares senderName // and notifies Send thread. class StartUp { private String senderName; private boolean waiting; public StartUp() { senderName = "UNKNOWN"; waiting = false; } public void setNameNotify(String inSenderName) { // Make sure other thread is waiting // If the notify occurs before the other thread is waiting, // it will be useless. while (!waiting) { try { // note sleep does NOT release a lock Thread.sleep(100); } catch (InterruptedException e1) { System.err.println("Error in setNameNotify " + e1); } } // Only one thread may enter. synchronized (this) { senderName = inSenderName; notify(); } } public String getNameWait() { // Only one thread may enter. synchronized (this) { try { waiting = true; wait(); // releases the lock } catch (InterruptedException e1) { System.err.println("Error in getNameWait " + e1); } return senderName; } } } // A shared variable class used during shut down. // Set and get methods of a protected boolean value. class ShutDown { private boolean shutDown; public ShutDown() { shutDown = false; } public synchronized void setShutDown(boolean inShutDown) { shutDown = inShutDown; } public synchronized boolean getShutDown() { return shutDown; } }