diff --git a/.idea/misc.xml b/.idea/misc.xml
index d2b5d0f..f6a588b 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -8,7 +8,5 @@
-
-
-
+
\ No newline at end of file
diff --git a/src/main/java/com/university/chat/Client/ServerListener.java b/src/main/java/com/university/chat/Client/ServerListener.java
index b609d74..79c8439 100644
--- a/src/main/java/com/university/chat/Client/ServerListener.java
+++ b/src/main/java/com/university/chat/Client/ServerListener.java
@@ -1,20 +1,54 @@
package com.university.chat.Client;
-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)
+import com.university.chat.Common.ChatMessage;
+import com.university.chat.Common.FileMessage;
+
+import java.io.ObjectInputStream;
+
+public class ServerListener implements Runnable
+{
+
+ private ObjectInputStream in;
+
+ public ServerListener(ObjectInputStream in)
+ {
+ this.in = in;
+ }
@Override
- public void run() {
- try {
- // TODO: 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.
- } catch (Exception e){
- System.out.println("Disconnected from server");
+ public void run()
+ {
+ try
+ {
+ while (true)
+ {
+ Object obj = in.readObject();
+
+ if (obj instanceof ChatMessage msg)
+ {
+ String content = msg.getContent();
+
+ if (content != null && !content.trim().isEmpty())
+ {
+ if (msg.getSender().equals("Server"))
+ {
+ System.out.println("[Server]: " + content);
+ }
+ else
+ {
+ System.out.println("[" + msg.getSender() + "]: " + content);
+ }
+ }
+ }
+ else if (obj instanceof FileMessage fileMsg)
+ {
+ System.out.println("[File received from " + fileMsg.getSender() + "]: " + fileMsg.getFilename());
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println("Connection to server lost.");
}
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/university/chat/Client/TransferProgress.java b/src/main/java/com/university/chat/Client/TransferProgress.java
index 0e617a4..dc9e6fe 100644
--- a/src/main/java/com/university/chat/Client/TransferProgress.java
+++ b/src/main/java/com/university/chat/Client/TransferProgress.java
@@ -1,23 +1,27 @@
package com.university.chat.Client;
-public class TransferProgress {
+public class TransferProgress
+{
private final long total;
private final long startTime;
private static final int WIDTH = 30;
- public TransferProgress(long total) {
+ public TransferProgress(long total)
+ {
this.total = total;
this.startTime = System.currentTimeMillis();
}
- public void update(long current) {
+ public void update(long current)
+ {
double percent = (double) current / total;
int filled = (int) (percent * WIDTH);
StringBuilder bar = new StringBuilder("[");
- for (int i = 0; i < WIDTH; i++) {
+ for (int i = 0; i < WIDTH; i++)
+ {
bar.append(i < filled ? "█" : " ");
}
bar.append("]");
@@ -30,7 +34,8 @@ public class TransferProgress {
(int) (percent * 100),
speed);
- if (current >= total) {
+ if (current >= total)
+ {
System.out.println();
}
}
diff --git a/src/main/java/com/university/chat/Client/chatClient.java b/src/main/java/com/university/chat/Client/chatClient.java
index dae0bad..53c6e40 100644
--- a/src/main/java/com/university/chat/Client/chatClient.java
+++ b/src/main/java/com/university/chat/Client/chatClient.java
@@ -1,29 +1,231 @@
package com.university.chat.Client;
-public class chatClient {
- public static void main() {
- // TODO: 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.
- // 2. Get the username, and send a LOGIN ChatMessage with that username
- // 3. Start a new Thread running a ServerListener(in) so incoming
- // messages are handled concurrently.
+import com.university.chat.Common.ChatMessage;
+import com.university.chat.Common.FileMessage;
+import com.university.chat.Common.MessageType;
- while (true){
- try {
- // TODO: 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[]
- // (you can use TransferProgress
- // to show progress)
- // and send it as a FileMessage
- // - anything else -> send a PUBLIC_MESSAGE
- // Remember to flush() the output stream after writeObject().
- } catch (Exception e){
- System.out.println("command failed: " + e.getMessage());
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.Socket;
+import java.util.Scanner;
+
+public class chatClient
+{
+
+ private static final String SERVER_HOST = "localhost";
+ private static final int SERVER_PORT = 5000;
+
+ public static void main(String[] args)
+ {
+
+ try
+ {
+ Socket socket = new Socket(SERVER_HOST, SERVER_PORT);
+ System.out.println("Connected to server.");
+
+
+ ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
+ out.flush();
+ ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
+
+ System.out.print("Enter username: ");
+ Scanner scanner = new Scanner(System.in);
+ String username = scanner.nextLine();
+
+ out.writeObject(new ChatMessage(MessageType.LOGIN, username, "", ""));
+ out.flush();
+
+ Thread listenerThread = new Thread(new ServerListener(in));
+ listenerThread.start();
+
+ System.out.println("You can now chat.");
+ System.out.println("Commands:");
+ System.out.println("/msg ");
+ System.out.println("/users");
+ System.out.println("/sendfile ");
+ System.out.println("Anything else will be sent as a public message.");
+ System.out.print("> ");
+
+ while (true)
+ {
+ try
+ {
+ String line = scanner.nextLine();
+
+ if (line == null || line.trim().isEmpty())
+ {
+ System.out.print("> ");
+ continue;
+ }
+
+ line = line.trim();
+
+ if (line.startsWith("/msg "))
+ {
+ handlePrivateMessage(line, username, out);
+ }
+ else if (line.equals("/users"))
+ {
+ handleUserListRequest(username, out);
+ }
+ else if (line.startsWith("/sendfile "))
+ {
+ handleFileSend(line, username, out);
+ }
+ else
+ {
+ handlePublicMessage(line, username, out);
+ }
+
+ System.out.print("> ");
+
+ }
+ catch (Exception e)
+ {
+ System.out.println("command failed: " + e.getMessage());
+ System.out.print("> ");
+ }
}
+
+ }
+ catch (Exception e)
+ {
+ System.out.println("Could not connect to server: " + e.getMessage());
}
}
-}
+
+ private static void handlePrivateMessage(String line, String username, ObjectOutputStream out) throws Exception {
+
+ String[] parts = line.split(" ", 3);
+
+ if (parts.length < 3)
+ {
+ System.out.println("Usage: /msg ");
+ return;
+ }
+
+ String receiver = parts[1];
+ String messageText = parts[2];
+
+ ChatMessage privateMessage = new ChatMessage(
+ MessageType.PRIVATE_MESSAGE,
+ username,
+ messageText,
+ receiver
+ );
+
+ out.writeObject(privateMessage);
+ out.flush();
+ }
+
+ private static void handleUserListRequest(String username, ObjectOutputStream out) throws Exception
+ {
+ ChatMessage userListRequest = new ChatMessage(
+ MessageType.USER_LIST,
+ username,
+ null,
+ ""
+ );
+
+ out.writeObject(userListRequest);
+ out.flush();
+ }
+
+ private static void handlePublicMessage(String line, String username, ObjectOutputStream out) throws Exception
+ {
+ ChatMessage publicMessage = new ChatMessage(
+ MessageType.PUBLIC_MESSAGE,
+ username,
+ null,
+ line
+ );
+
+ out.writeObject(publicMessage);
+ out.flush();
+ }
+
+ private static void handleFileSend(String line, String username, ObjectOutputStream out) throws Exception
+ {
+
+ String[] parts = line.split(" ", 3);
+
+ if (parts.length < 3)
+ {
+ System.out.println("Usage: /sendfile ");
+ return;
+ }
+
+ String receiver = parts[1];
+ String filePath = parts[2];
+
+ File file = new File(filePath);
+
+ if (!file.exists())
+ {
+ System.out.println("File does not exist: " + filePath);
+ return;
+ }
+
+ if (!file.isFile())
+ {
+ System.out.println("This path is not a file: " + filePath);
+ return;
+ }
+
+ long totalSize = file.length();
+
+ TransferProgress progress = new TransferProgress(totalSize);
+
+ byte[] data = new byte[(int) totalSize];
+
+ FileInputStream fis = new FileInputStream(file);
+
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ int totalRead = 0;
+
+ while ((bytesRead = fis.read(buffer)) != -1)
+ {
+
+ System.arraycopy(buffer, 0, data, totalRead, bytesRead);
+ totalRead += bytesRead;
+
+ progress.update(totalRead);
+ }
+
+ fis.close();
+
+ FileMessage fileMessage = new FileMessage(
+ username,
+ receiver,
+ file.getName(),
+ data
+ );
+
+ out.writeObject(fileMessage);
+ out.flush();
+
+ System.out.println("File sent successfully: " + file.getName());
+ }
+
+ private static byte[] readFileToByteArray(File file) throws Exception
+ {
+ byte[] data = new byte[(int) file.length()];
+
+ FileInputStream fis = new FileInputStream(file);
+
+ int totalRead = 0;
+ int bytesRead;
+
+ while (totalRead < data.length && (bytesRead = fis.read(data, totalRead, data.length - totalRead)) != -1)
+ {
+ totalRead += bytesRead;
+ }
+
+ fis.close();
+
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/university/chat/Common/ChatMessage.java b/src/main/java/com/university/chat/Common/ChatMessage.java
index c097745..cbc6ba1 100644
--- a/src/main/java/com/university/chat/Common/ChatMessage.java
+++ b/src/main/java/com/university/chat/Common/ChatMessage.java
@@ -2,7 +2,8 @@ package com.university.chat.Common;
import java.io.Serializable;
-public class ChatMessage implements Serializable {
+public class ChatMessage implements Serializable
+{
private static final long serialVersionUID = 1L;
@@ -11,7 +12,8 @@ public class ChatMessage implements Serializable {
private String receiver;
private String content;
- public ChatMessage(MessageType type, String sender, String receiver, String content) {
+ public ChatMessage(MessageType type, String sender, String receiver, String content)
+ {
this.type = type;
this.sender = sender;
this.receiver = receiver;
diff --git a/src/main/java/com/university/chat/Common/FileMessage.java b/src/main/java/com/university/chat/Common/FileMessage.java
index 4f90fbf..cbd417a 100644
--- a/src/main/java/com/university/chat/Common/FileMessage.java
+++ b/src/main/java/com/university/chat/Common/FileMessage.java
@@ -2,7 +2,8 @@ package com.university.chat.Common;
import java.io.Serializable;
-public class FileMessage implements Serializable {
+public class FileMessage implements Serializable
+{
private static final long SerialVersionUID = 1L;
@@ -11,26 +12,31 @@ public class FileMessage implements Serializable {
private String filename;
private byte[] data;
- public FileMessage(String sender, String receiver, String filename, byte[] data) {
+ public FileMessage(String sender, String receiver, String filename, byte[] data)
+ {
this.sender = sender;
this.receiver = receiver;
this.filename = filename;
this.data = data;
}
- public String getSender() {
+ public String getSender()
+ {
return sender;
}
- public String getReceiver() {
+ public String getReceiver()
+ {
return receiver;
}
- public String getFilename() {
+ public String getFilename()
+ {
return filename;
}
- public byte[] getData() {
+ public byte[] getData()
+ {
return data;
}
}
diff --git a/src/main/java/com/university/chat/Common/MessageType.java b/src/main/java/com/university/chat/Common/MessageType.java
index 093ff8d..089a227 100644
--- a/src/main/java/com/university/chat/Common/MessageType.java
+++ b/src/main/java/com/university/chat/Common/MessageType.java
@@ -2,7 +2,8 @@ package com.university.chat.Common;
import java.io.Serializable;
-public enum MessageType implements Serializable {
+public enum MessageType implements Serializable
+{
LOGIN,
LOGIN_SUCCESS,
LOGIN_FAILED,
diff --git a/src/main/java/com/university/chat/Server/ChatServer.java b/src/main/java/com/university/chat/Server/ChatServer.java
index b2a35ae..99b909a 100644
--- a/src/main/java/com/university/chat/Server/ChatServer.java
+++ b/src/main/java/com/university/chat/Server/ChatServer.java
@@ -1,15 +1,53 @@
package com.university.chat.Server;
-public class ChatServer {
+import java.net.ServerSocket;
+import java.net.Socket;
+
+public class ChatServer
+{
+ private static final UserManager userManager = new UserManager();
+
+ private static final int PORT = 5000;
// TODO: 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) {
+ public static void main(String[] args)
+ {
+
+ System.out.println("Chat server starting on port " + PORT + "...");
+
+ try (ServerSocket serverSocket = new ServerSocket(PORT))
+ {
+
+ System.out.println("Server started. Waiting for clients...");
+
+ while (true)
+ {
+
+ Socket clientSocket = serverSocket.accept();
+
+ System.out.println("New client connected: "
+ + clientSocket.getInetAddress());
+
+ ClientSession session =
+ new ClientSession(clientSocket, userManager);
+
+ Thread thread = new Thread(session);
+ thread.start();
+ }
+
+ }
+ catch (Exception e)
+ {
+ System.out.println("Server error: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
// TODO: Create a ServerSocket
// TODO: In an infinite loop:
// accept an incoming client connection
// make a new thread running ClientSession for each user.
- }
+
}
diff --git a/src/main/java/com/university/chat/Server/ClientSession.java b/src/main/java/com/university/chat/Server/ClientSession.java
index f09d64a..1cf3666 100644
--- a/src/main/java/com/university/chat/Server/ClientSession.java
+++ b/src/main/java/com/university/chat/Server/ClientSession.java
@@ -2,70 +2,152 @@ 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.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.nio.file.Files;
-public class ClientSession implements Runnable {
+public class ClientSession implements Runnable
+{
+ private final Socket socket;
+ private final UserManager userManager;
+ private ObjectOutputStream out;
+ private ObjectInputStream in;
private String username;
- public ClientSession(Socket socket, UserManager userManager) {
- // TODO : Create an ObjectOutputStream from socket.getOutputStream()
- // and an ObjectInputStream from socket.getInputStream().
+ public ClientSession(Socket socket, UserManager userManager)
+ {
+ this.socket = socket;
+ this.userManager = userManager;
}
@Override
- public void run() {
- try {
+ public void run()
+ {
+ try
+ {
+ this.out = new ObjectOutputStream(socket.getOutputStream());
+ this.out.flush();
+ this.in = new ObjectInputStream(socket.getInputStream());
- // TODO: 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.
- // 4. Try to register the user via userManager.addUser(...).
- // 5. If the username is taken, send back LOGIN_FAILED and close the socket.
- // 6. Otherwise, create the user's folders with FileManager.createUserFolders(...)
- // and send back LOGIN_SUCCESS.
+ Object obj = in.readObject();
- // TODO: 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).
+ if (obj instanceof ChatMessage loginMsg && loginMsg.getType() == MessageType.LOGIN)
+ {
+ String requestedUsername = loginMsg.getSender();
+
+ if (userManager.addUser(requestedUsername, this))
+ {
+ this.username = requestedUsername;
+ FileManager.createUserFolders(this.username);
+
+ send(new ChatMessage(MessageType.LOGIN_SUCCESS, "Server", "Welcome to the chat system!", ""));
+ }
+ else
+ {
+ send(new ChatMessage(MessageType.LOGIN_FAILED, "Server", "Username already taken.", ""));
+ socket.close();
+ return;
+ }
+ }
+ else
+ {
+ socket.close();
+ return;
+ }
+
+ while (true)
+ {
+ Object receivedObj = in.readObject();
+
+ if (receivedObj instanceof ChatMessage msg)
+ {
+ handleChatMessage(msg);
+ }
+ else if (receivedObj instanceof FileMessage fileMsg)
+ {
+ handleFileMessage(fileMsg);
+ }
+ }
- } catch (Exception e) {
- System.out.println("Disconnected: " + username);
- } finally {
- // TODO: Remove the user from UserManager so they no longer
- // receive broadcasts or appear in users list
}
- }
-
-
- private void handleChatMessage(ChatMessage msg) throws IOException {
- switch (msg.getType()) {
- case PUBLIC_MESSAGE -> {
- // TODO: Broadcast this message to every connected client.
+ catch (Exception e)
+ {
+ System.out.println("Disconnected: " + (username != null ? username : "Unknown client"));
+ }
+ finally
+ {
+ if (username != null)
+ {
+ userManager.removeUser(username);
}
- case PRIVATE_MESSAGE -> {
- // TODO: Forward this message to the receiver user.
+ try
+ {
+ socket.close();
}
- case USER_LIST -> {
- // TODO: Reply to the requester with the list of online users.
+ catch (IOException e)
+ {
+ System.err.println("Error closing socket: " + e.getMessage());
}
}
}
- private void handleFileMessage(FileMessage fileMsg) throws IOException {
- // Storing the file
+ public void send(Object msg) throws IOException
+ {
+ if (out != null)
+ {
+ out.writeObject(msg);
+ out.flush();
+ }
+ }
+
+ private void handleChatMessage(ChatMessage msg) throws IOException
+ {
+ switch (msg.getType())
+ {
+ case PUBLIC_MESSAGE ->
+ {
+ for (ClientSession session : userManager.getAllSessions())
+ {
+ session.send(msg);
+ }
+ }
+ case PRIVATE_MESSAGE ->
+ {
+ ClientSession receiver = userManager.getUser(msg.getReceiver());
+ if (receiver != null)
+ {
+ receiver.send(msg);
+ }
+ else
+ {
+ send(new ChatMessage(MessageType.PUBLIC_MESSAGE, "Server", "User not found: " + msg.getReceiver(), username));
+ }
+ }
+ case USER_LIST ->
+ {
+ String userListStr = userManager.listUsers();
+ send(new ChatMessage(MessageType.USER_LIST, "Server", userListStr, username));
+ }
+ }
+ }
+
+ private void handleFileMessage(FileMessage fileMsg) throws IOException
+ {
var sentPath = FileManager.getSentPath(fileMsg.getSender(), fileMsg.getFilename());
var recvPath = FileManager.getReceivedPath(fileMsg.getReceiver(), fileMsg.getFilename());
Files.write(sentPath, fileMsg.getData());
Files.write(recvPath, fileMsg.getData());
- // TODO: Forward the received file-message to the destination user.
+ ClientSession receiver = userManager.getUser(fileMsg.getReceiver());
+ if (receiver != null)
+ {
+ receiver.send(fileMsg);
+ }
}
}
\ No newline at end of file