diff --git a/src/main/java/com/university/chat/Client/ServerListener.java b/src/main/java/com/university/chat/Client/ServerListener.java index b609d74..dab64d8 100644 --- a/src/main/java/com/university/chat/Client/ServerListener.java +++ b/src/main/java/com/university/chat/Client/ServerListener.java @@ -1,20 +1,40 @@ package com.university.chat.Client; +import com.university.chat.Common.ChatMessage; +import com.university.chat.Common.FileMessage; + +import java.io.ObjectInputStream; + public class ServerListener implements Runnable{ // TODO: store the ObjectInputStream from the user socket // (this should be the same input stream the // chatClient created when connecting) + private ObjectInputStream inputStream; + + public ServerListener(ObjectInputStream inputStream) { + this.inputStream = inputStream; + } @Override public void run() { try { - // TODO: In an infinite loop read objects from the server + // In an infinite loop read objects from the server // - if it's a ChatMessage -> print ": " // - if it's a FileMessage -> print that a file was received // (filename + sender), it's already // saved to disk by the server. + while (true) { + Object object = inputStream.readObject(); + + if(object instanceof ChatMessage) { + System.out.println(((ChatMessage) object).getSender() + ": " + ((ChatMessage) object).getContent()); + } + else if(object instanceof FileMessage) { + System.out.println("File received: " + ((FileMessage) object).getFilename() + " from " + ((FileMessage) object).getSender()); + } + } } catch (Exception e){ - System.out.println("Disconnected from server"); + System.out.println("Disconnected from server" ); } } } diff --git a/src/main/java/com/university/chat/Client/chatClient.java b/src/main/java/com/university/chat/Client/chatClient.java index dae0bad..f0f80f4 100644 --- a/src/main/java/com/university/chat/Client/chatClient.java +++ b/src/main/java/com/university/chat/Client/chatClient.java @@ -1,8 +1,19 @@ package com.university.chat.Client; +import com.university.chat.Common.ChatMessage; +import com.university.chat.Common.FileMessage; +import com.university.chat.Common.MessageType; + +import java.io.*; +import java.net.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Scanner; + public class chatClient { - public static void main() { - // TODO: Connecting to the server + public static void main() throws IOException { + // Connecting to the server // 1. Create a socket and connect to the server // 2. Create an ObjectOutputStream (out) and ObjectInputStream (in) // from the socket's streams — output FIRST, then input. @@ -10,9 +21,25 @@ public class chatClient { // 3. Start a new Thread running a ServerListener(in) so incoming // messages are handled concurrently. + Socket socket = new Socket("127.0.0.1", 5000); + ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); + ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); + + Scanner input = new Scanner(System.in); + System.out.println("Enter your username: "); + String username = input.nextLine(); + + ChatMessage chatMessage = new ChatMessage(MessageType.LOGIN, username, null, ""); + out.writeObject(chatMessage); + out.flush(); + + Thread thread = new Thread(new ServerListener(in)); + thread.start(); + + while (true){ try { - // TODO: Program loop — read a line from the console and act on it: + //Program loop — read a line from the console and act on it: // - "/msg " -> build & send a PRIVATE_MESSAGE // - "/users" -> build & send a USER_LIST request // - "/sendfile " -> read the file into a byte[] @@ -21,6 +48,38 @@ public class chatClient { // and send it as a FileMessage // - anything else -> send a PUBLIC_MESSAGE // Remember to flush() the output stream after writeObject(). + + String line = input.nextLine(); + if(line.equals("/users")) { + chatMessage = new ChatMessage(MessageType.USER_LIST, username, null, null); + out.writeObject(chatMessage); + out.flush(); + } + else if(line.startsWith("/msg")) { + String[] parts = line.split(" ", 3); + chatMessage = new ChatMessage(MessageType.PRIVATE_MESSAGE, + username, parts[1], parts[2]); + out.writeObject(chatMessage); + out.flush(); + } + else if(line.startsWith("/sendfile")) { + String[] parts = line.split(" ", 3); + String receiver = parts[1]; + String path = parts[2]; + + Path filePath = Paths.get(path); + byte[] data = Files.readAllBytes(filePath); + FileMessage fileMessage = new FileMessage(username, receiver, filePath.getFileName().toString(), data); + + out.writeObject(fileMessage); + out.flush(); + } + else { + chatMessage = new ChatMessage(MessageType.PUBLIC_MESSAGE, username, null, line); + out.writeObject(chatMessage); + out.flush(); + } + } catch (Exception e){ System.out.println("command failed: " + e.getMessage()); } diff --git a/src/main/java/com/university/chat/Server/ChatServer.java b/src/main/java/com/university/chat/Server/ChatServer.java index b2a35ae..8a23644 100644 --- a/src/main/java/com/university/chat/Server/ChatServer.java +++ b/src/main/java/com/university/chat/Server/ChatServer.java @@ -1,15 +1,28 @@ package com.university.chat.Server; +import java.io.IOException; +import java.net.*; + public class ChatServer { - // TODO: declare a single shared UserManager instance (static final) + // declare a single shared UserManager instance (static final) // This MUST be shared by all ClientSession threads so that // broadcasting and private messaging work correctly. - public static void main(String[] args) { - // TODO: Create a ServerSocket + static final UserManager userManager = new UserManager(); - // TODO: In an infinite loop: + public static void main(String[] args) throws IOException { + // Create a ServerSocket + ServerSocket serverSocket = new ServerSocket(5000); + + // In an infinite loop: // accept an incoming client connection // make a new thread running ClientSession for each user. + while (true) { + Socket socket = serverSocket.accept(); + System.out.println("New client accepted"); + ClientSession clientSession = new ClientSession(socket, userManager); + Thread thread = new Thread(clientSession); + thread.start(); + } } } diff --git a/src/main/java/com/university/chat/Server/ClientSession.java b/src/main/java/com/university/chat/Server/ClientSession.java index f09d64a..3f38a47 100644 --- a/src/main/java/com/university/chat/Server/ClientSession.java +++ b/src/main/java/com/university/chat/Server/ClientSession.java @@ -2,25 +2,35 @@ package com.university.chat.Server; import com.university.chat.Common.ChatMessage; import com.university.chat.Common.FileMessage; +import com.university.chat.Common.MessageType; -import java.io.IOException; +import java.io.*; import java.net.Socket; import java.nio.file.Files; public class ClientSession implements Runnable { private String username; + UserManager userManager = null; + Socket socket = null; + ObjectOutputStream outputStream = null; + ObjectInputStream inputStream = null; - public ClientSession(Socket socket, UserManager userManager) { - // TODO : Create an ObjectOutputStream from socket.getOutputStream() + public ClientSession(Socket socket, UserManager userManager) throws IOException { + // Create an ObjectOutputStream from socket.getOutputStream() // and an ObjectInputStream from socket.getInputStream(). + this.userManager = userManager; + this.socket = socket; + outputStream = new ObjectOutputStream(socket.getOutputStream()); + outputStream.flush(); + inputStream = new ObjectInputStream(socket.getInputStream()); } @Override public void run() { try { - // TODO: Welcome the user (login step) + // Welcome the user (login step) // 1. Read the first object sent by the client. // 2. Check it's a ChatMessage with type LOGIN. // 3. Extract the username. @@ -29,17 +39,50 @@ public class ClientSession implements Runnable { // 6. Otherwise, create the user's folders with FileManager.createUserFolders(...) // and send back LOGIN_SUCCESS. - // TODO: Main message loop + Object object = inputStream.readObject(); + + if(object instanceof ChatMessage) { + ChatMessage chatMessage = (ChatMessage) object; + + if(chatMessage.getType().equals(MessageType.LOGIN)) { + username = chatMessage.getSender(); + + if (userManager.addUser(username, this)) { + FileManager.createUserFolders(username); + ChatMessage response = new ChatMessage(MessageType.LOGIN_SUCCESS, "Server", username, "Login successfully"); + outputStream.writeObject(response); + outputStream.flush(); + } + else { + ChatMessage response = new ChatMessage(MessageType.LOGIN_FAILED, "Server", username, "Username already exists"); + outputStream.writeObject(response); + outputStream.flush(); + socket.close(); + } + } + } + + // Main message loop // In a loop, call in.readObject(), you can separate messages by their type: // - if it's a ChatMessage -> call handleChatMessage(msg) // - if it's a FileMessage -> call handleFileMessage(fileMsg) // Keep looping until the connection is closed (an exception will be thrown). + while (!socket.isClosed()) { + object = inputStream.readObject(); + + if(object instanceof ChatMessage) + handleChatMessage((ChatMessage) object); + else if(object instanceof FileMessage) + handleFileMessage((FileMessage) object); + } + } catch (Exception e) { System.out.println("Disconnected: " + username); } finally { - // TODO: Remove the user from UserManager so they no longer + // Remove the user from UserManager so they no longer // receive broadcasts or appear in users list + if(!socket.isClosed()) userManager.removeUser(username); } } @@ -47,13 +90,22 @@ public class ClientSession implements Runnable { private void handleChatMessage(ChatMessage msg) throws IOException { switch (msg.getType()) { case PUBLIC_MESSAGE -> { - // TODO: Broadcast this message to every connected client. + // Broadcast this message to every connected client. + for (ClientSession clientSession : userManager.getAllSessions()) { + clientSession.outputStream.writeObject(msg); + clientSession.outputStream.flush(); + } } case PRIVATE_MESSAGE -> { - // TODO: Forward this message to the receiver user. + // Forward this message to the receiver user. + userManager.getUser(msg.getReceiver()).outputStream.writeObject(msg); + userManager.getUser(msg.getReceiver()).outputStream.flush(); } case USER_LIST -> { - // TODO: Reply to the requester with the list of online users. + // Reply to the requester with the list of online users. + ChatMessage chatMessage = new ChatMessage(MessageType.USER_LIST, "UserManager", username, userManager.listUsers()); + outputStream.writeObject(chatMessage); + outputStream.flush(); } } } @@ -66,6 +118,9 @@ public class ClientSession implements Runnable { Files.write(sentPath, fileMsg.getData()); Files.write(recvPath, fileMsg.getData()); - // TODO: Forward the received file-message to the destination user. + // Forward the received file-message to the destination user. + ClientSession recieverSession = userManager.getUser(fileMsg.getReceiver()); + recieverSession.outputStream.writeObject(fileMsg); + recieverSession.outputStream.flush(); } } \ No newline at end of file