Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 171 additions & 49 deletions src/ChatterboxClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

Expand All @@ -11,18 +15,19 @@
*
* Protocol summary (what the server expects):
* 1) Client connects via TCP to host:port.
* 2) Server sends a prompt asking for "Please enter your username and password, separated by a space".
* 2) Server sends a prompt asking for "Please enter your username and password,
* separated by a space".
* 3) Client sends ONE LINE containing: username + space + password + newline.
* 4) Server responds with either:
* - a line starting with the word "Welcome" (success), or
* - an error line (failure), then closes the connection.
* - a line starting with the word "Welcome" (success), or
* - an error line (failure), then closes the connection.
* 5) After success, the client:
* - prints any incoming server messages to the user output
* - reads user input and sends each line to the server
* - prints any incoming server messages to the user output
* - reads user input and sends each line to the server
*
* Important design constraint:
* - Do NOT read/write directly from System.in/System.out inside helper methods.
* Always use userInput/userOutput instead.
* Always use userInput/userOutput instead.
*/
public class ChatterboxClient {

Expand All @@ -43,10 +48,14 @@ public class ChatterboxClient {
* Program entry.
*
* Expected command-line usage:
* javac src/*.java && java -cp src ChatterboxClient HOST PORT USERNAME PASSWORD
* javac src/*.java && java -cp src ChatterboxClient HOST PORT USERNAME PASSWORD
*
* Example:
* javac src/*.java && java -cp src ChatterboxClient localhost 12345 sharon abc123
* javac src/*.java && java -cp src ChatterboxClient localhost 12345 sharon
* abc123
*
* javac src/*.java && java -cp src ChatterboxClient localhost 12345 sharon
* abc123
*
* This method is already complete. Your work is in the TODO methods below.
*/
Expand All @@ -59,24 +68,25 @@ public static void main(String[] args) {
} catch (IllegalArgumentException e) {
System.err.println("Error parsing arguments");
System.err.println(e.getMessage());
System.err.println("Usage: javac src/*.java && java -cp src ChatterboxClient HOST PORT USERNAME PASSWORD");
System.err.println(
"Usage: javac src/*.java && java -cp src ChatterboxClient HOST PORT USERNAME PASSWORD");
System.exit(1);
}
}
System.out.println("Read options: " + options.toString());

System.out.println("Creating client...");

ChatterboxClient client = new ChatterboxClient(options, System.in, System.out);
System.out.println("Client created: " + client.toString());

System.out.println("Connecting to server...");
try {
client.connect();
} catch(IOException e) {
} catch (IOException e) {
System.err.println("Failed to connect to server");
System.err.println(e.getMessage());
System.exit(1);
}
}
System.out.println("Connected to server");

System.out.println("Authenticating...");
Expand All @@ -90,7 +100,7 @@ public static void main(String[] args) {
System.err.println("Failed authentication");
System.err.println(e.getMessage());
System.exit(1);
}
}
System.out.println("Finished authentication");

System.out.println("Beginning chat streaming");
Expand All @@ -100,9 +110,8 @@ public static void main(String[] args) {
System.err.println("Error streaming chats");
System.err.println(e.getMessage());
System.exit(1);
}
}
catch (UnsupportedOperationException e) {
}
} catch (UnsupportedOperationException e) {
System.err.println(e.getMessage());
}
}
Expand All @@ -111,10 +120,10 @@ public static void main(String[] args) {
* Parse command-line arguments into a ChatterboxOptions object.
*
* Required argument order:
* HOST
* PORT
* USERNAME
* PASSWORD
* HOST
* PORT
* USERNAME
* PASSWORD
*
* Rules:
* - If args.length != 4, throw IllegalArgumentException.
Expand All @@ -125,9 +134,31 @@ public static void main(String[] args) {
* @throws IllegalArgumentException on any bad/missing input
*/
public static ChatterboxOptions parseArgs(String[] args) throws IllegalArgumentException {
// TODO: read args in the required order and return new ChatterboxOptions(host, port, username, password)

// TODO: read args in the required order and return new ChatterboxOptions(host,
// port, username, password)
// Remove this exception
throw new UnsupportedOperationException("Argument parsing not yet implemented. Implement parseArgs and remove this exception");

ChatterboxOptions chatterboxOptions = new ChatterboxOptions(args[0],
Integer.parseInt(args[1]), args[2], args[3]);

// - If args.length != 4, throw IllegalArgumentException.
if (args.length != 4) {
throw new IllegalArgumentException("Require host, port, username, password");
}
// - PORT must parse as an integer in the range 1..65535, else throw.
String portString = args[1];
int port = Integer.parseInt(portString);

if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Port must be in the range 1..65535");

}
return chatterboxOptions;

// throw new UnsupportedOperationException(
// "Argument parsing not yet implemented. Implement parseArgs and remove this
// exception");
}

/**
Expand All @@ -138,16 +169,24 @@ public static ChatterboxOptions parseArgs(String[] args) throws IllegalArgumentE
* - Copy host/port/username/password from options into fields.
* - Do NOT open sockets or talk to the network here. That's connect().
*
* @param options parsed connection/auth settings
* @param userInput stream to read user-typed data from
* @param options parsed connection/auth settings
* @param userInput stream to read user-typed data from
* @param userOutput stream to print data to the user
*/
public ChatterboxClient(ChatterboxOptions options, InputStream userInput, OutputStream userOutput) {
this.userInput = new Scanner(userInput, StandardCharsets.UTF_8);
this.userOutput = userOutput;

throw new UnsupportedOperationException("Constructor not yet implemented. Implement ChatterboxClient constructor and remove this exception");
// TODO: copy options.getHost(), getPort(), getUsername(), getPassword() into fields
// throw new UnsupportedOperationException(
// "Constructor not yet implemented. Implement ChatterboxClient constructor and
// remove this exception");
// TODO: copy options.getHost(), getPort(), getUsername(), getPassword() into
// fields
this.host = options.getHost();
this.port = options.getPort();
this.username = options.getUsername();
this.password = options.getPassword();

}

/**
Expand All @@ -164,89 +203,172 @@ public ChatterboxClient(ChatterboxOptions options, InputStream userInput, Output
* @throws IOException if the socket cannot be opened
*/
public void connect() throws IOException {
throw new UnsupportedOperationException("Connect not yet implemented. Implement connect() and remove this exception!");

// Make sure to have this.serverReader and this.serverWriter set by the end of this method!
// hint: get the streams from the sockets, use those to create the InputStreamReader/OutputStreamWriter and the BufferedReader/BufferedWriter
Socket socket = new Socket(host, port);
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,
java.nio.charset.StandardCharsets.UTF_8);
this.serverReader = new BufferedReader(inputStreamReader);

OutputStream outputStream = socket.getOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream,
java.nio.charset.StandardCharsets.UTF_8);
this.serverWriter = new BufferedWriter(outputStreamWriter);

}

