diff --git a/src/ChatterboxClient.java b/src/ChatterboxClient.java index 9f80fc0..2900eeb 100644 --- a/src/ChatterboxClient.java +++ b/src/ChatterboxClient.java @@ -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; @@ -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 { @@ -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. */ @@ -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..."); @@ -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"); @@ -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()); } } @@ -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. @@ -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"); } /** @@ -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(); + } /** @@ -164,52 +203,97 @@ 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(); + } /** @@ -217,18 +301,40 @@ public void streamChat() throws IOException { * * 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(); + + } + } /** @@ -236,17 +342,33 @@ public void printIncomingChats() { * * 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() {