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