Netty(四、序列化与反序列化)

序列化就是将对象的状态信息转换成可以存储或传输的过程。

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();
    }
    
}

 

posted @ 2020-05-12 10:37  学霸王先森  阅读(1183)  评论(0编辑  收藏  举报