Netty中使用LengthFieldBaseFrame解决粘包半包问题,并实现双向通信

LengthFieldBaseFrameDecoderService:

package Netty3;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;

public class LengthFieldBaseFrameDecoderService {
    private static final int MAX_FRAME_LENGTH = 1024 * 1024;
    private static final int LENGTH_FIELD_LENGTH = 4;
    private static final int LENGTH_FIELD_OFFSET = 1;
    private static final int LENGTH_ADJUSTMENT = 0;
    private static final int INITIAL_BYTES_TO_STRIP = 0;

    private int port;

    public LengthFieldBaseFrameDecoderService(int port) {
        this.port = port;
    }

    public void start(){
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap sbs = new ServerBootstrap()
                    .group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new NewServerChannelInitializer(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP))
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture future = sbs.bind(port).sync();

            System.out.println("Server start listen at " + port );
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        LengthFieldBaseFrameDecoderService server = new LengthFieldBaseFrameDecoderService(7788);
        server.start();
    }

}

NewServerChannelInitializer:

package Netty3;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;

public class NewServerChannelInitializer extends ChannelInitializer<SocketChannel> {
    private  final int MAX_FRAME_LENGTH;
    private  final int LENGTH_FIELD_LENGTH;
    private  final int LENGTH_FIELD_OFFSET;
    private  final int LENGTH_ADJUSTMENT;
    private  final int INITIAL_BYTES_TO_STRIP;

    public NewServerChannelInitializer(int MAX_FRAME_LENGTH, int LENGTH_FIELD_LENGTH, int LENGTH_FIELD_OFFSET, int LENGTH_ADJUSTMENT, int INITIAL_BYTES_TO_STRIP) {
        this.MAX_FRAME_LENGTH = MAX_FRAME_LENGTH;
        this.LENGTH_FIELD_LENGTH = LENGTH_FIELD_LENGTH;
        this.LENGTH_FIELD_OFFSET = LENGTH_FIELD_OFFSET;
        this.LENGTH_ADJUSTMENT = LENGTH_ADJUSTMENT;
        this.INITIAL_BYTES_TO_STRIP = INITIAL_BYTES_TO_STRIP;
    }

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new NewDecoder(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP,false));
        // 自己的逻辑Handler
        pipeline.addLast(new NewEncoder());
        pipeline.addLast("handler0", new NewServerHandler());

    }

}

NewServerHandler:

package Netty3;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NewServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
        if(o instanceof Message) {
            Message msg = (Message)o;
            System.out.println("Client->Server:"+channelHandlerContext.channel().remoteAddress()+" send "+msg.getMsgBody());

            String m = "OK.I Get Your Message.";
            Message msg1 = new Message((byte)0xCA, m.length(), m);
            channelHandlerContext.writeAndFlush(msg1);
        }
    }
}

Message:

package Netty3;

public class Message {
    //消息类型
    private byte type;
    //消息长度
    private int length;
    //消息体
    private String msgBody;
    public Message(byte type, int length, String msgBody) {
        this.type = type;
        this.length = length;
        this.msgBody = msgBody;
    }
    public byte getType() {
        return type;
    }
    public void setType(byte type) {
        this.type = type;
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
    public String getMsgBody() {
        return msgBody;
    }
    public void setMsgBody(String msgBody) {
        this.msgBody = msgBody;
    }
}

NewDecoder:

package Netty3;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import java.nio.charset.Charset;

public class NewDecoder extends LengthFieldBasedFrameDecoder {

    /**
     * 我们在Message类中定义了type和length,这都放在消息头部
     * type占1个字节,length占4个字节所以头部总长度是5个字节
     */
    private static final int HEADER_SIZE = 5;
    private byte type;
    private int length;
    private String msgBody;


    /**
     *
     * @param maxFrameLength   网络字节序,默认为大端字节序
     * @param lengthFieldOffset 消息中长度字段偏移的字节数
     * @param lengthFieldLength 数据帧的最大长度
     * @param lengthAdjustment 该字段加长度字段等于数据帧的长度
     * @param initialBytesToStrip 从数据帧中跳过的字节数
     * @param failFast 如果为true,则表示读取到长度域,TA的值的超过maxFrameLength,就抛出一个 TooLongFrameException
     */
    public NewDecoder(int maxFrameLength, int lengthFieldOffset,
                      int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip,
                      boolean failFast) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength,
                lengthAdjustment, initialBytesToStrip, failFast);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        if(in == null){
            return null;
        }
        if(in.readableBytes() < HEADER_SIZE){
            throw new Exception("错误的消息");
        }

