序列化就是将对象的状态信息转换成可以存储或传输的过程。
Netty序列化对象一般有以下几种方式:
JDK
JBoss Marshalling
Protocol Buffers
kryo
JDK
实体类
Request
packagecom.wk.test.nettyTest.jdk;importjava.io.Serializable;public class Request implementsSerializable {privateString id;privateString name;privateString info;publicString getId() {returnid;
}public voidsetId(String id) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicString getInfo() {returninfo;
}public voidsetInfo(String info) {this.info =info;
}
}
Response
packagecom.wk.test.nettyTest.jdk;importjava.io.Serializable;public class Response implementsSerializable{private static final long serialVersionUID = 1L;privateString id;privateString name;privateString responseMessage;publicString getId() {returnid;
}public voidsetId(String id) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicString getResponseMessage() {returnresponseMessage;
}public voidsetResponseMessage(String responseMessage) {this.responseMessage =responseMessage;
}
}
服务端
NettyServerTest
packagecom.wk.test.nettyTest.jdk;importio.netty.bootstrap.ServerBootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelInitializer;importio.netty.channel.ChannelOption;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioServerSocketChannel;importio.netty.handler.codec.serialization.ClassResolvers;importio.netty.handler.codec.serialization.ObjectDecoder;importio.netty.handler.codec.serialization.ObjectEncoder;importio.netty.handler.logging.LogLevel;importio.netty.handler.logging.LoggingHandler;importio.netty.handler.timeout.ReadTimeoutHandler;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;public classNettyServerTest {private static final Logger logger = LoggerFactory.getLogger(NettyServerTest.class);public static void main(String[] args) throwsInterruptedException {
EventLoopGroup pGroup= newNioEventLoopGroup();
EventLoopGroup cGroup= newNioEventLoopGroup();
ServerBootstrap b= newServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)//设置日志
.handler(newLoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {protected void initChannel(SocketChannel sc) throwsException {
sc.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
sc.pipeline().addLast(newObjectEncoder());
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(newServerHandler());
}
});
ChannelFuture cf= b.bind(8090).sync();
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
ServerHandler
packagecom.wk.test.nettyTest.jdk;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;public class ServerHandler extendsChannelInboundHandlerAdapter {
@Overridepublic void channelActive(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {
Request request=(Request)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " +request.getInfo());
Response response= newResponse();
response.setId(request.getId());
response.setName("response" +request.getName());
response.setResponseMessage("响应内容" +request.getInfo());
ctx.writeAndFlush(response);
}
@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {
ctx.close();
}
}
客户端
NettyClientTest
packagecom.wk.test.nettyTest.jdk;importio.netty.bootstrap.Bootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelInitializer;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioSocketChannel;importio.netty.handler.codec.serialization.ClassResolvers;importio.netty.handler.codec.serialization.ObjectDecoder;importio.netty.handler.codec.serialization.ObjectEncoder;importio.netty.handler.logging.LogLevel;importio.netty.handler.logging.LoggingHandler;importio.netty.handler.timeout.ReadTimeoutHandler;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;public classNettyClientTest {private static final Logger logger = LoggerFactory.getLogger(NettyClientTest.class);private static classSingletonHolder {static final NettyClientTest instance = newNettyClientTest();
}public staticNettyClientTest getInstance() {returnSingletonHolder.instance;
}privateEventLoopGroup group;privateBootstrap b;privateChannelFuture cf;privateNettyClientTest() {
group= newNioEventLoopGroup();
b= newBootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(newLoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer() {
@Overrideprotected void initChannel(SocketChannel sc) throwsException {
sc.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
sc.pipeline().addLast(newObjectEncoder());//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(newClientHandler());
}
});
}public voidconnect() {try{this.cf = b.connect("127.0.0.1", 8090).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
}catch(Exception e) {
e.printStackTrace();
}
}publicChannelFuture getChannelFuture() {if (this.cf == null) {this.connect();
}if (!this.cf.channel().isActive()) {this.connect();
}return this.cf;
}public static void main(String[] args) throwsInterruptedException {final NettyClientTest c =NettyClientTest.getInstance();
ChannelFuture future=c.getChannelFuture();
Request request= newRequest();
request.setId("1");
request.setName("上杉绘梨衣");
request.setInfo("04.24,和Sakura去东京天空树,世界上最暖和的地方在天空树的顶上。");
future.channel().writeAndFlush(request).sync();
Request request2= newRequest();
request2.setId("2");
request2.setName("上杉绘梨衣");
request2.setInfo("04.26,和Sakura去明治神宫,有人在那里举办婚礼。");
future.channel().writeAndFlush(request2);
Request request3= newRequest();
request3.setId("3");
request3.setName("上杉绘梨衣");
request3.setInfo("04.25,和Sakura去迪士尼,鬼屋很可怕,但是有Sakura在,所以不可怕。");
future.channel().writeAndFlush(request3);
Request request4= newRequest();
request4.setId("4");
request4.setName("上杉绘梨衣");
request4.setInfo("Sakura最好了。");
future.channel().writeAndFlush(request4);
future.channel().closeFuture().sync();
}
}
ClientHandler
packagecom.wk.test.nettyTest.jdk;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;importio.netty.util.ReferenceCountUtil;public class ClientHandler extendsChannelInboundHandlerAdapter {
@Overridepublic void channelActive(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {try{
Response resp=(Response)msg;
System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " +resp.getResponseMessage());
}finally{
ReferenceCountUtil.release(msg);
}
}
@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {
ctx.close();
}
}
JBoss Marshalling
这种序列化效率比JDK快三倍左右,这里暂不介绍。
protobuf
谷歌开源的一种二进制数据格式,是目前序列化最快的。
相较于json和xml来说,序列化后体积小,传输速率快。序列化后不可读,必须反序列化才可读。
使用
1.下载
这里下载protoc-3.11.4-win64,windows系统使用的protoc.exe
2.编写proto格式文件
我们需要编写一个.proto格式的协议文件,通过该协议文件来生产java类,具体的语法和规则可以参考官方文档。这里只举个例子:
Request.proto
syntax = "proto3";
option java_package= "com.wk.test.nettyTest.proto";
option java_outer_classname= "Request";
message MessageRequest{
uint64 id= 1;
string name= 2;
string info= 3;
}
syntax = "proto3";是使用的协议版本是3
java_package 是生成文件的包路径
java_outer_classname 是类名
message MessageRequest{
uint64 id = 1;
string name = 2;
string info = 3;
}
消息体内容:
64 int类型的id
string 姓名和内容
后面的数字代表一个应答序号,同一级别下不可重复
3.生成协议文件对应的消息类
CMD命令到我们下载好的protoc.exe目录下,执行命令
protoc.exe ./Request.proto --java_out=./
生成Requst.java
4.编写代码
准备工作已经结束了,我们将.proto文件和生成的java文件放入相对应的程序中就可以开始开发了
开发
pom.xml
com.google.protobuf
protobuf-java
3.11.4
这里注意要跟下载的protoc.exe版本一致
实体类
就是生成的java和proto文件
服务端
NettyServerTest
packagecom.wk.test.nettyTest.proto;importio.netty.bootstrap.ServerBootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelInitializer;importio.netty.channel.ChannelOption;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioServerSocketChannel;importio.netty.handler.codec.protobuf.ProtobufDecoder;importio.netty.handler.codec.protobuf.ProtobufEncoder;importio.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;importio.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;importio.netty.handler.logging.LogLevel;importio.netty.handler.logging.LoggingHandler;importio.netty.handler.timeout.ReadTimeoutHandler;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;public classNettyServerTest {private static final Logger logger = LoggerFactory.getLogger(NettyServerTest.class);public static void main(String[] args) throwsInterruptedException {
EventLoopGroup pGroup= newNioEventLoopGroup();
EventLoopGroup cGroup= newNioEventLoopGroup();
ServerBootstrap b= newServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)//设置日志
.handler(newLoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {protected void initChannel(SocketChannel sc) throwsException {
sc.pipeline().addLast(newProtobufVarint32FrameDecoder());
sc.pipeline().addLast(newProtobufDecoder(Request.MessageRequest.getDefaultInstance()));
sc.pipeline().addLast(newProtobufVarint32LengthFieldPrepender());
sc.pipeline().addLast(newProtobufEncoder());
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(newServerHandler());
}
});
ChannelFuture cf= b.bind(8090).sync();
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
ServerHandler
packagecom.wk.test.nettyTest.proto;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;public class ServerHandler extendsChannelInboundHandlerAdapter {
@Overridepublic void channelActive(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {
Request.MessageRequest request=(Request.MessageRequest)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " +request.getInfo());
ctx.writeAndFlush(request);
}
@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {
ctx.close();
}
}
客户端
NettyClientTest
packagecom.wk.test.nettyTest.proto;importio.netty.bootstrap.Bootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelInitializer;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioSocketChannel;importio.netty.handler.codec.protobuf.ProtobufDecoder;importio.netty.handler.codec.protobuf.ProtobufEncoder;importio.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;importio.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;importio.netty.handler.logging.LogLevel;importio.netty.handler.logging.LoggingHandler;importio.netty.handler.timeout.ReadTimeoutHandler;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;public classNettyClientTest {private static final Logger logger = LoggerFactory.getLogger(NettyClientTest.class);private static classSingletonHolder {static final NettyClientTest instance = newNettyClientTest();
}public staticNettyClientTest getInstance() {returnSingletonHolder.instance;
}privateEventLoopGroup group;privateBootstrap b;privateChannelFuture cf;privateNettyClientTest() {
group= newNioEventLoopGroup();
b= newBootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(newLoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer() {
@Overrideprotected void initChannel(SocketChannel sc) throwsException {
sc.pipeline().addLast(newProtobufVarint32FrameDecoder());
sc.pipeline().addLast(newProtobufDecoder(Request.MessageRequest.getDefaultInstance()));
sc.pipeline().addLast(newProtobufVarint32LengthFieldPrepender());
sc.pipeline().addLast(newProtobufEncoder());//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(newClientHandler());
}
});
}public voidconnect() {try{this.cf = b.connect("127.0.0.1", 8090).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
}catch(Exception e) {
e.printStackTrace();
}
}publicChannelFuture getChannelFuture() {if (this.cf == null) {this.connect();
}if (!this.cf.channel().isActive()) {this.connect();
}return this.cf;
}public static void main(String[] args) throwsInterruptedException {final NettyClientTest c =NettyClientTest.getInstance();
ChannelFuture future=c.getChannelFuture();
Request.MessageRequest.Builder builder=Request.MessageRequest.newBuilder();
builder.setId(1);
builder.setName("上杉绘梨衣");
builder.setInfo("04.24,和Sakura去东京天空树,世界上最暖和的地方在天空树的顶上。");
future.channel().writeAndFlush(builder.build()).sync();
Request.MessageRequest.Builder builder2=Request.MessageRequest.newBuilder();
builder2.setId(2);
builder2.setName("上杉绘梨衣");
builder2.setInfo("04.26,和Sakura去明治神宫,有人在那里举办婚礼。");
future.channel().writeAndFlush(builder2.build());
Request.MessageRequest.Builder builder3=Request.MessageRequest.newBuilder();
builder3.setId(3);
builder3.setName("上杉绘梨衣");
builder3.setInfo("04.25,和Sakura去迪士尼,鬼屋很可怕,但是有Sakura在,所以不可怕。");
future.channel().writeAndFlush(builder3.build());
Request.MessageRequest.Builder builder4=Request.MessageRequest.newBuilder();
builder4.setId(4);
builder4.setName("上杉绘梨衣");
builder4.setInfo("Sakura最好了。");
future.channel().writeAndFlush(builder4.build());
future.channel().closeFuture().sync();
}
}
ClientHandler
packagecom.wk.test.nettyTest.proto;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;importio.netty.util.ReferenceCountUtil;public class ClientHandler extendsChannelInboundHandlerAdapter {
@Overridepublic void channelActive(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {try{
Request.MessageRequest request=(Request.MessageRequest)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " +request.getInfo());
}finally{
ReferenceCountUtil.release(msg);
}
}
@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {
ctx.close();
}
}
优缺点
优点:protobuf是目前序列化最快的没有之一,较json,xml传输体积小,速率高,适合高性能通讯的应用场景
缺点:如果修改消息内容,则需要重新生成java类。proto文件和java文件不对应则报错。
Kryo(推荐使用)
kryo是基于proto的序列化框架,目前的dubbo中就是使用的它,速率仅次于protobuf,体积小,且不用通过proto文件生成java类。
pom.xml
com.esotericsoftware
kryo
5.0.0-RC5
实体类 Request
packagecom.wk.test.nettyTest.kryo;importjava.io.Serializable;public class Request implementsSerializable {privateString id;privateString name;privateString info;publicString getId() {returnid;
}public voidsetId(String id) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicString getInfo() {returninfo;
}public voidsetInfo(String info) {this.info =info;
}
}
封装kryo
因为kryo是线程不安全的,因此我们要对kryo进行一层封装
Serializer
序列化接口类
packagecom.wk.test.nettyTest.kryo;public interfaceSerializer {//序列化接口
byte[] serialize(Object object);//反序列化接口
T deserialize(byte[] bytes);
}
KryoSerializer
序列化实现类,通过ThreadLocal 使每个kryo都有一个线程副本,不会相互影响。
packagecom.wk.test.nettyTest.kryo;importcom.esotericsoftware.kryo.Kryo;importcom.esotericsoftware.kryo.io.Input;importcom.esotericsoftware.kryo.io.Output;importcom.esotericsoftware.kryo.serializers.BeanSerializer;importorg.apache.commons.io.IOUtils;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;public class KryoSerializer implementsSerializer {private final Class>clazz;public KryoSerializer(Class>clazz){this.clazz =clazz;
}final ThreadLocal kryoThreadLocal = new ThreadLocal(){
@OverrideprotectedKryo initialValue(){
Kryo kryo= newKryo();
kryo.register(clazz,newBeanSerializer(kryo,clazz));returnkryo;
}
};privateKryo getKryo(){returnkryoThreadLocal.get();
}
@Overridepublic byte[] serialize(Object object) {
ByteArrayOutputStream byteArrayOutputStream= newByteArrayOutputStream();
Output output= newOutput(byteArrayOutputStream);try{
Kryo kryo=getKryo();
kryo.writeObjectOrNull(output,object,object.getClass());
output.flush();returnbyteArrayOutputStream.toByteArray();
}finally{
IOUtils.closeQuietly(output);
IOUtils.closeQuietly(byteArrayOutputStream);
}
}
@Overridepublic T deserialize(byte[] bytes) {if(bytes ==null){return null;
}
ByteArrayInputStream byteArrayInputStream= newByteArrayInputStream(bytes);
Input input= newInput(byteArrayInputStream);try{
Kryo kryo=getKryo();return(T) kryo.readObjectOrNull(input,clazz);
}finally{
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(byteArrayInputStream);
}
}
}
KryoSerializerFactory
工厂类,通过传入class来获取相对应的序列化工具类
packagecom.wk.test.nettyTest.kryo;public classKryoSerializerFactory {public static Serializer getSerializer(Class>clazz){return newKryoSerializer(clazz);
}
}
编码、解码类(也可以称为序列化、反序列化类)
KryoMsgEncoder
packagecom.wk.test.nettyTest.kryo;importio.netty.buffer.ByteBuf;importio.netty.channel.ChannelHandlerContext;importio.netty.handler.codec.MessageToByteEncoder;public class KryoMsgEncoder extends MessageToByteEncoder{private Serializer serializer = KryoSerializerFactory.getSerializer(Request.class);
@Overrideprotected void encode(ChannelHandlerContext channelHandlerContext, Request request, ByteBuf byteBuf) throwsException {byte[] body =serializer.serialize(request);int headLength =body.length;//相当于消息头
byteBuf.writeInt(headLength);//相当于消息体
byteBuf.writeBytes(body);
}
}
KryoMsgDecoder
packagecom.wk.test.nettyTest.kryo;importio.netty.buffer.ByteBuf;importio.netty.channel.ChannelHandlerContext;importio.netty.handler.codec.ByteToMessageDecoder;importjava.util.List;public class KryoMsgDecoder extendsByteToMessageDecoder {private Serializer serializer = KryoSerializerFactory.getSerializer(Request.class);
@Overrideprotected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throwsException {//标记读取的指针的位置
byteBuf.markReaderIndex();//获取消息头,也就是长度
int dataLength =byteBuf.readInt();if(dataLength <=0){//长度不对则当前消息有问题,关闭通道
channelHandlerContext.close();
}//长度小于真实长度则重新加载读取指针
if(byteBuf.readableBytes()
byteBuf.resetReaderIndex();return;
}byte[] body = new byte[dataLength];
byteBuf.readBytes(body);
Request request=serializer.deserialize(body);
list.add(request);
}
}
服务端
NettyKryoServer
packagecom.wk.test.nettyTest.kryo;importio.netty.bootstrap.ServerBootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelInitializer;importio.netty.channel.ChannelOption;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioServerSocketChannel;importio.netty.handler.logging.LogLevel;importio.netty.handler.logging.LoggingHandler;importio.netty.handler.timeout.ReadTimeoutHandler;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;public classNettyKryoServer {private static final Logger logger = LoggerFactory.getLogger(NettyKryoServer.class);public static void main(String[] args) throwsInterruptedException {
EventLoopGroup pGroup= newNioEventLoopGroup();
EventLoopGroup cGroup= newNioEventLoopGroup();
ServerBootstrap b= newServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)//设置日志
.handler(newLoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {protected void initChannel(SocketChannel sc) throwsException {
sc.pipeline().addLast(newKryoMsgDecoder());
sc.pipeline().addLast(newKryoMsgEncoder());
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(newKryoServerHandler());
}
});
ChannelFuture cf= b.bind(8090).sync();
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
KryoServerHandler
packagecom.wk.test.nettyTest.kryo;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;public class KryoServerHandler extendsChannelInboundHandlerAdapter {
@Overridepublic void channelActive(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {
Request request=(Request)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " +request.getInfo());
ctx.writeAndFlush(request);
}
@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {
ctx.close();
}
}
客户端
NettyKryoClient
packagecom.wk.test.nettyTest.kryo;importio.netty.bootstrap.Bootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelInitializer;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioSocketChannel;importio.netty.handler.logging.LogLevel;importio.netty.handler.logging.LoggingHandler;importio.netty.handler.timeout.ReadTimeoutHandler;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;public classNettyKryoClient {private static final Logger logger = LoggerFactory.getLogger(NettyKryoClient.class);private static classSingletonHolder {static final NettyKryoClient instance = newNettyKryoClient();
}public staticNettyKryoClient getInstance() {returnSingletonHolder.instance;
}privateEventLoopGroup group;privateBootstrap b;privateChannelFuture cf;privateNettyKryoClient() {
group= newNioEventLoopGroup();
b= newBootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(newLoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer() {
@Overrideprotected void initChannel(SocketChannel sc) throwsException {
sc.pipeline().addLast(newKryoMsgDecoder());
sc.pipeline().addLast(newKryoMsgEncoder());//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(newKryoClientHandler());
}
});
}public voidconnect() {try{this.cf = b.connect("127.0.0.1", 8090).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
}catch(Exception e) {
e.printStackTrace();
}
}publicChannelFuture getChannelFuture() {if (this.cf == null) {this.connect();
}if (!this.cf.channel().isActive()) {this.connect();
}return this.cf;
}public static void main(String[] args) throwsInterruptedException {final NettyKryoClient c =NettyKryoClient.getInstance();
ChannelFuture future=c.getChannelFuture();
Request request= newRequest();
request.setId("1");
request.setName("上杉绘梨衣");
request.setInfo("04.24,和Sakura去东京天空树,世界上最暖和的地方在天空树的顶上。");
future.channel().writeAndFlush(request).sync();
Request request2= newRequest();
request2.setId("2");
request2.setName("上杉绘梨衣");
request2.setInfo("04.26,和Sakura去明治神宫,有人在那里举办婚礼。");
future.channel().writeAndFlush(request2);
Request request3= newRequest();
request3.setId("3");
request3.setName("上杉绘梨衣");
request3.setInfo("04.25,和Sakura去迪士尼,鬼屋很可怕,但是有Sakura在,所以不可怕。");
future.channel().writeAndFlush(request3);
Request request4= newRequest();
request4.setId("4");
request4.setName("上杉绘梨衣");
request4.setInfo("Sakura最好了。");
future.channel().writeAndFlush(request4);
future.channel().closeFuture().sync();
}
}
KryoClientHandler
packagecom.wk.test.nettyTest.kryo;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;importio.netty.util.ReferenceCountUtil;public class KryoClientHandler extendsChannelInboundHandlerAdapter {
@Overridepublic void channelActive(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {try{
Request resp=(Request)msg;
System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " +resp.getInfo());
}finally{
ReferenceCountUtil.release(msg);
}
}
@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throwsException {
}
@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {
ctx.close();
}
}