Java NIO Channels and Buffers
A channel is a connection that moves bytes into and out of a buffer. A ByteBuffer is the fixed-size block of memory that holds those bytes during the move.
A buffer fills, flips, and drains
This program writes five bytes into a buffer. It prints the position and the remaining count after each step.
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.put(StandardCharsets.UTF_8.encode("hello"));
System.out.println("After put: position=" + buffer.position() + ", remaining=" + buffer.remaining());
buffer.flip();
System.out.println("After flip: position=" + buffer.position() + ", remaining=" + buffer.remaining());
String text = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println("Text: " + text);
System.out.println("After drain: position=" + buffer.position() + ", remaining=" + buffer.remaining());
}
}A file channel writes and reads back
One channel writes a line to a relative file. A second channel reads the same file and prints its content.
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class Main {
public static void main(String[] args) throws Exception {
Path path = Path.of("channel-note.txt");
String line = "Reading through a channel";
try (FileChannel out = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
out.write(StandardCharsets.UTF_8.encode(line));
}
try (FileChannel in = FileChannel.open(path, StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(64);
in.read(buffer);
buffer.flip();
System.out.println(StandardCharsets.UTF_8.decode(buffer).toString());
}
}
}Example
import java.net.StandardProtocolFamily;
import java.net.UnixDomainSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws Exception {
Path path = Path.of("echo.sock");
Files.deleteIfExists(path);
UnixDomainSocketAddress address = UnixDomainSocketAddress.of(path);
CountDownLatch bound = new CountDownLatch(1);
Thread server = new Thread(() -> {
try (ServerSocketChannel serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
serverChannel.bind(address);
bound.countDown();
try (SocketChannel channel = serverChannel.accept()) {
ByteBuffer buffer = ByteBuffer.allocate(64);
channel.read(buffer);
buffer.flip();
String text = StandardCharsets.UTF_8.decode(buffer).toString();
channel.write(StandardCharsets.UTF_8.encode("Echo: " + text));
}
} catch (Exception e) {
System.out.println("Server error: " + e);
}
});
server.start();
bound.await();
try (SocketChannel client = SocketChannel.open(StandardProtocolFamily.UNIX)) {
client.connect(address);
client.write(StandardCharsets.UTF_8.encode("hello channels"));
ByteBuffer buffer = ByteBuffer.allocate(64);
client.read(buffer);
buffer.flip();
System.out.println("Client received: " + StandardCharsets.UTF_8.decode(buffer).toString());
}
server.join();
Files.deleteIfExists(path);
}
}Key points
- A ByteBuffer has a position, a limit, and a capacity.
- Writing fills the buffer from its position forward.
- flip switches the buffer from writing to reading.
- A FileChannel moves bytes between a file and a buffer.
- A SocketChannel moves bytes between a program and a network address.
- Channels and buffers move bytes in blocks, and the buffer numbers show each step.
In this example, a server channel and a client channel share one Unix domain address inside one program, and the client prints the echo that comes back through its buffer.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does flip do to a ByteBuffer?