Java · Course navigation

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
Before you start

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

Java
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();
    }
  }
}
Output
echo: hello

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1import java.io.*;
2import java.net.*;
3
4class Main {
5 public static void main(String[] args) throws Exception {
6 try (ServerSocket server = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) {
7 int port = server.getLocalPort();
8 Thread serverThread = new Thread(() -> {
9 try (Socket client = server.accept();
10 BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
11 PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {
12 String line = in.readLine();
13 out.println("echo: " + line);
14 } catch (IOException e) {
15 System.out.println("server error: " + e.getMessage());
16 }
17 });
18 serverThread.start();
19
20 try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), port);
21 PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
22 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
23 out.println("hello");
24 System.out.println(in.readLine());
25 }
26 serverThread.join();
27 }
28 }
29}

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
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput 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.

Further reading