← Back to Advanced Java Course | Chapter 7: Networking & APIs | Lesson 2 of 4

Java Socket Programming

ServerSocket Basics

ServerSocket is the class you use to write server applications: it binds to and listens on a specific port, then blocks on accept() until a client connects. Understanding that accept() blocks is important, because a naive server will only ever be able to talk to one client at a time unless you hand connections off to other threads.

Example: ServerSocket Basics

java
import java.net.ServerSocket;
import java.net.Socket;
public class Main {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(0); // OS picks a free port
		new Thread(() -> {
			try (Socket client = server.accept()) { // blocks until a client connects
				System.out.println("Client connected");
			} catch (Exception e) {}
		}).start();
		new Socket("localhost", server.getLocalPort());
		Thread.sleep(100);
		server.close();
	}
}

Socket Client Basics

The Socket class represents a client-side connection to a server. You connect by supplying the server's hostname and port to its constructor, at which point Java performs the TCP handshake and gives you input/output streams to actually exchange data.

Example: Socket Client Basics

java
import java.net.*;
public class Main {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(0);
		new Thread(() -> { try { server.accept(); } catch (Exception e) {} }).start();
		Socket client = new Socket("localhost", server.getLocalPort()); // TCP handshake happens here
		System.out.println("Connected: " + client.isConnected());
		client.close();
		server.close();
	}
}

Bidirectional Socket Communication

You can establish bidirectional communication by starting a ServerSocket in a background thread so it can accept connections without blocking the rest of your program, then connecting a client Socket to it from elsewhere. Once connected, both sides read and write independently over the same underlying TCP connection.

Example: Bidirectional Socket Communication

java
import java.net.*;
import java.io.*;
public class Main {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(0);
		Thread serverThread = new Thread(() -> {
			try (Socket s = server.accept()) {
				BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
				System.out.println("Server received: " + in.readLine());
				new PrintWriter(s.getOutputStream(), true).println("reply");
			} catch (Exception e) {}
		});
		serverThread.start();
		Socket client = new Socket("localhost", server.getLocalPort());
		new PrintWriter(client.getOutputStream(), true).println("hello");
		BufferedReader clientIn = new BufferedReader(new InputStreamReader(client.getInputStream()));
		System.out.println("Client received: " + clientIn.readLine());
		client.close();
		server.close();
	}
}

Multi-Threaded Server

To handle multiple clients simultaneously, a well-designed server delegates each accepted connection to its own thread (or a thread-pool task) rather than processing it inline on the accept loop. Without this, a single slow or hanging client would stall every other client waiting to be served.

Example: Multi-Threaded Server

java
import java.net.*;
public class Main {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(0);
		Runnable acceptLoop = () -> {
			try {
				for (int i = 0; i < 2; i++) {
					Socket client = server.accept();
					new Thread(() -> System.out.println("Handling a client on its own thread")).start(); // delegated, not inline
					client.close();
				}
			} catch (Exception e) {}
		};
		new Thread(acceptLoop).start();
		new Socket("localhost", server.getLocalPort()).close();
		new Socket("localhost", server.getLocalPort()).close();
		Thread.sleep(100);
		server.close();
	}
}

Closing Sockets Safely

Always close socket streams and connection resources once you're done with them, since leaked sockets can exhaust a server's available file descriptors under load. The try-with-resources statement handles this cleanup automatically, closing sockets and streams even if an exception is thrown mid-operation.

Example: Closing Sockets Safely

java
import java.net.*;
public class Main {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(0);
		new Thread(() -> { try { server.accept().close(); } catch (Exception e) {} }).start();
		try (Socket client = new Socket("localhost", server.getLocalPort())) { // try-with-resources closes automatically
			System.out.println("Connected and will auto-close");
		}
		server.close();
	}
}
🔒

Chapter Quiz — Complete all 4 topics to unlock

0/4 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.