Integrating Protobuf with Netty for Java Object Transmission
This guide walks through building a Netty server and client that exchange Java objects using Protobuf serialization, covering .proto definition, code generation with protoc, pipeline configuration with Netty's Protobuf handlers, and full example implementations for both sides.
The article demonstrates how to use Netty socket programming together with Google Protobuf to serialize and transmit Java objects between a server and a client.
First, a Person.proto file is created under a protobuf directory with the following content:
syntax = "proto3";
package com.badao.protobuf;
option optimize_for = SPEED;
option java_package = "com.badao.NettyProtobuf";
option java_outer_classname = "BadaoDataInfo";
message Person {
string name = 1;
int32 age = 2;
string address = 3;
}The protoc compiler is then invoked to generate Java classes:
protoc --java_out=src/main/java src/protobuf/Person.protoThis produces BadaoDataInfo.Person in the com.badao.NettyProtobuf package.
Next, a Netty server is implemented. The NettyProtobufServer class creates boss and worker EventLoopGroup s, configures a ServerBootstrap, adds the custom initializer NettyProtobufInitializer, binds to port 70, and shuts down gracefully:
package com.badao.NettyProtobuf;
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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
public class NettyProtobufServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new NettyProtobufInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(70).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}The initializer configures the channel pipeline with Netty's built‑in Protobuf handlers and a custom handler:
package com.badao.NettyProtobuf;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
public class NettyProtobufInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(BadaoDataInfo.Person.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new NettyProtobufHandler());
}
}The custom handler simply prints the received Person fields:
package com.badao.NettyProtobuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class NettyProtobufHandler extends SimpleChannelInboundHandler<BadaoDataInfo.Person> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, BadaoDataInfo.Person msg) throws Exception {
System.out.println(msg.getName());
System.out.println(msg.getAge());
System.out.println(msg.getAddress());
}
}On the client side, NettyProtobufClient sets up a Bootstrap, connects to localhost:70, and uses a matching initializer:
package com.badao.NettyProtobuf;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
public class NettyProtobufClient {
public static void main(String[] args) throws Exception {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new NettyProtoBufClientInitializer());
ChannelFuture channelFuture = bootstrap.connect("localhost", 70);
channelFuture.channel().closeFuture().sync();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
}The client initializer mirrors the server pipeline:
package com.badao.NettyProtobuf;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
public class NettyProtoBufClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(BadaoDataInfo.Person.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new NettyProtobufClientHandler());
}
}The client handler builds a Person message when the channel becomes active and sends it to the server:
package com.badao.NettyProtobuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class NettyProtobufClientHandler extends SimpleChannelInboundHandler<BadaoDataInfo.Person> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, BadaoDataInfo.Person msg) throws Exception {
// No processing needed for this demo
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
BadaoDataInfo.Person person = BadaoDataInfo.Person.newBuilder()
.setName("公众号:霸道的程序猿")
.setAge(100)
.setAddress("中国")
.build();
ctx.channel().writeAndFlush(person);
}
}Running the server's main method followed by the client's main method establishes a TCP connection, transmits the serialized Person object, and the server prints the name, age, and address. The article also provides a screenshot of the console output and a download link for the complete example code.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
The Dominant Programmer
Resources and tutorials for programmers' advanced learning journey. Advanced tracks in Java, Python, and C#. Blog: https://blog.csdn.net/badao_liumang_qizhi
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
