序列化就是将对象的状态信息转换成可以存储或传输的过程。
Netty序列化对象一般有以下几种方式:
JDK
JBoss Marshalling
Protocol Buffers
kryo
JDK
实体类
Request
package com.wk.test.nettyTest.jdk;
import java.io.Serializable;
public class Request implements Serializable {
private String id;
private String name;
private String info;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
Response
package com.wk.test.nettyTest.jdk;
import java.io.Serializable;
public class Response implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String responseMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
}
服务端
NettyServerTest
package com.wk.test.nettyTest.jdk;
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.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NettyServerTest {
private static final Logger logger = LoggerFactory.getLogger(NettyServerTest.class);
public static void main(String[] args) throws InterruptedException {
EventLoopGroup pGroup = new NioEventLoopGroup();
EventLoopGroup cGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
//设置日志
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
sc.pipeline().addLast(new ObjectEncoder());
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture cf = b.bind(8090).sync();
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
ServerHandler
package com.wk.test.nettyTest.jdk;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Request request = (Request)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " + request.getInfo());
Response response = new Response();
response.setId(request.getId());
response.setName("response" + request.getName());
response.setResponseMessage("响应内容" + request.getInfo());
ctx.writeAndFlush(response);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
客户端
NettyClientTest
package com.wk.test.nettyTest.jdk;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NettyClientTest {
private static final Logger logger = LoggerFactory.getLogger(NettyClientTest.class);
private static class SingletonHolder {
static final NettyClientTest instance = new NettyClientTest();
}
public static NettyClientTest getInstance() {
return SingletonHolder.instance;
}
private EventLoopGroup group;
private Bootstrap b;
private ChannelFuture cf;
private NettyClientTest() {
group = new NioEventLoopGroup();
b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
sc.pipeline().addLast(new ObjectEncoder());
//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new ClientHandler());
}
});
}
public void connect() {
try {
this.cf = b.connect("127.0.0.1", 8090).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
} catch (Exception e) {
e.printStackTrace();
}
}
public ChannelFuture getChannelFuture() {
if (this.cf == null) {
this.connect();
}
if (!this.cf.channel().isActive()) {
this.connect();
}
return this.cf;
}
public static void main(String[] args) throws InterruptedException {
final NettyClientTest c = NettyClientTest.getInstance();
ChannelFuture future = c.getChannelFuture();
Request request = new Request();
request.setId("1");
request.setName("上杉绘梨衣");
request.setInfo("04.24,和Sakura去东京天空树,世界上最暖和的地方在天空树的顶上。");
future.channel().writeAndFlush(request).sync();
Request request2 = new Request();
request2.setId("2");
request2.setName("上杉绘梨衣");
request2.setInfo("04.26,和Sakura去明治神宫,有人在那里举办婚礼。");
future.channel().writeAndFlush(request2);
Request request3 = new Request();
request3.setId("3");
request3.setName("上杉绘梨衣");
request3.setInfo("04.25,和Sakura去迪士尼,鬼屋很可怕,但是有Sakura在,所以不可怕。");
future.channel().writeAndFlush(request3);
Request request4 = new Request();
request4.setId("4");
request4.setName("上杉绘梨衣");
request4.setInfo("Sakura最好了。");
future.channel().writeAndFlush(request4);
future.channel().closeFuture().sync();
}
}
ClientHandler
package com.wk.test.nettyTest.jdk;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
Response resp = (Response)msg;
System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " + resp.getResponseMessage());
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
JBoss Marshalling
这种序列化效率比JDK快三倍左右,这里暂不介绍。
protobuf
谷歌开源的一种二进制数据格式,是目前序列化最快的。
相较于json和xml来说,序列化后体积小,传输速率快。序列化后不可读,必须反序列化才可读。
使用
1.下载
下载地址:https://github.com/google/protobuf/releases
这里下载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
<!-- protobuf -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.11.4</version>
</dependency>
这里注意要跟下载的protoc.exe版本一致
实体类
就是生成的java和proto文件
服务端
NettyServerTest
package com.wk.test.nettyTest.proto;
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;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NettyServerTest {
private static final Logger logger = LoggerFactory.getLogger(NettyServerTest.class);
public static void main(String[] args) throws InterruptedException {
EventLoopGroup pGroup = new NioEventLoopGroup();
EventLoopGroup cGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
//设置日志
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ProtobufVarint32FrameDecoder());
sc.pipeline().addLast(new ProtobufDecoder(Request.MessageRequest.getDefaultInstance()));
sc.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
sc.pipeline().addLast(new ProtobufEncoder());
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture cf = b.bind(8090).sync();
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
ServerHandler
package com.wk.test.nettyTest.proto;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Request.MessageRequest request = (Request.MessageRequest)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " + request.getInfo());
ctx.writeAndFlush(request);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
客户端
NettyClientTest
package com.wk.test.nettyTest.proto;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NettyClientTest {
private static final Logger logger = LoggerFactory.getLogger(NettyClientTest.class);
private static class SingletonHolder {
static final NettyClientTest instance = new NettyClientTest();
}
public static NettyClientTest getInstance() {
return SingletonHolder.instance;
}
private EventLoopGroup group;
private Bootstrap b;
private ChannelFuture cf;
private NettyClientTest() {
group = new NioEventLoopGroup();
b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ProtobufVarint32FrameDecoder());
sc.pipeline().addLast(new ProtobufDecoder(Request.MessageRequest.getDefaultInstance()));
sc.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
sc.pipeline().addLast(new ProtobufEncoder());
//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new ClientHandler());
}
});
}
public void connect() {
try {
this.cf = b.connect("127.0.0.1", 8090).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
} catch (Exception e) {
e.printStackTrace();
}
}
public ChannelFuture getChannelFuture() {
if (this.cf == null) {
this.connect();
}
if (!this.cf.channel().isActive()) {
this.connect();
}
return this.cf;
}
public static void main(String[] args) throws InterruptedException {
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
package com.wk.test.nettyTest.proto;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
Request.MessageRequest request = (Request.MessageRequest)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " + request.getInfo());
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
优缺点
优点:protobuf是目前序列化最快的没有之一,较json,xml传输体积小,速率高,适合高性能通讯的应用场景
缺点:如果修改消息内容,则需要重新生成java类。proto文件和java文件不对应则报错。
Kryo(推荐使用)
kryo是基于proto的序列化框架,目前的dubbo中就是使用的它,速率仅次于protobuf,体积小,且不用通过proto文件生成java类。
pom.xml
<!-- kryo -->
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.0.0-RC5</version>
</dependency>
实体类 Request
package com.wk.test.nettyTest.kryo;
import java.io.Serializable;
public class Request implements Serializable {
private String id;
private String name;
private String info;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
封装kryo
因为kryo是线程不安全的,因此我们要对kryo进行一层封装
Serializer
序列化接口类
package com.wk.test.nettyTest.kryo;
public interface Serializer {
//序列化接口
byte[] serialize(Object object);
//反序列化接口
<T> T deserialize(byte[] bytes);
}
KryoSerializer
序列化实现类,通过ThreadLocal 使每个kryo都有一个线程副本,不会相互影响。
package com.wk.test.nettyTest.kryo;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.BeanSerializer;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public class KryoSerializer implements Serializer {
private final Class<?> clazz;
public KryoSerializer(Class<?> clazz){
this.clazz = clazz;
}
final ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>(){
@Override
protected Kryo initialValue(){
Kryo kryo = new Kryo();
kryo.register(clazz, new BeanSerializer(kryo,clazz));
return kryo;
}
};
private Kryo getKryo(){
return kryoThreadLocal.get();
}
@Override
public byte[] serialize(Object object) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Output output = new Output(byteArrayOutputStream);
try {
Kryo kryo = getKryo();
kryo.writeObjectOrNull(output,object,object.getClass());
output.flush();
return byteArrayOutputStream.toByteArray();
}finally {
IOUtils.closeQuietly(output);
IOUtils.closeQuietly(byteArrayOutputStream);
}
}
@Override
public <T> T deserialize(byte[] bytes) {
if(bytes ==null){
return null;
}
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Input input = new Input(byteArrayInputStream);
try {
Kryo kryo = getKryo();
return (T) kryo.readObjectOrNull(input,clazz);
}finally {
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(byteArrayInputStream);
}
}
}
KryoSerializerFactory
工厂类,通过传入class来获取相对应的序列化工具类
package com.wk.test.nettyTest.kryo;
public class KryoSerializerFactory {
public static Serializer getSerializer(Class<?> clazz){
return new KryoSerializer(clazz);
}
}
编码、解码类(也可以称为序列化、反序列化类)
KryoMsgEncoder
package com.wk.test.nettyTest.kryo;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class KryoMsgEncoder extends MessageToByteEncoder<Request> {
private Serializer serializer = KryoSerializerFactory.getSerializer(Request.class);
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Request request, ByteBuf byteBuf) throws Exception {
byte[] body = serializer.serialize(request);
int headLength = body.length;
//相当于消息头
byteBuf.writeInt(headLength);
//相当于消息体
byteBuf.writeBytes(body);
}
}
KryoMsgDecoder
package com.wk.test.nettyTest.kryo;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
public class KryoMsgDecoder extends ByteToMessageDecoder {
private Serializer serializer = KryoSerializerFactory.getSerializer(Request.class);
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
//标记读取的指针的位置
byteBuf.markReaderIndex();
//获取消息头,也就是长度
int dataLength = byteBuf.readInt();
if(dataLength <=0){
//长度不对则当前消息有问题,关闭通道
channelHandlerContext.close();
}
//长度小于真实长度则重新加载读取指针
if(byteBuf.readableBytes() < dataLength){
byteBuf.resetReaderIndex();
return;
}
byte[] body = new byte[dataLength];
byteBuf.readBytes(body);
Request request = serializer.deserialize(body);
list.add(request);
}
}
服务端
NettyKryoServer
package com.wk.test.nettyTest.kryo;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NettyKryoServer {
private static final Logger logger = LoggerFactory.getLogger(NettyKryoServer.class);
public static void main(String[] args) throws InterruptedException {
EventLoopGroup pGroup = new NioEventLoopGroup();
EventLoopGroup cGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
//设置日志
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new KryoMsgDecoder());
sc.pipeline().addLast(new KryoMsgEncoder());
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new KryoServerHandler());
}
});
ChannelFuture cf = b.bind(8090).sync();
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
KryoServerHandler
package com.wk.test.nettyTest.kryo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class KryoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Request request = (Request)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " + request.getInfo());
ctx.writeAndFlush(request);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
客户端
NettyKryoClient
package com.wk.test.nettyTest.kryo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NettyKryoClient {
private static final Logger logger = LoggerFactory.getLogger(NettyKryoClient.class);
private static class SingletonHolder {
static final NettyKryoClient instance = new NettyKryoClient();
}
public static NettyKryoClient getInstance() {
return SingletonHolder.instance;
}
private EventLoopGroup group;
private Bootstrap b;
private ChannelFuture cf;
private NettyKryoClient() {
group = new NioEventLoopGroup();
b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new KryoMsgDecoder());
sc.pipeline().addLast(new KryoMsgEncoder());
//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new KryoClientHandler());
}
});
}
public void connect() {
try {
this.cf = b.connect("127.0.0.1", 8090).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
} catch (Exception e) {
e.printStackTrace();
}
}
public ChannelFuture getChannelFuture() {
if (this.cf == null) {
this.connect();
}
if (!this.cf.channel().isActive()) {
this.connect();
}
return this.cf;
}
public static void main(String[] args) throws InterruptedException {
final NettyKryoClient c = NettyKryoClient.getInstance();
ChannelFuture future = c.getChannelFuture();
Request request = new Request();
request.setId("1");
request.setName("上杉绘梨衣");
request.setInfo("04.24,和Sakura去东京天空树,世界上最暖和的地方在天空树的顶上。");
future.channel().writeAndFlush(request).sync();
Request request2 = new Request();
request2.setId("2");
request2.setName("上杉绘梨衣");
request2.setInfo("04.26,和Sakura去明治神宫,有人在那里举办婚礼。");
future.channel().writeAndFlush(request2);
Request request3 = new Request();
request3.setId("3");
request3.setName("上杉绘梨衣");
request3.setInfo("04.25,和Sakura去迪士尼,鬼屋很可怕,但是有Sakura在,所以不可怕。");
future.channel().writeAndFlush(request3);
Request request4 = new Request();
request4.setId("4");
request4.setName("上杉绘梨衣");
request4.setInfo("Sakura最好了。");
future.channel().writeAndFlush(request4);
future.channel().closeFuture().sync();
}
}
KryoClientHandler
package com.wk.test.nettyTest.kryo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
public class KryoClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
Request resp = (Request)msg;
System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " + resp.getInfo());
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}