上一篇我们对Protobuf有个一个初步的了解,并成功搭建起了开发环境,且对Protobuf API有了一个简单的调试。
在掌握了以上内容后,下面我们使用Netty的Protobuf编解码框架来进行客户端和服务端的开发,通过一个图书订阅的小案例就行讲解说明,那我们就开始吧!
代码全部都完整给出,强烈建议自己一遍遍的手动来写,多理解。
理解的越多,则需要记忆的内容就越少
SubBookServer.java
package com.moreday.netty_protobuf.codec.serializable.netty;
import com.moreday.netty_protobuf.codec.protobuf.BookReqProto;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* @ClassName SubBookServer
* @Description TODO(这里用一句话描述这个类的作用)
* @author 寻找手艺人
* @Date 2020年4月15日 上午10:46:37
* @version 1.0.0
*/
public class SubBookServer {
/**
* @Description (TODO这里用一句话描述这个方法的作用)
* @param port
* @throws Exception
*/
public void bind(int port) throws Exception {
// 配置服务端NIO线程
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//ProtobufVarint32FrameDecoder主要用于半包的处理
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
//指定ProtobufDecoder解码器需要的解码后的目标实例类型
//参数:MessageLite 就是我们的BookReq类型
ch.pipeline().addLast(new ProtobufDecoder(new BookReqProto.BookReq().getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new SubBookServerHandler());
}
});
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if (args != null && args.length > 0) {
port = Integer.valueOf(args[0]);
}
new SubBookServer().bind(port);
}
}
SubBookServerHandler.java
package com.moreday.netty_protobuf.codec.serializable.netty;
import com.moreday.netty_protobuf.codec.protobuf.BookReqProto;
import com.moreday.netty_protobuf.codec.protobuf.BookRespProto;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* @ClassName SubBookServerHandler
* @Description TODO(这里用一句话描述这个类的作用)
* @author 寻找手艺人
* @Date 2020年4月15日 上午10:50:53
* @version 1.0.0
*/
public class SubBookServerHandler extends ChannelInboundHandlerAdapter{
/* (非 Javadoc)
* Description:
* @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
BookReqProto.BookReq req = (BookReqProto.BookReq) msg;
if("寻找手艺人".equalsIgnoreCase(req.getUserName())) {
System.out.println("Server accept client subscribe req ["+req.toString()+"]");
ctx.writeAndFlush(subResp(req.getId()));
}
}
/**
* @Description (TODO这里用一句话描述这个方法的作用)
* @param id
* @return
*/
private BookRespProto.BookResp subResp(int id) {
BookRespProto.BookResp.Builder builder = BookRespProto.BookResp.newBuilder();
builder.setSubReqID(id);
builder.setRespCode(200);
builder.setDesc("Head First设计模式 book order successed,1 day later,send to the designated address! good lucky~");
return builder.build();
}
}
SubBookClient.java
package com.moreday.netty_protobuf.codec.serializable.netty;
import com.moreday.netty_protobuf.codec.protobuf.BookRespProto;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
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;
/**
* @ClassName SubBookClient
* @Description TODO(这里用一句话描述这个类的作用)
* @author 寻找手艺人
* @Date 2020年4月15日 上午10:48:19
* @version 1.0.0
*/
public class SubBookClient {
public void connect(int port, String host) throws Exception {
// 配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(BookRespProto.BookResp.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new SubBookClientHandler());
}
});
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args!=null && args.length>0) {
port = Integer.valueOf(args[0]);
}
new SubBookClient().connect(port, "127.0.0.1");
}
}
SubBookClientHandler.java
package com.moreday.netty_protobuf.codec.serializable.netty;
import java.util.ArrayList;
import java.util.List;
import com.moreday.netty_protobuf.codec.protobuf.BookReqProto;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* @ClassName SubBookClientHandler
* @Description TODO(这里用一句话描述这个类的作用)
* @author 寻找手艺人
* @Date 2020年4月15日 下午12:46:06
* @version 1.0.0
*/
public class SubBookClientHandler extends ChannelInboundHandlerAdapter {
/* (非 Javadoc)
* Description:
* @see io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext)
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for(int i=1;i<=3;i++) {
ctx.writeAndFlush(subBookReq(i));
}
}
/**
* @Description (TODO这里用一句话描述这个方法的作用)
* @param i
* @return
*/
private BookReqProto.BookReq subBookReq(int i) {
BookReqProto.BookReq.Builder builder = BookReqProto.BookReq.newBuilder();
builder.setId(i);
builder.setProductName("Head First设计模式");
builder.setUserName("寻找手艺人");
List<String> address = new ArrayList<String>();
address.add("98.00桐梓");
address.add("198.00遵义");
address.add("298.00济宁");
address.add("398.00青岛");
builder.addAllAddress(address);
return builder.build();
}
/* (非 Javadoc)
* Description:
* @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// TODO Auto-generated method stub
System.out.println("Client Receive Server response :["+msg+"]");
}
}
客户端发起3次订阅请求,服务端呈现3次请求响应,完全符合我们的设计初衷。