diff --git a/my_passwords.txt b/my_passwords.txt index 0332ee1..d6a6f24 100644 --- a/my_passwords.txt +++ b/my_passwords.txt @@ -1,2 +1,3 @@ You can add your passwords here and they won't be committed to your git repository. +Passion1218! \ No newline at end of file diff --git a/src/ChatterboxClient.java b/src/ChatterboxClient.java index 9f80fc0..2fbd602 100644 --- a/src/ChatterboxClient.java +++ b/src/ChatterboxClient.java @@ -6,6 +6,17 @@ import java.nio.charset.StandardCharsets; import java.util.Scanner; +import java.net.Socket; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +/* +Worked on WAVE 1 - 7 +MOST OF MY COMMENTS BEGIN WITH NOTE, some dont have but the main ones +have NOTE + */ + + /** * A simple command-line chat client for the Chatterbox server. * @@ -32,12 +43,12 @@ public class ChatterboxClient { private String password; // Streams for user I/O - private Scanner userInput; - private OutputStream userOutput; + private Scanner userInput; // for reading user input + private OutputStream userOutput; // for printing to user // Readers/Writers for server I/O (set up in connect()) - private BufferedReader serverReader; - private BufferedWriter serverWriter; + private BufferedReader serverReader; // for reading from the network socket + private BufferedWriter serverWriter; // for writing to the network socket /** * Program entry. @@ -124,12 +135,45 @@ public static void main(String[] args) { * @return a fully populated ChatterboxOptions * @throws IllegalArgumentException on any bad/missing input */ + + + // This is Wave 1 --> for the command arg lines public static ChatterboxOptions parseArgs(String[] args) throws IllegalArgumentException { // 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"); + // throw new UnsupportedOperationException("Argument parsing not yet implemented. Implement parseArgs and remove this exception"); + + // NOTE: We need 4 arguments of validation for the program + if ( args.length != 4) { + throw new IllegalArgumentException("Expected 4 arguments: HOST PORT USERNAME PASSWORD"); + + } + + //NOTE: The expeced order based on the required argument order + String host = args[0]; + String portString = args[1]; + String username = args[2]; + String password = args[3]; + + int port; + try { + // NOTE: This is for converting the portString to Integer + //Its required coz the PORT is numerical + port = Integer.parseInt(portString); + } catch (NumberFormatException e) { + // NOTE: It rhrows when the string isnt a valid numnber + throw new IllegalArgumentException("The port has to be a valid integer."); } + if (port < 1 || port > 65535) { + throw new IllegalArgumentException("Port must be between 1 and 65535."); + } + + return new ChatterboxOptions(host, port, username, password); +} + +// this is WAVE 2 --> Adding constructors + /** * Construct a ChatterboxClient from already-parsed options and user streams. * @@ -143,12 +187,25 @@ public static ChatterboxOptions parseArgs(String[] args) throws IllegalArgumentE * @param userOutput stream to print data to the user */ public ChatterboxClient(ChatterboxOptions options, InputStream userInput, OutputStream userOutput) { + // NOTE: THE utf 8 is for the character encoding + // NOTE: the userinput gets the input then the chat starts 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"); + // NOTE: add // on line 196-> so it removes the red scuiggle line in line 188 + // throw new UnsupportedOperationException("Constructor not yet implemented. Implement ChatterboxClient constructor and remove this exception"); // TODO: copy options.getHost(), getPort(), getUsername(), getPassword() into fields - } + + // NOTE: In the this.host (line 202 - 205) the constructor just saves the data + // into the client's memory for them to be used with other methods + this.host = options.getHost(); + this.port = options.getPort(); + this.username = options.getUsername(); + this.password = options.getPassword(); +} + + /** * Open a TCP connection to the server. @@ -163,12 +220,40 @@ public ChatterboxClient(ChatterboxOptions options, InputStream userInput, Output * * @throws IOException if the socket cannot be opened */ + + //THis is wave 4 --> Connect + public void connect() throws IOException { - throw new UnsupportedOperationException("Connect not yet implemented. Implement connect() and remove this exception!"); + // 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 - } + + //FRED: --> WAVE4 PART + // NOTE: Added socket connection + // NOTE: The socket line makes the call --> then establish connection + // if server's not there rhe exception is throen + // NOTE: The socket initializes the connection to the specified host and port + + Socket socket = new Socket(this.host, this.port); + + // NOTE: Create UTF-8 input reader + // NOTE: the socketgetinputstream --> gets the byte stram from the netword + // NOTE: the inputstramreader changes the raw bytes to readable UTF 8 Texts + // that we can read + InputStreamReader inputStreamReader = + new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8); + this.serverReader = new BufferedReader(inputStreamReader); + + // Create UTF-8 output writer + OutputStreamWriter outputStreamWriter = + new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8); + // NOTE: The buffered writer helps send the text to the server --> + // kinda like an outbox in gmail + this.serverWriter = new BufferedWriter(outputStreamWriter); +} + + /** * Authenticate with the server using the simple protocol. @@ -190,10 +275,53 @@ public void connect() throws IOException { * @throws IOException for network errors * @throws IllegalArgumentException for bad credentials / server rejection */ + + //THis is wave 5 --> Authenticate public void authenticate() throws IOException, IllegalArgumentException { - throw new UnsupportedOperationException("Authenticate not yet implemented. Implement authenticate() and remove this exception!"); + // 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 // send messages using serverWriter (don't forget to flush!) + + + // NOTE: Read users prompt if there is + String prompt = serverReader.readLine(); + if (prompt == null) { + throw new IOException("Server disconnected before authentication prompt."); + } + + // NOTE: Prompt sent to the user + // NOTE: The getBytes() --> changes tect into bytes to be sent + // to the outstream + userOutput.write((prompt + "\n").getBytes(StandardCharsets.UTF_8)); + userOutput.flush(); + + // NOTE: Send username and password as required by the server + String loginLine = username + " " + password; + serverWriter.write(loginLine + "\n"); + // NOTE: The flush() sends it to the server directly for the login attempt + serverWriter.flush(); + + // Read server response + String response = serverReader.readLine(); + // NOTE: If response is null, server disconnects + if (response == null) { + throw new IOException("Server disconnected during authentication."); + } + + //NOTE: server's response to the user + userOutput.write((response + "\n").getBytes(StandardCharsets.UTF_8)); + userOutput.flush(); + + //NOTE: if successful, you get welcome, if not an exception is thrown + if (response.startsWith("Welcome")) { + } else { + // NOTE: Failed authentication throws an exception + // if the main method catches the exception it directly exits the client + throw new IllegalArgumentException(response); + } + + + } /** @@ -208,8 +336,50 @@ public void authenticate() throws IOException, IllegalArgumentException { * * @throws IOException */ + + // this is for wave 6 & 7 --> Chat streaming public void streamChat() throws IOException { - throw new UnsupportedOperationException("Chat streaming not yet implemented. Implement streamChat() and remove this exception!"); + // throw new UnsupportedOperationException("Chat streaming not yet implemented. Implement streamChat() and remove this exception!"); + + // NOTE: in this wave, the incoming thread runs the thread printincomingchats everytime + //thus helps the client can then keep reading the messages of the server + // The lambda makes the thread shorter and cleaner. (eg line 351 with ->) + + + //ADDED THE PRINT CHATS FOR WAVE 6 + // printIncomingChats(); + + // updated the printincoming chats and added the try method + Thread incomingThread = new Thread(() -> { + try { + printIncomingChats(); + } catch (IOException e) { + // When server disconnects, this will throw the exception and + // the thread ends there + } + }); + + Thread outgoingThread = new Thread(() -> { + try { + sendOutgoingChats(); + } catch (IOException e) { + // Writing failed (server likely disconnected) + } + }); + + // NOTE: Both of these start at the same time + incomingThread.start(); + outgoingThread.start(); + + try { + // this tells the main thread to wait until its done reading messages + incomingThread.join(); + outgoingThread.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + } /** @@ -226,9 +396,20 @@ public void streamChat() throws IOException { * - If an IOException happens, treat it as disconnect: * print a message to userOutput and exit. */ - public void printIncomingChats() { + public void printIncomingChats() throws IOException{ // Listen on serverReader // Write to userOutput, NOT System.out + + // WAVE 6 continuation: + String line; + // NOTE: As long as the readline is not null, the loop continues + // as long as there are readlines or texts added. It will continue reading + while ((line = serverReader.readLine()) != null) { + // + userOutput.write((line + "\n").getBytes(StandardCharsets.UTF_8)); + // NOTE: Flush so the user sees the message ASAP + userOutput.flush(); + } } /** @@ -243,10 +424,24 @@ public void printIncomingChats() { * - If writing fails (IOException), the connection is gone: * print a message to userOutput and exit. */ - public void sendOutgoingChats() { + public void sendOutgoingChats() throws IOException{ // Use the userInput to read, NOT System.in directly // loop forever reading user input // write to serverOutput + + // WAVE 7: + while (true) { + // NOTE: This is to check if there is anothe rline to read + if (!userInput.hasNextLine()) { + break; // if not found break and exit + } + String message = userInput.nextLine(); + serverWriter.write(message + "\n"); + serverWriter.flush(); + } + + + } public String getHost() {