Netty 4 Client-Server Communication Demo

Netty is an asynchronous event-driven network application framework that simplifies the development of high-performance protocol servers and clients. This article walks through a minimal but complete client-server communication example built with Netty 4.1, using the Reactor threading model (Main Reactor + Sub Reactors) and line-delimited string messages over TCP.

Server Implementation

The server is responsible for accepting connections, receiving messages, processing them, and sending responses back to the client. The implementation separates bootstrap configuration, channel pipeline setup, and business logic.

Bootstrap and Listening

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class AppServer {
    private static final int PORT = 8090;
    private static EventLoopGroup bossGroup = new NioEventLoopGroup();
    private static EventLoopGroup workerGroup = new NioEventLoopGroup();
    private static ServerBootstrap bootstrap = new ServerBootstrap();

    public static void main(String[] args) throws InterruptedException {
        try {
            bootstrap.group(bossGroup, workerGroup)
                     .channel(NioServerSocketChannel.class)
                     .childHandler(new ServerChannelInitializer());
            ChannelFuture future = bootstrap.bind(PORT).sync();
            System.out.println("Server started on port " + PORT);
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

Business Logic Handler

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Date;

public class ServerMessageHandler extends SimpleChannelInboundHandler<String> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("Server received: " + msg);
        if ("quit".equals(msg)) {
            ctx.close();
            return;
        }
        String reply = "Reply at " + new Date() + "\n";
        ctx.writeAndFlush(reply);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Remote client connected: " + ctx.channel().remoteAddress());
        ctx.writeAndFlush("Welcome, you are connected from " + ctx.channel().remoteAddress() + "\n");
        super.channelActive(ctx);
    }
}

Channel Initializer (Pipeline Setup)

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        // Frame decoder that splits messages on newline
        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new ServerMessageHandler());
    }
}

Client Implementation

The client connects to the server, sends a message, processes the response, and can send additional data.

Client Bootstrap

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.io.IOException;

public class ClientApp {
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 8090;
    private static EventLoopGroup group = new NioEventLoopGroup();
    private static Bootstrap bootstrap = new Bootstrap();
    private static Channel channel;

    public static void main(String[] args) throws InterruptedException, IOException {
        try {
            bootstrap.group(group)
                     .channel(NioSocketChannel.class)
                     .handler(new ClientChannelInitializer());
            channel = bootstrap.connect(HOST, PORT).sync().channel();
            sendMessage();
        } finally {
            // Group shutdown handled on exit
        }
    }

    private static void sendMessage() throws IOException {
        String message = "Hello from Netty client";
        channel.writeAndFlush(message + "\r\n");
        System.out.println("Client sent: " + message);
    }
}

Client Handler

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class ClientMessageHandler extends SimpleChannelInboundHandler<String> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("Client received: " + msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Connection established...");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Connection closed.");
        super.channelInactive(ctx);
    }
}

Client Channel Initializer

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new ClientMessageHandler());
    }
}

Example Output

After starting the server, runing the client yields messages similar to the screenshots below.

Server Console

Server console output

Client Console

Client console output

Tags: Netty Java NIO Client-Server Communication Network Programming

Posted on Mon, 29 Jun 2026 17:22:47 +0000 by btherl