Java: a TCP echo server on the loopback interface
Open a server socket on an ephemeral port and connect to it with a client socket in the same program.
On this page
Java threads and I/O streams.
- Open a server socket on an ephemeral port and connect to it with a client socket in the same program.
- Predict the sample output and explain the state changes.
- Complete the practice task and check the boundary cases.
The concept
TCP gives two programs a reliable, ordered byte stream. A server creates a ServerSocket bound to an address and port and calls accept, which blocks until a client connects. The client creates a Socket to that address and port. Each side then reads and writes through input and output streams.
Passing port 0 asks the operating system for any free port, and getLocalPort reports which one was chosen. Binding to the loopback address keeps all traffic inside the machine. Here the server runs on a second thread, reads one line, and replies with an echo. try-with-resources closes the sockets even if an error occurs.
Code example
import java.io.*;
import java.net.*;
class Main {
public static void main(String[] args) throws Exception {
try (ServerSocket server = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) {
int port = server.getLocalPort();
Thread serverThread = new Thread(() -> {
try (Socket client = server.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {
String line = in.readLine();
out.println("echo: " + line);
} catch (IOException e) {
System.out.println("server error: " + e.getMessage());
}
});
serverThread.start();
try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), port);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
out.println("hello");
System.out.println(in.readLine());
}
serverThread.join();
}
}
}echo: hello
Follow the code
Open the walkthrough to inspect the example one step at a time.
Show guided walkthrough
import java.io.*;import java.net.*; class Main { public static void main(String[] args) throws Exception { try (ServerSocket server = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) { int port = server.getLocalPort(); Thread serverThread = new Thread(() -> { try (Socket client = server.accept(); BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); PrintWriter out = new PrintWriter(client.getOutputStream(), true)) { String line = in.readLine(); out.println("echo: " + line); } catch (IOException e) { System.out.println("server error: " + e.getMessage()); } }); serverThread.start(); try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), port); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { out.println("hello"); System.out.println(in.readLine()); } serverThread.join(); } }}Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| Listen on the loopback address; port 0 lets the OS pick a free port. | server bound to 127.0.0.1 | — | |
| The server thread waits here until the client connects. | server bound to 127.0.0.1 | — | |
| The client connects to the chosen port. | connection established | — | |
| The client sends one line of text over the stream. | sent: hello | — | |
| The client reads the server's reply and prints it. | received: echo: hello | echo: hello |
Behind the scenes
How Java processes this code
The Java compiler produces bytecode. The JVM loads the class and invokes its main method.
Cost and common mistakes
O(m) time for a message of m bytes, plus the connection setup round trip.
- A TCP stream has no message boundaries. Real protocols need a delimiter, such as the newline used here, or a length prefix.
- Reading with readLine blocks forever if the other side never sends a newline.
- Binding a server to all addresses (0.0.0.0) exposes it to the network; use loopback while learning.
Try it yourself
How would the server handle several clients one after another?
Reveal answer and reasoning
Wrap accept and the read/reply code in a loop, or hand each accepted socket to a new thread or an executor.