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.

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

Explanation

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.

Explore the dry run
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

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