Socket Programming in Java

Naveen AnamaneniUpdated · 8 min read

Socket programming means writing programs that talk to each other over a TCP connection. Java gives you two classes for it. A ServerSocket waits on a port for clients to connect. A Socket is one end of an established connection, with an input stream to read what the other side sends and an output stream to write to it. The client creates a Socket to connect; the server gets a Socket for each client from accept(). In this lesson, you will learn the steps on each side, the methods and exceptions involved, and build an echo server that serves many clients at the same time.

The Steps

ServerSocket accepts; each client gets its own Socket
StepServerClient
1new ServerSocket(port)
2accept() waits for a clientnew Socket(host, port) connects
3accept() returns a Socket
4Read and write through the streamsRead and write through the streams
5Close the Socket; go back to step 2Close the Socket

Methods

MethodDescription
new ServerSocket(port)It is used to listen on a port; port 0 picks any free one.
serverSocket.accept()It waits for a client and returns a Socket connected to it.
serverSocket.getLocalPort()It returns the port actually in use.
new Socket(host, port)It connects to a server.
socket.connect(address, timeoutMillis)It connects an unconnected Socket with a time limit.
socket.getInputStream()It returns the stream of data arriving from the other side.
socket.getOutputStream()It returns the stream for sending data to the other side.
socket.setSoTimeout(millis)It sets how long a read may wait before SocketTimeoutException.
socket.getRemoteSocketAddress()It returns the other side's address and port.
close()It closes the socket and its streams.

Checkpoint: An Echo Server with Many Clients

An echo server sends back what it receives. This one replies in upper case, says goodbye when a client sends bye, and serves three clients at the same time:

EchoServer.java▶ Try it
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 
public class EchoServer {
    // Server side: one virtual thread per connected client.
    static void serve(ServerSocket server) {
        try (ExecutorService pool =
                 Executors.newVirtualThreadPerTaskExecutor()) {
            while (true) {
                Socket client = server.accept();
                pool.submit(() -> handle(client));
            }
        } catch (IOException e) {
            // accept() fails when the server socket is closed: time to stop.
        }
    }
 
    static void handle(Socket client) {
        try (client;
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(client.getInputStream()));
             PrintWriter out =
                 new PrintWriter(client.getOutputStream(), true)) {
            String line;
            while ((line = in.readLine()) != null) {
                if (line.equals("bye")) {
                    out.println("Goodbye!");
                    break;
                }
                out.println("ECHO: " + line.toUpperCase());
            }
        } catch (IOException e) {
            System.out.println("Client problem: " + e.getMessage());
        }
    }
 
    // Client side: send two lines, read two replies, then say bye.
    static String talk(int port, String name) throws IOException {
        try (Socket socket = new Socket("localhost", port);
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(socket.getInputStream()));
             PrintWriter out =
                 new PrintWriter(socket.getOutputStream(), true)) {
            socket.setSoTimeout(3000);
            out.println("hello from " + name);
            String first = in.readLine();
            out.println("bye");
            String last = in.readLine();
            return name + " got [" + first + "] then [" + last + "]";
        }
    }
 
    public static void main(String[] args) throws Exception {
        try (ServerSocket server = new ServerSocket(0)) {
            int port = server.getLocalPort();
            Thread.ofVirtual().start(() -> serve(server));
 
            try (ExecutorService clients =
                     Executors.newVirtualThreadPerTaskExecutor()) {
                List<Future<String>> results = List.of(
                    clients.submit(() -> talk(port, "Navika")),
                    clients.submit(() -> talk(port, "Srihitha")),
                    clients.submit(() -> talk(port, "Advitha")));
                for (Future<String> r : results) {
                    System.out.println(r.get());
                }
            }
        }
        System.out.println("Server stopped");
    }
}
Output
Navika got [ECHO: HELLO FROM NAVIKA] then [Goodbye!]
Srihitha got [ECHO: HELLO FROM SRIHITHA] then [Goodbye!]
Advitha got [ECHO: HELLO FROM ADVITHA] then [Goodbye!]
Server stopped

Explanation:

  1. serve loops forever on accept(). Each time a client connects, it hands the new Socket to handle on a new virtual thread and immediately goes back to waiting. That is why one slow client can't hold up the others.
  2. handle wraps the socket's streams in a BufferedReader and a PrintWriter to work with lines of text. The true in new PrintWriter(..., true) turns on auto-flush, so each println is sent right away.
  3. readLine() returns null when the client closes the connection, which ends the loop. try (client; ...) closes the socket whatever happens.
  4. talk is a client: connect, set a read timeout, send a line, read the reply, send bye, read the goodbye.
  5. Stopping: when main leaves its try block, the ServerSocket is closed. The blocked accept() then throws an IOException, which serve treats as the signal to stop.