// Make sure to have this.serverReader and this.serverWriter set by the end of
// this method!
// hint: get the streams from the sockets, use those to create the
// InputStreamReader/OutputStreamWriter and the BufferedReader/BufferedWriter

/**
* Authenticate with the server using the simple protocol.
*
* Responsibilities:
* - Read and display the server's initial prompt line (if any)
* to userOutput.
* to userOutput.
* - Send ONE LINE containing:
* username + " " + password + "\n"
* using serverOutput.
* username + " " + password + "\n"
* using serverOutput.
* - Read ONE response line from serverReader.
* - If the response indicates failure, throw IllegalArgumentException
* with that response text.
* with that response text.
* - If success, print the welcome line(s) to userOutput and return.
*
* Assumption:
* - The server closes the connection after a failed auth.
*
* @throws IOException for network errors
* @throws IOException for network errors
* @throws IllegalArgumentException for bad credentials / server rejection
*/
public void authenticate() throws IOException, IllegalArgumentException {
throw new UnsupportedOperationException("Authenticate not yet implemented. Implement authenticate() and remove this exception!");
// Hint: use the username/password instance variables, DO NOT READ FROM userInput

String serverPrompt = serverReader.readLine();
System.out.println(serverPrompt);

serverWriter.write(username + " " + password);
serverWriter.newLine();
serverWriter.flush();

String responseReader = serverReader.readLine();
System.out.println(responseReader);

// Hint: use the username/password instance variables, DO NOT READ FROM
// userInput
// send messages using serverWriter (don't forget to flush!)
}

/**
* Start full-duplex chat streaming. SEE INSTRUCTIONS FOR HOW TO DO THIS PART BY PART
* Start full-duplex chat streaming. SEE INSTRUCTIONS FOR HOW TO DO THIS PART BY
* PART
*
* Responsibilities:
* - Run printIncomingChats() and sendOutgoingChats() in separate threads.
*
* Tip:
* - Make printIncomingChats() work (single-threaded) before worrying about
* sendOutgoingChats() and threading.
* sendOutgoingChats() and threading.
*
* @throws IOException
*/
public void streamChat() throws IOException {
throw new UnsupportedOperationException("Chat streaming not yet implemented. Implement streamChat() and remove this exception!");
// printIncomingChats();
// sendOutgoingChats();
Thread thread1 = new Thread(() -> {
try {
printIncomingChats();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
sendOutgoingChats();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
thread1.start();
thread2.start();

}

/**
* Continuously read messages from the server and print them to the user.
*
* Responsibilities:
* - Loop:
* readLine() from server
* if null -> server disconnected, exit program
* else write that line to userOutput
* readLine() from server
* if null -> server disconnected, exit program
* else write that line to userOutput
*
* Notes:
* - Do NOT use System.out directly.
* - If an IOException happens, treat it as disconnect:
* print a message to userOutput and exit.
* print a message to userOutput and exit.
*/
public void printIncomingChats() {

/***
*
* OutputStream outputStream = socket.getOutputStream();
* OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream,
* java.nio.charset.StandardCharsets.UTF_8);
* this.serverWriter = new BufferedWriter(outputStreamWriter);
*
* @throws IOException
*
*/
public void printIncomingChats() throws IOException {
// Listen on serverReader
// Write to userOutput, NOT System.out
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(userOutput,
java.nio.charset.StandardCharsets.UTF_8);
BufferedWriter nBufferedWriter = new BufferedWriter(outputStreamWriter);
String line;
while ((line = serverReader.readLine()) != null) {
nBufferedWriter.write(line);
nBufferedWriter.newLine();
nBufferedWriter.flush();

}

}

/**
* Continuously read user-typed messages and send them to the server.
*
* Responsibilities:
* - Loop forever:
* if scanner has a next line, read it
* write it to serverOutput + newline + flush
* if scanner has a next line, read it
* write it to serverOutput + newline + flush
*
* Notes:
* - If writing fails (IOException), the connection is gone:
* print a message to userOutput and exit.
* print a message to userOutput and exit.
*/

/*
* @throws IOException
*
* @throws UnknownHostException
*
*/
public void sendOutgoingChats() {
public void sendOutgoingChats() throws UnknownHostException, IOException {
// Use the userInput to read, NOT System.in directly
// loop forever reading user input
// write to serverOutput

String line;
while ((line = userInput.nextLine()) != null) {
serverWriter.write(line);
serverWriter.newLine();
serverWriter.flush();

}

}

public String getHost() {
Expand Down