Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
140: Sockets in Java
I've seen this happen to almost every developer the first time they dive into Java networking. You write a server, you write a client, you run them both, and... nothing. The client is sitting there waiting, the server is running, but neither is talking. You'll probably spend an hour checking your firewall settings before you realize the bug is actually in your code.
// The "Hanging" Server
ServerSocket serverSocket = new ServerSocket(8080);
Socket clientSocket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
System.out.println("Waiting for message...");
String message = in.readLine(); // The program stops here forever
System.out.println("Received: " + message);
If you run this and then send a message from a client using a simple OutputStream.write(), the server will just sit there. It's not crashed; it's blocked. I've spent way too many late nights debugging this exact scenario.
The Silent Hang: Why your Server isn't Responding
The culprit here is readLine(). In the world of TCP sockets, data is treated as a continuous stream of bytes. There are no "messages" or "packets" at the application level—just a flow. The BufferedReader.readLine() method specifically waits until it encounters a line feed (\n) or a carriage return (\r\n).
If your client sends "Hello World" but doesn't append a newline character, readLine() will keep waiting, thinking the message is still being typed or transmitted. It will block the entire thread until that character arrives or the connection is dropped. This is a fundamental shift from how we handle files or console input; in networking, you have to be explicit about where a message ends.
The Fix: Implementing the Newline Terminator
To fix this, you have two real options. You can either manually append \n to your output, or use a PrintWriter with "auto-flush" enabled. I highly recommend PrintWriter because it handles the character encoding for you and makes the code much cleaner.
// The Fixed Client
Socket socket = new Socket("localhost", 8080);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 'true' enables auto-flush
out.println("Hello Server!"); // println adds the necessary newline
out.close();
By using println(), we ensure the server's readLine() sees the terminator and finally wakes up. The true argument in the PrintWriter constructor is also critical. Without auto-flush, Java might buffer the data internally to be "efficient," meaning the data stays in your RAM and never actually hits the network wire.
Managing Concurrent Clients with Threads
If you use the code above, your server can only handle one person at a time. If a second client tries to connect while the first one is still talking, they'll be queued up in the OS backlog and appear to be frozen. Since serverSocket.accept() and in.readLine() are both blocking calls, you can't do two things at once on a single thread.
The standard pattern is to hand off the connected socket to a new thread immediately after acceptance. I usually wrap this in a Runnable or use an ExecutorService for better resource management. This allows the main thread to loop back to accept() and wait for the next visitor while the other thread handles the conversation.
Cleaning up the Socket Mess
Sockets are "heavy" resources. They tie up a file descriptor in the operating system. If you keep opening sockets without closing them, you'll eventually hit the "Too many open files" error, and your server will refuse all new connections.
Always wrap your sockets and streams in a try-with-resources block. This ensures that even if an IOException occurs halfway through a conversation, the port is released and the connection is severed properly. Remember that closing the BufferedReader also closes the underlying Socket, so you don't need to call socket.close() separately if you've wrapped the stream.
📋 Practical Task
Build a Remote System Status Monitor
Your task is to create a server-client pair where the server acts as a "System Monitor" and the client acts as the "Dashboard".
- The Server:
- Must listen on port 9999.
- When a client connects, the server should send a single string containing the current available memory (use
Runtime.getRuntime().freeMemory()) and the current time. - The server must use a
PrintWriterwith auto-flush to ensure the client receives the data immediately. - The server should close the connection immediately after sending the status.
- The Client:
- Must connect to
localhost:9999. - Must read the response using
BufferedReader.readLine(). - Must print the received status to the console in a readable format (e.g., "System Status: [Received Message]").
- Must connect to
Requirement: Ensure both the server and client use try-with-resources to prevent port-leakage.
There are no comments for now.