Netty 序列化

Netty内置的序列化器

  • JBoss Marshalling 和 Protocol Buffers

集成第三方 MessagePack

  • LengthFieldBasedFrame 详解

在这里插入图片描述
在这里插入图片描述

  1. maxFrameLength:表示的是包的最大长度
  2. lengthFieldOffset:指的是长度域的偏移量,表示跳过指定个数字节之后的才是长度域;
  3. lengthFieldLength:记录该帧数据长度的字段,也就是长度域本身的长度;
  4. lengthAdjustment:长度的一个修正值,可正可负,Netty 在读取到数据包的长度值 N 后, 认为接下来的 N 个字节都是需要读取的,但是根据实际情况,有可能需要增加 N 的值,也有可能需要减少 N 的值,具体增加多少,减少多少,写在这个参数里;
  5. initialBytesToStrip:从数据帧中跳过的字节数,表示得到一个完整的数据包之后,扔掉这个数据包中多少字节数,才是后续业务实际需要的业务数据。
  6. failFast:如果为 true,则表示读取到长度域,TA 的值的超过 maxFrameLength,就抛出一个 TooLongFrameException,而为 false 表示只有当真正读取完长度域的值表示的字节之后, 才会抛出 TooLongFrameException,默认情况下设置为 true,建议不要修改,否则可能会造成内存溢出。

pom

在这里插入图片描述

<dependency>
    <groupId>org.msgpack</groupId>
    <artifactId>msgpack</artifactId>
    <version>0.6.12</version>
</dependency>

创建实体类

  • 注意要添加@Message注解

在这里插入图片描述

package org.example.msgpack;

import org.msgpack.annotation.Message;

@Message
public class User {
    
    
    private String userName;
    private int age;

    public User() {
    
    
    }

    public User(String userName, int age) {
    
    
        this.userName = userName;
        this.age = age;
    }

    public String getUserName() {
    
    
        return userName;
    }

    public void setUserName(String userName) {
    
    
        this.userName = userName;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }
}

创建解码器

在这里插入图片描述

package org.example.msgpack;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.msgpack.MessagePack;

import java.util.List;

//基于MessagePack的解码器,反序列化
public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf> {
    
    
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
    
    
        //获取消息长度
        int length = byteBuf.readableBytes();
        //创建字节数据,并定义长度
        byte[] bytes = new byte[length];
        //将byteBuf转换为字节数组
        byteBuf.getBytes(byteBuf.readerIndex(), bytes);
        MessagePack messagePack = new MessagePack();
        //将字节数组解码成User对象,并添加到解码器集合中
        list.add(messagePack.read(bytes, User.class));
    }
}

创建编码器

在这里插入图片描述

package org.example.msgpack;

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

//基于MessagePack的编码器,序列化
public class MsgPackEncoder extends MessageToByteEncoder<User> {
    
    
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, User user, ByteBuf byteBuf) throws Exception {
    
    
        MessagePack messagePack = new MessagePack();
        //将User对象编码成字节数组
        byte[] write = messagePack.write(user);
        //将字节数组写入ByteBuf
        byteBuf.writeBytes(write);
    }
}

创建客户端handler

在这里插入图片描述

package org.example.msgpack;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

import java.util.concurrent.atomic.AtomicInteger;

public class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    
    
    private AtomicInteger counter = new AtomicInteger(0);

    //客户端读取到网络数据后的处理
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
    
    
        System.out.println("客户端接收的消息:" + byteBuf.toString(CharsetUtil.UTF_8) + ", count = " + counter.incrementAndGet());
    }

    //channel活跃时,做业务处理
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    
    
        //分5次写入,一起刷新
        for (int i = 0; i < 5; i++) {
    
    
            User user = new User();
            user.setUserName("user" + i);
            user.setAge(i);
            System.out.println("发送:" + user);
            ctx.write(user);
        }
        ctx.flush();
    }
}

创建客户端

在这里插入图片描述

package org.example.msgpack;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
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.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.LineBasedFrameDecoder;

import java.net.InetSocketAddress;

public class Client {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    
        //线程组
        EventLoopGroup group = new NioEventLoopGroup();
        //客户端必备
        Bootstrap b = new Bootstrap();
        try {
    
    
            b.group(group)//将线程组传入
                    .channel(NioSocketChannel.class)//指定使用NIO进行网络传输
                    .remoteAddress(new InetSocketAddress("127.0.0.1", 9001))//配置要连接服务器的ip地址和端口
                    .handler(new ChannelInitializer<Channel>() {
    
    
                        @Override
                        protected void initChannel(Channel channel) throws Exception {
    
    
                            //通过LengthFieldPrepender计算报文长度,把这个值存入报文头,并指定大小2字节,2的16次方,即长度不能超过65536
                            channel.pipeline().addLast(new LengthFieldPrepender(2));
                            //对服务器应答报文解码,解决粘包半包,服务端的应答报文这里使用回车换行符进行分隔
                            channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                            //对发送的数据做编码-序列化
                            channel.pipeline().addLast(new MsgPackEncoder());
                            channel.pipeline().addLast(new ClientHandler());
                        }
                    });
            ChannelFuture future = b.connect().sync();
            System.out.println("已连接到服务器。。。");
            future.channel().closeFuture().sync();
        } finally {
    
    
            //优雅关闭线程组
            group.shutdownGracefully().sync();
        }
    }

}

创建服务端handler

在这里插入图片描述

package org.example.msgpack;

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

import java.util.concurrent.atomic.AtomicInteger;

public class ServerHandler extends ChannelInboundHandlerAdapter {
    
    
    private AtomicInteger counter = new AtomicInteger(0);

    //服务端读取到网络数据后的处理
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
        //将上一个handler解码后的数据强转
        User user = (User) msg;
        System.out.println("服务端接收的数据:" + user + ", count = " + counter.incrementAndGet());
        //服务器应答消息,使用回车换行符分隔
        String resp = "user :" + user.getUserName() + System.getProperty("line.separator");
        ctx.writeAndFlush(Unpooled.copiedBuffer(resp.getBytes()));
        //传递消息至下一个处理器
        ctx.fireChannelRead(user);
    }
    
}

创建服务端

在这里插入图片描述

package org.example.msgpack;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
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.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import java.net.InetSocketAddress;

public class Server {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    
        EventLoopGroup group = new NioEventLoopGroup();
        //服务端启动必须
        ServerBootstrap b = new ServerBootstrap();
        try {
    
    
            b.group(group).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(9001))
                    .childHandler(new ChannelInitializer<Channel>() {
    
    
                        @Override
                        protected void initChannel(Channel channel) throws Exception {
    
    
                            //最大长度65536,偏移量0,长度值长度2字节,长度修正值0,丢弃2字节(把存放长度值的2字节消息头丢弃)
                            channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,
                                    0, 2, 0, 2));
                            //解码
                            channel.pipeline().addLast(new MsgPackDecoder());
                            channel.pipeline().addLast(new ServerHandler());
                        }
                    });
            ChannelFuture f = b.bind().sync();
            System.out.println("服务器启动完成,等待客户端的连接和数据.....");
            f.channel().closeFuture().sync();
        } finally {
    
    
            //优雅关闭线程组
            group.shutdownGracefully().sync();
        }
    }
    
}

启动服务端,再启动客户端

  • 服务端启动成功

在这里插入图片描述

  • 客户端打印

在这里插入图片描述

  • 服务端打印

在这里插入图片描述

代码下载地址

https://gitee.com/fisher3652/netWork.git

猜你喜欢

转载自blog.csdn.net/qq_40977118/article/details/109612260
今日推荐