netty-netty使用ProtoBuf
摘自<netty权威指南>
案例
服务器端
public class EchoServer {
private int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws InterruptedException {
int port = 8080;
new EchoServer(port).start();
}
private void start() throws InterruptedException {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(boss, work)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(
new ProtobufDecoder(SubscribeReqProto.SubscribeReq.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new SubReqServerHandler());
}
});
ChannelFuture future = b.bind(port);
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()){
System.out.println("server start success...");
}else {
System.out.println("server start failed...");
}
}
});
future.channel().closeFuture().sync();
} finally {
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
}
SubReqServerHandler
:
public class SubReqServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq) msg;
if ("netty".equalsIgnoreCase(req.getUserName())) {
System.out.println("server recv req[x] : [" + req.toString() + "]");
ctx.writeAndFlush(resp(req.getSubReqID()));
}
}
private SubscribeRespProto.SubscribeResp resp(int subReqID) {
SubscribeRespProto.SubscribeResp.Builder builder =
SubscribeRespProto.SubscribeResp.newBuilder();
builder.setSubReqID(subReqID);
builder.setDesc("netty book subscribe success...");
builder.setRespCode(0);
return builder.build();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客户端
public class EchoClient {
private String host;
private int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public static void main(String[] args) throws Exception {
int port = 8080;
String host = "127.0.0.1";
new EchoClient(host, port).connect();
}
private void connect() throws Exception {
EventLoopGroup boss = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(boss)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(
SubscribeRespProto.SubscribeResp.getDefaultInstance()
));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new SubReqClientHandler());
}
});
ChannelFuture future = b.connect(host, port).sync();
future.channel().closeFuture().sync();
} finally {
boss.shutdownGracefully();
}
}
}
SubReqClientHandler
:
public class SubReqClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx,
Object msg) throws Exception {
System.out.println("client recv [x]: [" + msg + "]");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 10; i++) {
ctx.write(subReq(i));
}
ctx.flush();
}
private SubscribeReqProto.SubscribeReq subReq(int i) {
SubscribeReqProto.SubscribeReq.Builder builder =
SubscribeReqProto.SubscribeReq.newBuilder();
builder.setSubReqID(i);
builder.setProductName("netty definitive book");
builder.setUserName("netty");
List<String> address = new ArrayList<>();
address.add("ShangHai");
address.add("BeiJing");
builder.addAllAddress(address);
return builder.build();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx)
throws Exception {
ctx.flush();
}
}
注意事项
ProtoBufDecoder
仅仅负责解码,并不能解决半包问题,处理半包方案:
- 使用netty提供的
ProtobufVarint32FrameDecoder
- 继承netty提供的通过半包解码器
LengthFieldBasedFrameDecoder
- 继承
ByteToMessageDecoder
,自定义实现
运行结果
# server
server start success...
server recv req[x] : [userName: "netty"
productName: "netty definitive book"
address: "ShangHai"
address: "BeiJing"
]
...
# client
client recv [x]: [desc: "netty book subscribe success..." ]
client recv [x]: [subReqID: 1 desc: "netty book subscribe success..." ]
...