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