Home/Learn/Java A–Z/Socket Programming

Socket Programming

Advanced
I/O and Networking

Java sockets enable low-level TCP and UDP communication between processes — the foundation of all networked Java applications.

Overview

Java sockets (java.net package) provide raw TCP and UDP communication. ServerSocket listens for incoming connections; Socket represents a connected endpoint. Each connection has an InputStream and OutputStream for data exchange. For high-concurrency servers, threads or NIO channels (SocketChannel, AsynchronousSocketChannel) replace the simple one-thread-per-connection model. Virtual threads (Java 21) make thread-per-connection scalable again.

TCP Server and Client

ServerSocket.accept() blocks until a client connects, returning a Socket. Read from socket.getInputStream() and write to socket.getOutputStream().

The server should handle each client in a separate thread to serve multiple clients concurrently.

TcpEcho.java
// --- Server ---
try (ServerSocket server = new ServerSocket(8080)) {
    System.out.println("Listening on port 8080...");
    while (true) {
        Socket client = server.accept(); // blocks
        // Handle each client in a thread
        Thread.ofVirtual().start(() -> handleClient(client));
    }
}

void handleClient(Socket socket) {
    try (socket;
         var in  = new BufferedReader(new InputStreamReader(socket.getInputStream()));
         var out = new PrintWriter(socket.getOutputStream(), true)) {
        String line;
        while ((line = in.readLine()) != null) {
            out.println("Echo: " + line);
        }
    } catch (IOException e) { /* log */ }
}

// --- Client ---
try (Socket socket = new Socket("localhost", 8080);
     var out = new PrintWriter(socket.getOutputStream(), true);
     var in  = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
    out.println("Hello, Server!");
    System.out.println(in.readLine()); // Echo: Hello, Server!
}

UDP with DatagramSocket

UDP is connectionless and does not guarantee delivery or order. DatagramSocket sends and receives DatagramPacket objects. Suitable for real-time applications (games, video) where low latency matters more than reliability.

UDP packets include the destination address in the packet itself, unlike TCP which establishes a connection.

UdpSocket.java
// UDP Server
try (DatagramSocket server = new DatagramSocket(9090)) {
    byte[] buf = new byte[256];
    DatagramPacket packet = new DatagramPacket(buf, buf.length);
    server.receive(packet); // blocks
    String msg = new String(packet.getData(), 0, packet.getLength());
    System.out.println("Received: " + msg);

    // Echo back
    DatagramPacket reply = new DatagramPacket(
        packet.getData(), packet.getLength(),
        packet.getAddress(), packet.getPort());
    server.send(reply);
}

// UDP Client
try (DatagramSocket client = new DatagramSocket()) {
    byte[] data = "Hello UDP".getBytes();
    InetAddress addr = InetAddress.getByName("localhost");
    DatagramPacket packet = new DatagramPacket(data, data.length, addr, 9090);
    client.send(packet);
}

Non-Blocking NIO Sockets

For high-concurrency servers, NIO SocketChannel with Selector enables a single thread to manage thousands of connections. The selector monitors multiple channels and notifies when they are ready to read/write.

This is the I/O multiplexing pattern (similar to select/epoll on Linux). Virtual threads (Project Loom, Java 21) offer an alternative: thread-per-connection with virtual threads is nearly as efficient as NIO selectors.

NioServer.java
// NIO selector-based server (single thread, many connections)
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false);

Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select(); // blocks until at least one event
    Set<SelectionKey> keys = selector.selectedKeys();
    Iterator<SelectionKey> iter = keys.iterator();
    while (iter.hasNext()) {
        SelectionKey key = iter.next();
        iter.remove();
        if (key.isAcceptable()) {
            SocketChannel client = serverChannel.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
        } else if (key.isReadable()) {
            // read from (SocketChannel) key.channel()
        }
    }
}

Key Points to Remember

  • ServerSocket.accept() blocks until a client connects; handle each client in a thread.
  • Virtual threads (Java 21) make thread-per-connection scalable — prefer over NIO selectors for new code.
  • UDP uses DatagramSocket/DatagramPacket; TCP uses Socket/ServerSocket.
  • NIO SocketChannel + Selector enables a single thread to multiplex many connections.
  • Always close sockets with try-with-resources to release OS file descriptors.

Practice Socket Programming in the Playground

Run and modify code directly in your browser - no setup needed.

Interview Questions

Sign in to ask Aria
1

What is the difference between TCP and UDP sockets in Java?

EasyAmazon
2

How do you handle multiple concurrent clients in a socket server?

MediumGoogle
3

What is the C10K problem and how does NIO Selector address it?

HardNetflix
4

How do virtual threads change the design of socket servers in Java 21?

HardOracle
5

What is the role of SelectionKey in NIO-based servers?

HardMicrosoft

Ask Aria about Socket Programming

Your personal AI tutor — ask anything about this concept