diff --git a/src/chat/Client.java b/src/chat/Client.java deleted file mode 100644 index ecc8d88..0000000 --- a/src/chat/Client.java +++ /dev/null @@ -1,7 +0,0 @@ -package chat; - -public class Client { - public static void main(String[] args) throws Exception { - System.out.println("Client started!"); - } -} \ No newline at end of file diff --git a/src/chat/Server.java b/src/chat/Server.java deleted file mode 100644 index cd56e71..0000000 --- a/src/chat/Server.java +++ /dev/null @@ -1,7 +0,0 @@ -package chat; - -public class Server { - public static void main(String[] args) throws Exception { - System.out.println("Server started!"); - } -} \ No newline at end of file diff --git a/src/chat/clientside/Client.java b/src/chat/clientside/Client.java new file mode 100644 index 0000000..ea03ae7 --- /dev/null +++ b/src/chat/clientside/Client.java @@ -0,0 +1,76 @@ +package chat.clientside; + +import chat.common.Commands; +import chat.common.Configs; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.time.LocalDateTime; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Client { + private static final Logger LOGGER = Logger.getLogger(Client.class.getName()); + + private final String name; + + public Client(String name) { + this.name = name; + } + + public void start() { + final Scanner scanner = new Scanner(System.in); + + try (final Socket socket = new Socket(InetAddress.getByName(Configs.CHAT_SERVER_ADDRESS), Configs.CHAT_SERVER_PORT)) { + LOGGER.info(String.format("Established connection to %s", socket.getRemoteSocketAddress().toString())); + + final DataOutputStream output = new DataOutputStream(socket.getOutputStream()); + final DataInputStream input = new DataInputStream(socket.getInputStream()); + + final Thread receivedMessagesHandler = new Thread(() -> { + try { + while (true) { + final String msg = input.readUTF(); + final LocalDateTime now = LocalDateTime.now().withNano(0); + System.out.println(String.format("[%s] %s", now.toString().replace('T', ' '), msg)); + } + } catch(IOException e){ + LOGGER.log(Level.SEVERE, "Cannot interact with the server"); + } + }); + + receivedMessagesHandler.start(); + + output.writeUTF(name); + + while (true) { + final String msgToSend = scanner.nextLine(); + output.writeUTF(msgToSend); + + if (msgToSend.trim().equalsIgnoreCase(Commands.EXIT.getCode())) { + break; + } + } + + socket.close(); + input.close(); + output.close(); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Cannot interact with the server", e); + } + } + + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("Please, specify your name as an argument when starting it."); + return; + } + + Client client = new Client(args[0]); + client.start(); + } +} \ No newline at end of file diff --git a/src/chat/common/Commands.java b/src/chat/common/Commands.java new file mode 100644 index 0000000..a940ed6 --- /dev/null +++ b/src/chat/common/Commands.java @@ -0,0 +1,16 @@ +package chat.common; + +public enum Commands { + + EXIT("/exit"), LIST("/list"); + + Commands(String code) { + this.code = code; + } + + private final String code; + + public String getCode() { + return code; + } +} diff --git a/src/chat/common/Configs.java b/src/chat/common/Configs.java new file mode 100644 index 0000000..02930ff --- /dev/null +++ b/src/chat/common/Configs.java @@ -0,0 +1,10 @@ +package chat.common; + +public final class Configs { + + private Configs() { } + + public static final int CHAT_SERVER_PORT = 23456; + public static final String CHAT_SERVER_ADDRESS = "127.0.0.1"; + public static final int THREAD_POOL_SIZE = 1; +} diff --git a/src/chat/serverside/ClientHandler.java b/src/chat/serverside/ClientHandler.java new file mode 100644 index 0000000..2c3f12c --- /dev/null +++ b/src/chat/serverside/ClientHandler.java @@ -0,0 +1,86 @@ +package chat.serverside; + +import chat.common.Commands; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ClientHandler extends Thread { + private static final Logger LOGGER = Logger.getLogger(ClientHandler.class.getName()); + + private final Server server; + private final Socket clientSocket; + + private final DataInputStream input; + private final DataOutputStream output; + + private final UUID clientId; + private String clientName = "Unknown"; + + public ClientHandler(UUID clientId, Server server, Socket client) throws IOException { + this.clientId = clientId; + this.server = server; + this.clientSocket = client; + this.input = new DataInputStream(client.getInputStream()); + this.output = new DataOutputStream(client.getOutputStream()); + } + + public void sendMsg(String msg) throws IOException { + output.writeUTF(msg); + output.flush(); + } + + @Override + public void run() { + try { + this.clientName = input.readUTF(); + + final String helloMsg = String.format("%s is ready to start chatting\n", clientName); + server.broadcastMessage(helloMsg); + + while (true) { + if (clientSocket.isClosed()) { + break; + } + + final String msg = input.readUTF(); + if (msg.trim().equalsIgnoreCase(Commands.EXIT.getCode())) { + disconnect(); + break; + } + + final String msgWithClient = clientName + ": " + msg; + LOGGER.info(msgWithClient); + server.broadcastMessage(msgWithClient); + } + } catch(IOException e){ + LOGGER.log(Level.SEVERE, String.format("Cannot interact with %s", clientName), e); + disconnect(); + } + } + + private void disconnect() { + try { + clientSocket.close(); + input.close(); + output.close(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, String.format("Cannot disconnect %s", clientName), e); + } + server.dropClient(clientId); + server.broadcastMessage(String.format("%s disconnected from the server\n", clientName)); + } + + public String getClientName() { + return clientName; + } + + public UUID getClientId() { + return clientId; + } +} diff --git a/src/chat/serverside/Server.java b/src/chat/serverside/Server.java new file mode 100644 index 0000000..1e49dd0 --- /dev/null +++ b/src/chat/serverside/Server.java @@ -0,0 +1,68 @@ +package chat.serverside; + +import chat.common.Configs; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Server { + private static final Logger LOGGER = Logger.getLogger(Server.class.getName()); + + private static ArrayList handlers = new ArrayList<>(); + + private final int port; + + public Server(int port) { + this.port = port; + } + + public void start() { + try (final ServerSocket server = new ServerSocket(port)) { + LOGGER.info(String.format("The chat server has been started on port %d", server.getLocalPort())); + while (true) { + LOGGER.info("Waiting for a client"); + final Socket client = server.accept(); + regNewClient(client); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, String.format("Cannot start server on %d", Configs.CHAT_SERVER_PORT), e); + } + } + + public synchronized void regNewClient(Socket client) { + try { + final ClientHandler clientThread = new ClientHandler(UUID.randomUUID(), this, client); + clientThread.start(); + handlers.add(clientThread); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Cannot register a new client", e); + } + } + + public synchronized void dropClient(UUID clientId) { + handlers.removeIf(client -> Objects.equals(client.getClientId(), clientId)); + } + + public synchronized void broadcastMessage(String msg) { + handlers.forEach(handler -> { + try { + handler.sendMsg(msg); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, String.format("Cannot send message to %s", handler.getClientName()), e); + } + }); + } + + public static void main(String[] args) { + final Server chatServer = new Server(Configs.CHAT_SERVER_PORT); + chatServer.start(); + } +} \ No newline at end of file