/** * ================================================================================ * SiPro SocketArena™ - Question 2: TCP Concurrent Even/Odd Game Server * ================================================================================ * COURSE : Distributed Computing & Systems Programming * ASSIGNMENT : Q2 - Concurrent Multi-Client TCP Server for Even/Odd Game * LANGUAGE : Java (SE 11+) * ARCHITECTURE : Concurrent TCP Server. For each connected client, the server * forks/spawns an independent worker thread to handle the game * session concurrently without blocking other players. * PROTOCOL : RFC-SIPRO-EVENODD (ASCII Line-Delimited TCP Protocol) * * GAME RULES: * 1. The client connects and sends: "PLAY " * 2. The client picks an integer number between 1 and 10 and guesses "EVEN" or "ODD". * Client command syntax: "GUESS " * 3. The server generates its own random number (1 to 10). * 4. The server calculates the total sum: (clientNumber + serverNumber). * 5. If the sum is even and the client guessed "EVEN", the client WINS. * If the sum is odd and the client guessed "ODD", the client WINS. * Otherwise, the client LOSES. * 6. The server transmits the outcome frame to the client: * "RESULT " * * STUDENT INSTRUCTIONS: * Complete all 10 fill-in-the-blank sections marked with: * // >>> FILL IN THE BLANK [X]: ... <<< * * COMPILATION & EXECUTION: * javac EvenOddServer.java * java EvenOddServer * ================================================================================ */ import java.io.*; import java.net.*; import java.util.Random; public class EvenOddServer { public static final String HOST = "0.0.0.0"; public static final int PORT = 9098; /** * Dedicated Client Worker Task. * Manages an isolated client game session concurrently. */ static class ClientHandler implements Runnable { private final Socket clientSocket; private final Random random; public ClientHandler(Socket socket) { this.clientSocket = socket; this.random = new Random(); } @Override public void run() { String clientEndpoint = clientSocket.getRemoteSocketAddress().toString(); System.out.println("\n[CONCURRENT WORKER SPAWNED] Active session for: " + clientEndpoint); try ( BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream()), true) ) { // Initial Greeting & Instructions out.println("READY RFC-SIPRO-EVENODD Server v1.0. Send: PLAY "); String playerName = "Player"; boolean active = true; while (active) { // Read incoming command line from client String line = in.readLine(); if (line == null) { System.out.println("[" + clientEndpoint + "] Client closed connection."); break; } line = line.trim(); if (line.isEmpty()) continue; String[] tokens = line.split("\\s+"); String command = tokens[0].toUpperCase(); switch (command) { case "PLAY": if (tokens.length > 1) { playerName = tokens[1]; } System.out.println("[" + clientEndpoint + "] Registered player: " + playerName); out.println("WELCOME " + playerName + " Pick a number (1-10) and guess (EVEN or ODD). Syntax: GUESS "); break; case "GUESS": // Syntax: GUESS // >>> FILL IN THE BLANK [6]: Validate that tokens array contains exactly 3 parts <<< // boolean isValidFormat = (tokens.length == 3); boolean isValidFormat = false; // REPLACE THIS LINE if (!isValidFormat) { out.println("ERROR Invalid syntax. Usage: GUESS "); break; } int clientNumber; try { clientNumber = Integer.parseInt(tokens[1]); } catch (NumberFormatException e) { out.println("ERROR Number must be an integer between 1 and 10."); break; } String clientGuess = tokens[2].toUpperCase(); // >>> FILL IN THE BLANK [7]: Validate range (1 <= clientNumber <= 10) and guess ("EVEN" or "ODD") <<< // boolean isValidRange = (clientNumber >= 1 && clientNumber <= 10) && ("EVEN".equals(clientGuess) || "ODD".equals(clientGuess)); boolean isValidRange = false; // REPLACE THIS LINE if (!isValidRange) { out.println("ERROR Number must be 1..10 and guess must be 'EVEN' or 'ODD'."); break; } // >>> FILL IN THE BLANK [8]: Generate a server random number between 1 and 10 inclusive <<< // int serverNumber = random.nextInt(10) + 1; int serverNumber = 0; // REPLACE THIS LINE // >>> FILL IN THE BLANK [9]: Calculate sum and determine if sum is Even or Odd <<< // int sum = clientNumber + serverNumber; // boolean isSumEven = (sum % 2 == 0); // String actualParity = isSumEven ? "EVEN" : "ODD"; int sum = 0; // REPLACE THIS LINE String actualParity = null; // REPLACE THIS LINE // Determine if client won or lost boolean clientWon = clientGuess.equals(actualParity); String outcome = clientWon ? "WIN" : "LOSS"; String message = clientWon ? "Congratulations! " + sum + " is " + actualParity + ". You guessed correctly!" : "Sorry! " + sum + " is " + actualParity + ". You guessed " + clientGuess + "."; // Transmit outcome frame to client // Protocol: RESULT String responseFrame = String.format("RESULT %s %d %d %d %s %s", outcome, clientNumber, serverNumber, sum, actualParity, message); out.println(responseFrame); System.out.printf("[%s] Player '%s' -> ClientNum:%d, ServerNum:%d, Sum:%d (%s), Guess:%s => Outcome: %s%n", clientEndpoint, playerName, clientNumber, serverNumber, sum, actualParity, clientGuess, outcome); break; case "QUIT": out.println("BYE Thanks for playing Even/Odd!"); active = false; break; default: out.println("ERROR Unrecognized command. Available: PLAY , GUESS , QUIT"); break; } } } catch (IOException e) { System.err.println("[" + clientEndpoint + "] Network I/O exception: " + e.getMessage()); } finally { // >>> FILL IN THE BLANK [10]: Ensure the client socket is closed cleanly <<< // try { clientSocket.close(); } catch (IOException ignored) {} System.out.println("[" + clientEndpoint + "] Worker thread terminated and socket closed.\n"); } } } /** * Master TCP Server Daemon Loop. */ public static void main(String[] args) { System.out.println("===================================================================="); System.out.println(" SiPro SocketArena™ - Question 2: Concurrent TCP Even/Odd Server"); System.out.println(" Listening on: " + HOST + ":" + PORT); System.out.println(" Concurrency : Dedicated Worker Thread Per Client (Fork / Thread)"); System.out.println(" Protocol : RFC-SIPRO-EVENODD"); System.out.println("===================================================================="); ServerSocket serverSocket = null; try { // >>> FILL IN THE BLANK [1]: Instantiate master ServerSocket <<< // serverSocket = new ServerSocket(); serverSocket = null; // REPLACE THIS LINE // >>> FILL IN THE BLANK [2]: Enable immediate address reuse (SO_REUSEADDR) <<< // serverSocket.setReuseAddress(true); // REPLACE THIS LINE // >>> FILL IN THE BLANK [3]: Bind ServerSocket to local endpoint on port 9098 <<< // serverSocket.bind(new InetSocketAddress(PORT)); // REPLACE THIS LINE System.out.println("[DAEMON READY] Listening on port " + PORT + ". Awaiting concurrent players...\n"); while (true) { // >>> FILL IN THE BLANK [4]: Accept incoming client TCP connection <<< // Socket clientSocket = serverSocket.accept(); Socket clientSocket = null; // REPLACE THIS LINE if (clientSocket == null) { continue; } System.out.println("[TCP INGRESS] Connection accepted from: " + clientSocket.getRemoteSocketAddress()); // >>> FILL IN THE BLANK [5]: Spawn / fork a new concurrent worker thread for the client <<< // Thread workerThread = new Thread(new ClientHandler(clientSocket)); // workerThread.start(); // REPLACE THIS BLOCK WITH THREAD CREATION AND START } } catch (IOException e) { System.err.println("[SERVER ERROR] " + e.getMessage()); } finally { if (serverSocket != null && !serverSocket.isClosed()) { try { serverSocket.close(); } catch (IOException ignored) {} } } } }