Notes covering Java TCP/IP, UDP, traditional blocking I/O, multithreading, NIO channels, polling and event-driven selectors.
- TCP provides reliable, ordered and two-way communication.
ServerSocketlistens for connections on a port.Socketrepresents a connection between one client and server.accept()waits for a client and returns itsSocket.getInputStream()receives data.getOutputStream()sends data.readLine()waits for a complete newline-terminated message.PrintWriter(..., true)automatically flushes data withprintln().
Client → Request → Server
Client ← Response ← Server
When accept() is called only once, the server accepts only one client.
Both methods are blocking:
serverSocket.accept(); // Waits for a client
input.readLine(); // Waits for client dataIf the connected client remains inactive, the server cannot process another client.
Placing accept() inside a loop lets the server accept clients sequentially:
Handle client A completely
→ Handle client B
→ Handle client C
It still does not handle them simultaneously.
socket.setSoTimeout(20_000);- Sets a read-inactivity timeout of 20 seconds.
- Causes a blocked read to throw
SocketTimeoutException. - It prevents an inactive client from blocking forever.
- It does not create concurrency.
The main thread accepts connections, while worker threads handle clients.
ExecutorService executor =
Executors.newFixedThreadPool(20);
executor.submit(() -> handleClientRequest(socket));- Up to 20 clients can be handled simultaneously.
- Extra tasks wait in the executor queue.
- One blocked client affects only its worker thread.
- Each worker may still block inside
readLine(). - Many idle connections can consume significant thread and memory resources.
NIO uses channels and buffers instead of streams.
| Traditional I/O | NIO |
|---|---|
ServerSocket |
ServerSocketChannel |
Socket |
SocketChannel |
| Input/output streams | ByteBuffer |
Important buffer operations:
buffer.flip(); // Write mode → read mode
buffer.clear(); // Prepare for new dataclear() resets the position and limit; it does not necessarily erase the old bytes.
Channel read results:
> 0: Bytes were received.0: No data is currently available.-1: The remote client disconnected.
Using channels alone does not make code non-blocking.
Enable non-blocking mode:
serverChannel.configureBlocking(false);
clientChannel.configureBlocking(false);In this mode:
accept()returnsnullif no client is waiting.read()returns0if no data is available.- One thread can repeatedly check multiple clients.
Is a new client connecting?
Does client A have data?
Does client B have data?
Repeat...
This is called polling. It supports multiple clients but wastes CPU by continuously checking inactive channels.
A Selector monitors multiple non-blocking channels and reports only the channels that are ready.
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
clientChannel.register(selector, SelectionKey.OP_READ);Main operations:
| Operation | Meaning |
|---|---|
OP_ACCEPT |
A client is ready to be accepted |
OP_CONNECT |
A connection can finish |
OP_READ |
Data is ready to read |
OP_WRITE |
Output can be written |
selector.select();select() efficiently blocks until any registered event occurs.
Processing flow:
Selector wakes
→ Get selected keys
→ Accept or read
→ Remove processed key
→ Wait again
Always remove processed selected keys:
iterator.remove();SelectionKey connects a channel, selector and registered operations.
key.channel(); // Registered channel
key.cancel(); // Cancel registrationA selector provides concurrency with one event-loop thread, not parallel execution.
Avoid blocking the event-loop thread with slow database calls, file operations, external HTTP calls or CPU-intensive work. This event-loop model is used by Netty and Spring WebFlux.
TCP is a byte stream and does not preserve message boundaries.
One read() may contain:
- Part of one message
- One complete message
- Multiple messages
Applications need message framing, such as newline-delimited or length-prefixed messages.
Non-blocking writes can also be partial. Large pending responses should be stored and completed using OP_WRITE.
UDP is connectionless and does not guarantee:
- Delivery
- Ordering
- Duplicate protection
It is useful when speed matters more than perfect delivery, such as audio/video streaming, gaming and real-time communication.
NIO uses:
DatagramChannelImportant methods:
channel.receive(buffer); // Receive a datagram
channel.send(buffer, address); // Send to a client addressUnlike TCP, UDP does not use accept() and does not create a separate connection for every client.
The UDP server registers one DatagramChannel for read events:
datagramChannel.register(
selector,
SelectionKey.OP_READ
);The main event-loop thread:
- Receives the client’s file request.
- Reads the client’s address.
- Submits audio streaming to the executor.
executor.submit(() ->
sendDataToClient(file, clientAddress, datagramChannel)
);Worker threads read the audio file using FileChannel and send it in UDP packets.
Important distinction:
Selectorprovides event-driven channel monitoring.ExecutorServicemakes audio streaming multithreaded.- A selector alone does not create multiple threads.
| Approach | Model | Main limitation |
|---|---|---|
| Single blocking server | One client | only one client is handled |
| Accept loop + timeout | Sequential clients | No simultaneous handling |
| Thread pool | Worker per active client | Many blocked threads |
| Non-blocking polling | One thread, many clients | Wastes CPU |
| Selector | Event-driven single thread | More complex state handling |
| Selector + thread pool | Event loop plus workers | Requires careful coordination |
Blocking I/O is simple and suitable for smaller systems. NIO selectors allow one thread to manage many connections efficiently. Thread pools should be used for blocking or long-running work so that the event-loop thread remains responsive.