From 40fd4e5759502d6db4f1c73ea16426bfc838f1c4 Mon Sep 17 00:00:00 2001 From: Shash Date: Sun, 22 Jun 2025 09:50:14 +0530 Subject: [PATCH 1/2] comments --- SingleThreaded/Client.java | 20 +++++++++++++++++- SingleThreaded/Server.java | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/SingleThreaded/Client.java b/SingleThreaded/Client.java index 3c12d79..bf44881 100644 --- a/SingleThreaded/Client.java +++ b/SingleThreaded/Client.java @@ -10,16 +10,34 @@ public class Client { public void run() throws UnknownHostException, IOException{ - int port = 8090; + int port = 8010; InetAddress address = InetAddress.getByName("localhost"); + //InetAddress is used to get the IP address of the server locally + //getByName("localhost") returns the IP address of the server locally + //port is the port number of the server Socket socket = new Socket(address, port); + //Socket is used to connect to the server + //address is the IP address of the server PrintWriter toSocket = new PrintWriter(socket.getOutputStream(), true); + //print writer is used to send data to the server + //true is used to flush the output stream + //by flushing the output stream, the data is sent to the server + //socket.getOutputStream() returns the output stream of the server BufferedReader fromSocket = new BufferedReader(new InputStreamReader(socket.getInputStream())); + //buffered reader is used to read data from the server + //new InputStreamReader(socket.getInputStream()) returns the input stream of the server toSocket.println("Hello World from socket "+socket.getLocalSocketAddress()); + //println is used to send data to the server + //socket.getLocalSocketAddress() returns the local socket address of the client String line = fromSocket.readLine(); + //readLine is used to read data from the server + //fromSocket.readLine() returns the data from the server toSocket.close(); + //close is used to close the output stream fromSocket.close(); + //close is used to close the input stream socket.close(); + //close is used to close the socket } public static void main(String[] args) { diff --git a/SingleThreaded/Server.java b/SingleThreaded/Server.java index fc43b97..9c23017 100644 --- a/SingleThreaded/Server.java +++ b/SingleThreaded/Server.java @@ -8,17 +8,60 @@ public class Server { + //This model is simple to understand but impractical for real-world use. + //If one client connects and the server is doing some time-consuming work, all other clients have to wait. + //This is because the server is single-threaded and can only handle one client at a time. + //This is not efficient and is not scalable. + //This is why we need to use a multi-threaded server. + //A multi-threaded server can handle multiple clients at the same time. + //This is more efficient and is scalable. + //This is why we need to use a multi-threaded server. + public void run() throws IOException, UnknownHostException{ + //we are throwing an exception because we are not handling it + //we are not handling the exception because we are not using a try-with-resources block + + //IOE exception is thrown when there is an error in the input/output stream + //UnknownHostException is thrown when the host is not found + + //listening on port 8010 int port = 8010; + + //creating a server socket ServerSocket socket = new ServerSocket(port); + + //setting a timeout of 20 seconds socket.setSoTimeout(20000); + + //infinite loop to accept connections while(true){ + //printing the port number System.out.println("Server is listening on port: "+port); + + //accepting a connection from the client Socket acceptedConnection = socket.accept(); + + //printing the remote socket address System.out.println("Connected to "+acceptedConnection.getRemoteSocketAddress()); + //getRemoteSocketAddress() returns the remote socket address of the client + //getInetAddress() returns the local socket address of the server + //getPort() returns the port number of the client + //getAddress() returns the IP address of the client + //getPort() returns the port number of the server + //getAddress() returns the IP address of the server + //getInetAddress() returns the local socket address of the server + + //creating a print writer to send data to the client PrintWriter toClient = new PrintWriter(acceptedConnection.getOutputStream(), true); + //print writer is used to send data to the client + //true is used to flush the output stream + //acceptedConnection.getOutputStream() returns the output stream of the client + //acceptedConnection.getInputStream() returns the input stream of the client BufferedReader fromClient = new BufferedReader(new InputStreamReader(acceptedConnection.getInputStream())); + //buffered reader is used to read data from the client + //new InputStreamReader(acceptedConnection.getInputStream()) returns the input stream of the client toClient.println("Hello World from the server"); + //print writer is used to send data to the client } } From 62ecef9c24ac90776584591a93bbe4c1538d4c46 Mon Sep 17 00:00:00 2001 From: Shash Date: Sun, 22 Jun 2025 10:18:48 +0530 Subject: [PATCH 2/2] add:(comments) added in comments at each line, so new people would not get stuck in the heavy terminlogies. also explained and recaped some of the intuition mentioned in the video --- Multithreaded/Client.java | 28 ++++++++++++++++++++++++++++ Multithreaded/Server.java | 29 +++++++++++++++++++++++++++-- ThreadPool/Server.java | 32 +++++++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/Multithreaded/Client.java b/Multithreaded/Client.java index fa55495..d042d76 100644 --- a/Multithreaded/Client.java +++ b/Multithreaded/Client.java @@ -9,21 +9,43 @@ public class Client { + //the client is desinged to test the concurrent capabilities of the serer by + //simulating manny clinet connecctions at once. + //it creates a new thread for each client and sends a message to the server. + //it then reads the response from the server and prints it to the console. + //it does this for 100 clients. + //the server is designed to handle multiple clients concurrently. + public Runnable getRunnable() throws UnknownHostException, IOException { + //runnable is a functional interface that takes no arguments and returns nothing return new Runnable() { @Override + //override is used to override the run method of the runnable interface public void run() { int port = 8010; + //port is the port number of the server try { + //InetAddress is used to get the IP address of the server locally InetAddress address = InetAddress.getByName("localhost"); + //Socket is used to connect to the server Socket socket = new Socket(address, port); + //PrintWriter is used to send data to the server + //true is used to flush the output stream + //socket.getOutputStream() returns the output stream of the server try ( + //print writer in this try block is used to send data to the server PrintWriter toSocket = new PrintWriter(socket.getOutputStream(), true); + //buffered reader here is used to read data from the server + //new InputStreamReader(socket.getInputStream()) returns the input stream of the server BufferedReader fromSocket = new BufferedReader(new InputStreamReader(socket.getInputStream())) ) { + toSocket.println("Hello from Client " + socket.getLocalSocketAddress()); String line = fromSocket.readLine(); + //readLine is used to read data from the server + //fromSocket.readLine() returns the data from the server System.out.println("Response from Server " + line); + //println is used to print the response from the server } catch (IOException e) { e.printStackTrace(); } @@ -38,8 +60,14 @@ public void run() { public static void main(String[] args){ Client client = new Client(); + //client is the object of the client class + //for loop is used to create 100 threads for(int i=0; i<100; i++){ try{ + //thread is the object of the thread class + //new Thread(client.getRunnable()) is used to create a new thread + //client.getRunnable() is used to get the runnable object of the client class + Thread thread = new Thread(client.getRunnable()); thread.start(); }catch(Exception ex){ diff --git a/Multithreaded/Server.java b/Multithreaded/Server.java index e72afe9..c0737bb 100644 --- a/Multithreaded/Server.java +++ b/Multithreaded/Server.java @@ -5,9 +5,18 @@ import java.util.function.Consumer; public class Server { + + //we will handle each client in a separate thread public Consumer getConsumer() { + //Consumer is a functional interface that takes a single argument and returns nothing + //clientSocket is the socket of the client + //PrintWriter is used to send data to the client + //true is used to flush the output stream + //clientSocket.getOutputStream() returns the output stream of the client return (clientSocket) -> { + //lambda expression is used to create a new thread for each client try (PrintWriter toSocket = new PrintWriter(clientSocket.getOutputStream(), true)) { + //clientSocket.getInetAddress() returns the IP address of the client toSocket.println("Hello from server " + clientSocket.getInetAddress()); } catch (IOException ex) { ex.printStackTrace(); @@ -23,12 +32,28 @@ public static void main(String[] args) { ServerSocket serverSocket = new ServerSocket(port); serverSocket.setSoTimeout(70000); System.out.println("Server is listening on port " + port); + //infinite loop to accept connections while (true) { + //accepting a connection from the client Socket clientSocket = serverSocket.accept(); - - // Create and start a new thread for each client + //serverSocket.accept() is a blocking call. + //the programs execution pauses here until a client connects. + //when a connection is made, it returns a socket objeect. + //this socket object is then passed to the getConsumer() method. + + + //creating a new thread for each client + //getConsumer() is a method that returns a Consumer + //accept(clientSocket) is a method that accepts a Socket + //server.getConsumer().accept(clientSocket) is a method that accepts a Socket + //server.getConsumer() is a method that returns a Consumer + //server.getConsumer().accept(clientSocket) is a method that accepts a Socket Thread thread = new Thread(() -> server.getConsumer().accept(clientSocket)); thread.start(); + + //every time a client connects, this upper code will start a new thread. + //the actual contet of the code that is to be run is ddefined in the getConsumer() method. + //where it handles the client handling logic. } } catch (IOException ex) { ex.printStackTrace(); diff --git a/ThreadPool/Server.java b/ThreadPool/Server.java index 8b3aa6b..fdc707e 100644 --- a/ThreadPool/Server.java +++ b/ThreadPool/Server.java @@ -5,14 +5,37 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; + +//the thread pool method is a significant improvement over the +//client-server model, yes even the multithreaded model. +//creating a new thread for each client is expensive in computational terms. + +//even tho, thread pool is single threaded it provides better efficiency. +// it maintains a fixed number of worker threads. These threads are reused to handle incoming client requests. +//this reduces the overhead of creating and destroying threads. +//this also reduces the overhead of context switching. +//this also reduces the overhead of synchronization. +//this also reduces the overhead of memory allocation. +//this also reduces the overhead of memory deallocation. +//this also reduces the overhead of memory allocation. + public class Server { private final ExecutorService threadPool; - +//The ExecutorService is a high-level API from Java's concurrency framework for managing threads. +//it provides a pool of threads that can be reused to handle incoming client requests. public Server(int poolSize) { this.threadPool = Executors.newFixedThreadPool(poolSize); + + // initializes the pool. Executors.newFixedThreadPool(10) creates a pool with exactly 10 worker threads. + // These 10 threads will be created once and then reused for the lifetime of the server. } public void handleClient(Socket clientSocket) { + + + //This method contains the logic for how to interact with a connected client. In this case, it's very simple + // it opens a PrintWriter, sends a "Hello" message, and the try-with-resources block ensures the writer (and underlying socket stream) is closed automatically. + //Crucially, this method does not create a thread. It's just a plain method that will be executed by a thread from the pool. try (PrintWriter toSocket = new PrintWriter(clientSocket.getOutputStream(), true)) { toSocket.println("Hello from server " + clientSocket.getInetAddress()); } catch (IOException ex) { @@ -30,6 +53,13 @@ public static void main(String[] args) { serverSocket.setSoTimeout(70000); System.out.println("Server is listening on port " + port); + +// This is the most important part. When a client connects (serverSocket.accept()), the server executes this line: +// server.threadPool.execute(() -> server.handleClient(clientSocket)); +// execute() is a method of the ExecutorService. It takes a Runnable task and assigns it to one of the available threads in the pool. +// If all 10 threads are busy handling other clients, the new task is placed in a queue. As soon as a thread finishes its current task, it will pick up the next task from the queue. +// The main server loop does not wait. It immediately loops back to accept() to listen for the next client. This ensures the server remains highly responsive. + while (true) { Socket clientSocket = serverSocket.accept();