TCP/IP Server Using sockets Java

61 Views Asked by At

I am trying to make a server in Java to interact with browser using TCP/IP sockets. When the IP address is entered (localhost:8080), it should serve an HTML page with a button that sends a POST request to server which should just print "Hello from browser" + clientId (A client is given a random int ID when connecting to a server) in terminal. I am running into an issue where when entering the IP address it connects the user twice to the socket (maybe sending multiple resources) and when pressing the button to send the POST request it also acts as if a new user is connecting to the socket. Terminal Output

Below is my code for Server and the ClientHandler classes:

import java.io.*;
import java.net.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {
    private static final int PORT = 8080;
    private static final String HTML_FILE_PATH = "src/main/java/org/example/index.html"; 

    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newCachedThreadPool(); // For handling client threads

        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("Server started on port " + PORT);

            while (true) {
                try {
                    Socket clientSocket = serverSocket.accept();
                    ClientHandler clientHandler = new ClientHandler(clientSocket);
                    executor.submit(clientHandler); // Handle the client connection in a new thread
                } catch (IOException e) {
                    System.err.println("Error accepting client connection: " + e.getMessage());
                }
            }
        } finally {
            executor.shutdown(); // Properly shut down the executor service when the server is stopping
        }
    }
}
import java.io.*;
import java.net.*;
import java.nio.file.Files;
import java.util.concurrent.atomic.AtomicInteger;

public class ClientHandler implements Runnable {
    private static final AtomicInteger nextId = new AtomicInteger(0); // For generating unique IDs
    private Socket clientSocket;
    private int clientId;
    private static final String HTML_FILE_PATH = "src/main/java/org/example/index.html"; 

    public ClientHandler(Socket clientSocket) {
        this.clientSocket = clientSocket;
        this.clientId = nextId.incrementAndGet(); // Assign a unique ID to this client
    }

    @Override
    public void run() {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
             BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) {

            System.out.println("Client " + clientId + " connected: " + clientSocket.getInetAddress());

            // Handle the incoming HTTP request here
            String line;
            StringBuilder requestBuilder = new StringBuilder();
            while (!(line = reader.readLine()).isBlank()) {
                requestBuilder.append(line).append("\r\n");
            }
            String request = requestBuilder.toString();

            // Parse the request line
            String[] requestLines = request.split("\r\n");
            String[] requestLine = requestLines[0].split(" ");
            String method = requestLine[0];
            String path = requestLine[1];

            if (method.equals("GET")) {
                if (path.equals("/")) {
                    serveFile(HTML_FILE_PATH, writer);
                } else {
                    // Respond with a 404 (Not Found) for other GET requests
                    writeResponse(writer, "HTTP/1.1 404 Not Found", "text/plain", "Not Found".getBytes(), false);
                }
            } else if (method.equals("POST")) {
                // Read and print the data from the POST request
                int contentLength = getContentLength(requestLines);
                char[] body = new char[contentLength];
                reader.read(body, 0, contentLength);
                String postData = new String(body);
                String decodedData = URLDecoder.decode(postData, "UTF-8");
                System.out.println("POST data from Client " + clientId + ": " + decodedData);

                
                writeResponse(writer, "HTTP/1.1 200 OK", "text/plain", "Data received".getBytes(), false);
            }
            else {
                // Method not supported
                writeResponse(writer, "HTTP/1.1 405 Method Not Allowed", "text/plain", "Unsupported method".getBytes(), false);
            }

            writer.flush();
            clientSocket.close();
        } catch (IOException e) {
            System.err.println("Error handling client " + clientId + ": " + e.getMessage());
        }
    }

    private void serveFile(String filePath, BufferedWriter writer) throws IOException {
        File file = new File(filePath);
        if (file.exists()) {
            byte[] fileContent = Files.readAllBytes(file.toPath());
            writeResponse(writer, "HTTP/1.1 200 OK", "text/html", fileContent, false);
        } else {
            writeResponse(writer, "HTTP/1.1 404 Not Found", "text/plain", "File not found".getBytes(), false);
        }
    }

    private void writeResponse(BufferedWriter writer, String statusLine, String contentType, byte[] content, boolean keepAlive) throws IOException {
        writer.write(statusLine + "\r\n");
        writer.write("Content-Type: " + contentType + "\r\n");
        writer.write("Content-Length: " + content.length + "\r\n");
        if (keepAlive) {
            writer.write("Connection: keep-alive\r\n");
            writer.write("Keep-Alive: timeout=5, max=100\r\n"); 
        } else {
            writer.write("Connection: close\r\n");
        }
        writer.write("\r\n");
        writer.write(new String(content));
    }
    private int getContentLength(String[] requestHeaders) {
        for (String header : requestHeaders) {
            if (header.startsWith("Content-Length:")) {
                return Integer.parseInt(header.split(":")[1].trim());
            }
        }
        return 0;
    }

}

I am trying to make sure that once the IP address is entered by a user they stay in the server as one connection and not keep connecting again (Like a session) because I am planning on making a 2 player game based off of this simple implementation first.

I have tried using keep-alive header but that was not successful. Either wrong implementation or did not solve the problem

Assignment_Specification

0

There are 0 best solutions below