序列化
序列化即是将java对象转为二进制数据流,在网络中的数据传输就要实现数据的序列化和反序列化。
实现序列化可以使用JDK自带的方式:实现Serializable接口即可,操作很简单。
但是这种方式的确定就是效率很低。
所以,这时我们可以使用一些第三方的序列化方式提高效率,这里使用protostuff。
首先导入依赖
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.5.0</version>
</dependency>
实体
public class Person {
String name;
int id;
String email;
}
proto描述文件的编写
语法
3.5的语法跟以前的protobuf语法有很大的变化,详细的可以参考官方文档,
地址:https://developers.google.com/protocol-buffers/docs/proto3
这里有一个中文翻译的文档
地址:http://blog.csdn.net/u011518120/article/details/54604615
重点注意的地方有这么几点:句法申明、数据类型
客户端
public class Client {
public static class ProtoBufClient {
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
public void initChannel(SocketChannel ch)
throws Exception {
// 用来添加报文长度字段
ch.pipeline().addLast(
new ProtobufVarint32LengthFieldPrepender());
//添加 ProtobufEncoder 进行序列化将实体类编码为字节
ch.pipeline().addLast(new ProtobufEncoder());
//添加自己的业务Handler
ch.pipeline().addLast(
new ProtoBufClientHandler());
}
});
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放NIO线程组
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
new ProtoBufClient().connect(port, "127.0.0.1");
}
}
}
客户端处理器
public class ProtoBufClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("准备生成数据===========>");
//生成实体类
PersonProto.Person.Builder builder = PersonProto.Person.newBuilder();
builder.setName("CemB");
builder.setId(1);
builder.setEmail("[email protected]");
System.out.println("发送数据===========>" + builder.getName());
//写往服务端,由编码器进行编码
ctx.writeAndFlush(builder.build());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
服务端
public class ProtoBufServer {
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)
//初始化服务端可连接队列为100个
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
//添加 ProtobufVarint32FrameDecoder 以分离数据帧
ch.pipeline().addLast(
new ProtobufVarint32FrameDecoder());
//添加 ProtobufDecoder 反序列化将字节解码为实体
ch.pipeline().addLast(new ProtobufDecoder(
PersonProto.Person.getDefaultInstance()
));
//添加自己业务Handler
ch.pipeline().addLast(new ProtoBufServerHandler());
}
});
// 绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
System.out.println("init start");
// 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
new ProtoBufServer().bind(port);
}
}
服务端处理器
public class ProtoBufServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
//将上一个handler传入的数据强制转型,因为已经反序列化了
PersonProto.Person req = (PersonProto.Person) msg;
System.out.println("收到数据:name=" + req.getName() + ",email=" + req.getEmail());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (cause instanceof IOException) {
System.out.println("远程客户端强迫关闭了一个现有的连接。");
}
ctx.close();
}
}