Before virtual threads, a server like this needed a thread pool, and the pool's size limited how many clients it could serve. With one virtual thread per client, the same simple blocking code can handle thousands of connections (see "Virtual Threads in Java").

What Can Go Wrong

Let's see an example of the three failures you will meet most often:

Fail.java▶ Try it
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
 
public class Fail {
    public static void main(String[] args) throws Exception {
        // 1. Nothing is listening on this port.
        int freePort;
        try (ServerSocket probe = new ServerSocket(0)) {
            freePort = probe.getLocalPort();
        }
        try (Socket s = new Socket()) {
            s.connect(new InetSocketAddress("localhost", freePort), 2000);
        } catch (IOException e) {
            System.out.println("1. " + e.getClass().getSimpleName());
        }
 
        // 2. The server accepts the connection but never sends anything.
        try (ServerSocket silent = new ServerSocket(0);
             Socket s = new Socket("localhost", silent.getLocalPort())) {
            s.setSoTimeout(300);
            s.getInputStream().read();
        } catch (IOException e) {
            System.out.println("2. " + e.getClass().getSimpleName());
        }
 
        // 3. Two servers try to use the same port.
        try (ServerSocket first = new ServerSocket(0);
             ServerSocket second = new ServerSocket(first.getLocalPort())) {
            System.out.println("should not happen");
        } catch (IOException e) {
            System.out.println("3. " + e.getClass().getSimpleName());
        }
    }
}
Output
1. ConnectException
2. SocketTimeoutException
3. BindException
ExceptionMeaningUsual fix
ConnectExceptionNothing is listening at that address and port ("connection refused")Check the host, the port, and that the server is running
SocketTimeoutExceptionA connect or a read took longer than the timeoutRetry, or report that the server is slow
BindExceptionThe port is already used by another programChoose another port, or stop the other program
UnknownHostExceptionThe host name can't be resolvedCheck the name and the DNS
SocketException: Connection resetThe other side closed the connection abruptlyReconnect; make the protocol tolerate it

All of them are subclasses of IOException.

Text and Binary Protocols

The echo server speaks a line-based text protocol: each message ends with a newline, so both sides know where a message stops. TCP itself has no message boundaries; it is one long stream of bytes. Every protocol must define its own, in one of two usual ways:

  • A delimiter: such as a newline. Simple and readable; HTTP headers work this way.
  • A length prefix: send the size first, then exactly that many bytes. DataOutputStream.writeInt and DataInputStream.readFully make this easy for binary data.

Common Mistakes

Forgetting to flush. With a buffered writer and no auto-flush, the message stays in the buffer and both sides wait for each other forever.

Handling clients one at a time. While the server talks to one client, the others wait. Use a thread per client.

Assuming one read returns one whole message. A single read may return part of a message, or parts of two. Use a delimiter or a length prefix.

Not closing sockets. Each open socket uses an operating system resource; leaks eventually stop the server from accepting anyone.

Interview Corner

How does a client-server program work with sockets in Java?

The server creates a ServerSocket bound to a port and calls accept(), which blocks until a client connects and then returns a Socket for that client. The client creates a Socket with the server's host and port. Each side reads from getInputStream() and writes to getOutputStream(), and closes the socket when finished. To serve several clients at once, the server handles each accepted socket on its own thread, traditionally from a thread pool and now often on a virtual thread per connection.

What is the difference between Socket and ServerSocket?

ServerSocket only listens for incoming connection requests on a port and creates sockets; it carries no data itself. Socket is an endpoint of an established connection and provides the input and output streams. A client uses only Socket; a server uses one ServerSocket plus one Socket per connected client.

Practice

  1. Easy: Add a command to the echo server: when a client sends time, reply with the current time.
  2. Harder: Turn the echo server into a chat room: keep the connected clients' writers in a thread-safe collection and send every line to all of them.

Quick check: Socket programming

4 questions
1What does ServerSocket.accept() return?
2What does readLine() return when the other side closes the connection?
3Which exception means nothing is listening on the port?
4Why set setSoTimeout on a socket?

Key Takeaways

Was this tutorial helpful?

Lesson 477 of 482