        /**
         * 通过源码我们能看到在读的过程中
         * 每读一次读过的字节即被抛弃
         * 即指针会往前跳
         */
        type = in.readByte();
        System.out.println("类型:"+type);

        length = in.readByte();
        System.out.println("长度:"+length);

        if(in.readableBytes() < length){
            throw new Exception("消息不正确");
        }

        ByteBuf buf = in.readBytes(length);

        System.out.println("内容:"+buf.toString(Charset.defaultCharset()));
        byte[] b = new byte[buf.readableBytes()];
        buf.readBytes(b);

        msgBody = new String(b,"UTF-8");
        Message msg = new Message(type,length,msgBody);
        return msg;
    }
}

NewEncoder:

package Netty3;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

import java.nio.charset.Charset;

public class NewEncoder extends MessageToByteEncoder<Message> {

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf byteBuf) throws Exception {
        if(message == null){
            throw new Exception("未获得消息内容");
        }
        String msgBody = message.getMsgBody();
        byte[] b = msgBody.getBytes(Charset.forName("utf-8"));
        byteBuf.writeByte(message.getType());
        byteBuf.writeByte(b.length);
        byteBuf.writeBytes(b);
    }
}

LengthFieldBaseFrameDecoderClient:

package Netty3;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.AttributeKey;

public class LengthFieldBaseFrameDecoderClient {
    private static final int MAX_FRAME_LENGTH = 1024 * 1024;
    private static final int LENGTH_FIELD_LENGTH = 4;
    private static final int LENGTH_FIELD_OFFSET = 1;
    private static final int LENGTH_ADJUSTMENT = 0;
    private static final int INITIAL_BYTES_TO_STRIP = 0;

    private  int port;
    private  String address;

    public LengthFieldBaseFrameDecoderClient(int port,String address) {
        this.port = port;
        this.address = address;
    }

    public void start(){
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new NewClientChannelInitializer(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP));
        try {
            ChannelFuture future = bootstrap.connect(address,port).sync();
            future.channel().closeFuture().sync();

         /*   AttributeKey<String> key = AttributeKey.valueOf("ServerData");
            Object result = future.channel().attr(key).get();
            System.out.println(result.toString());*/

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        LengthFieldBaseFrameDecoderClient client = new LengthFieldBaseFrameDecoderClient(7788,"127.0.0.1");
        client.start();
    }
}

NewClientChannelInitializer:

package Netty3;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.AttributeKey;

import java.nio.charset.Charset;

public class NewClientChannelInitializer extends ChannelInitializer<SocketChannel> {

    private  final int MAX_FRAME_LENGTH;
    private  final int LENGTH_FIELD_LENGTH;
    private  final int LENGTH_FIELD_OFFSET;
    private  final int LENGTH_ADJUSTMENT;
    private  final int INITIAL_BYTES_TO_STRIP;

    public NewClientChannelInitializer(int MAX_FRAME_LENGTH, int LENGTH_FIELD_LENGTH, int LENGTH_FIELD_OFFSET, int LENGTH_ADJUSTMENT, int INITIAL_BYTES_TO_STRIP) {
        this.MAX_FRAME_LENGTH = MAX_FRAME_LENGTH;
        this.LENGTH_FIELD_LENGTH = LENGTH_FIELD_LENGTH;
        this.LENGTH_FIELD_OFFSET = LENGTH_FIELD_OFFSET;
        this.LENGTH_ADJUSTMENT = LENGTH_ADJUSTMENT;
        this.INITIAL_BYTES_TO_STRIP = INITIAL_BYTES_TO_STRIP;
    }

    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();

        pipeline.addLast(new NewEncoder());
        pipeline.addLast(new NewClientHandler());
        pipeline.addLast(new NewDecoder(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP,false));
        /*pipeline.addLast(new ChannelInboundHandlerAdapter() {
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                if (msg instanceof ByteBuf) {
                    String value = ((ByteBuf) msg).toString(Charset.defaultCharset());
                    System.out.println("返回:" + value);
                }
                AttributeKey<String> key = AttributeKey.valueOf("ServerData");
                ctx.channel().attr(key).set("客户端处理完毕");
                ctx.close();
            }
        });*/
    }
}

NewClientHandler:

package Netty3;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NewClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for (int i=0;i<10;i++){
            String m = "你好啊,Netty。";
            Message msg = new Message((byte)0xCA, m.length(), m);
            ctx.writeAndFlush(msg);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值