netty粘包(二)ObjectDecoder & ObjectEncode

 参考:

https://blog.csdn.net/linwei_1029/article/details/49420391

Netty实现消息推送以及内部心跳机制

 

服务端:

package com.jds.test.pack2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.FixedLengthFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
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.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * 两端SimpleChannelInboundHandler+String编解码+FixedLengthFrameDecoder定长拆包
 * Created by sunyuming on 18/8/1.
 */
public class Server4 {
    public static void main(String[] args)  {

        //boss线程监听端口,worker线程负责数据读写
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();

        try{
            //辅助启动类
            ServerBootstrap bootstrap = new ServerBootstrap();
            //设置线程池
            bootstrap.group(boss,worker);

            //设置socket工厂
            bootstrap.channel(NioServerSocketChannel.class);

            //设置管道工厂
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    //获取管道
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    //定长解码类
//                    pipeline.addLast(new LengthFieldBasedFrameDecoder(100000, 0, 4, 0, 4));
//                    pipeline.addLast(new LengthFieldPrepender(4));
                    //字符串解码类
                    pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
                    pipeline.addLast(new ObjectEncoder());
                    //处理类
                    pipeline.addLast(new ServerHandler4());
                }
            });

            //绑定端口
            ChannelFuture future = bootstrap.bind(8866).sync();
            System.out.println("server start ...... ");

            //等待服务端监听端口关闭
            future.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //优雅退出,释放线程池资源
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

}

  

package com.jds.test.pack2;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * Created by sunyuming on 18/8/1.
 */
class ServerHandler4 extends SimpleChannelInboundHandler<BaseBean> {

    //用于记录次数
    private int count = 0;
    //读取客户端发送的数据
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, BaseBean msg) throws Exception {
        System.out.println("RESPONSE--------"+msg.toString());
        ctx.writeAndFlush(msg);
    }

    //新客户端接入
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
    }

    //客户端断开
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive");
    }

    //异常
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //关闭通道
        ctx.channel().close();
        //打印异常
        cause.printStackTrace();
    }
}

  

客户端:

package com.jds.test.pack2;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.FixedLengthFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
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.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.net.InetSocketAddress;

/**
 * Created by sunyuming on 18/8/1.
 */
public class Client4 {

    public static void main(String[] args) {

        //worker负责读写数据
        EventLoopGroup worker = new NioEventLoopGroup();

        try {
            //辅助启动类
            Bootstrap bootstrap = new Bootstrap();

            //设置线程池
            bootstrap.group(worker);

            //设置socket工厂
            bootstrap.channel(NioSocketChannel.class);

            //设置管道
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    //获取管道
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    //定长解码类
//                    pipeline.addLast(new LengthFieldBasedFrameDecoder(100000, 0, 4, 0, 4));
//                    pipeline.addLast(new LengthFieldPrepender(4));
                    //字符串解码类
                    pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
                    pipeline.addLast(new ObjectEncoder());
                    //处理类
                    pipeline.addLast(new ClientHandler4());
                }
            });

            //发起异步连接操作
            ChannelFuture futrue = bootstrap.connect(new InetSocketAddress("127.0.0.1",8866)).sync();

            //等待客户端链路关闭
            futrue.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //优雅的退出,释放NIO线程组
            worker.shutdownGracefully();
        }
    }

}

  

package com.jds.test.pack2;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * Created by sunyuming on 18/8/1.
 */
class ClientHandler4 extends SimpleChannelInboundHandler<BaseBean> {

    //接受服务端发来的消息
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, BaseBean msg) throws Exception {
        System.out.println("server response : "+msg.toString());
    }

    //与服务器建立连接
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //给服务器发消息

        //发送50次消息
        for (int i = 0; i < 5; i++) {
            ctx.channel().writeAndFlush(new BaseBean());
        }
    }

    //与服务器断开连接
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive");
    }

    //异常
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //关闭管道
        ctx.channel().close();
        //打印异常信息
        cause.printStackTrace();
    }

}

  

对象:

package com.jds.test.pack2;

import java.io.Serializable;

/**
 * Created by sunyuming on 18/8/1.
 */
public class BaseBean implements Serializable {
    private static int seria = 0;
    private static String base = "abcdefghigklmn";

    public BaseBean() {
        setNo(++seria);
        setRandom(base.substring(seria));
    }
    private Integer no;

    private String random;

    @Override
    public String toString() {
        return String.valueOf(no) + ":" + random;
    }

    public Integer getNo() {
        return no;
    }

    public void setNo(Integer no) {
        this.no = no;
    }

    public String getRandom() {
        return random;
    }

    public void setRandom(String random) {
        this.random = random;
    }
}

  

服务端输出:

server start ......
channelActive
RESPONSE--------1:bcdefghigklmn
RESPONSE--------2:cdefghigklmn
RESPONSE--------3:defghigklmn
RESPONSE--------4:efghigklmn
RESPONSE--------5:fghigklmn

客户端:

server response : 1:bcdefghigklmn
server response : 2:cdefghigklmn
server response : 3:defghigklmn
server response : 4:efghigklmn
server response : 5:fghigklmn

 

注意点:

1.Object编解码已经处理了粘拆包:

public class ObjectDecoder extends LengthFieldBasedFrameDecoder 

public ObjectDecoder(ClassResolver classResolver) {
this(1048576, classResolver);
}

public ObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
super(maxObjectSize, 0, 4, 0, 4);
this.classResolver = classResolver;
}

******************************************

public class ObjectEncoder extends MessageToByteEncoder<Serializable> {
private static final byte[] LENGTH_PLACEHOLDER = new byte[4];

public ObjectEncoder() {
}

protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
int startIdx = out.writerIndex();
ByteBufOutputStream bout = new ByteBufOutputStream(out);
bout.write(LENGTH_PLACEHOLDER);

 

替代了:

//    解码                pipeline.addLast(new LengthFieldBasedFrameDecoder(100000, 0, 4, 0, 4));
// 编码 pipeline.addLast(new LengthFieldPrepender(4));

句的功效

 

2.LengthFieldBasedFrameDecoder与LengthFieldPrepender自定义编解码可不共存?

自定义可参考:https://www.cnblogs.com/hupengcool/p/3931721.html

关键代码:

红色的为自己处理合包和拆包

当然我们Netty也有自带的LengthFieldBasedFrameDecoder,但是在使用自定义序列化的时候,我觉得还是自己写比较方便一点,反正总不是要写代码。

 

3. ObjectEncoder来实现自定义对象的序列化,但是用的是java内置的序列化,必须实现Serializable接口,实测。

 

posted on 2018-08-01 19:08  silyvin  阅读(846)  评论(0)    收藏  举报