Netty入门P8

客户端与服务端的交互流程

大致流程如下:

  1. 客户端构建登录用请求对象;
  2. 客户端编码成ByteBuf;
  3. 客户端写出数据(writeAndFlush());
  4. 服务端收到ByteBuf;
  5. 服务端解码成登录请求;
  6. 服务端登录校验;
  7. 服务端构建登录响应对象;
  8. 服务端编码成ByteBuf;
  9. 服务端写出数据(writeAndFlush());
  10. 客户端收到ByteBuf;
  11. 客户端解码成登录响应;
  12. 客户端处理响应结果;

客户端发送登录请求

修改后的ParseUtil.java

/**
 * @program: learnnetty
 * @description: 数据包的转换
 * @create: 2020-05-06 11:09
 **/
public class ParseFrame {

    private static final int HEAD_FLAG = 0x990718;

    private static ParseFrame parseFrame = null;

    private ParseFrame() {
    }

    public static ParseFrame getInstance() {
        if (parseFrame == null){
            parseFrame = new ParseFrame();
        }
        return parseFrame;
    }

    /**
     * 数据包的序列化
     * @param frame 对象
     * @return 完成序列化
     */
    public ByteBuf encode(ByteBufAllocator bba, BaseFrame frame){
        //此处使用IO读写相关的内存,直接内存不受JVM管理,写到IO缓冲区效率更高
        ByteBuf buf = bba.DEFAULT.ioBuffer();
        byte[] data = Serializer.DEFAULT.serialize(frame);

        //组装报文
        buf.writeInt(HEAD_FLAG);
        buf.writeByte(Serializer.DEFAULT.gerSerializerAlgorithm());
        buf.writeByte(frame.getCommand());
        buf.writeInt(data.length);
        buf.writeBytes(data);

        return buf;
    }

    /**
     * 逆序列化
     * @param byteBuf 数据
     * @return 对象
     */
    public BaseFrame decode(ByteBuf byteBuf){
        //跳过头部标志位
        byteBuf.skipBytes(4);
        //获取序列化算法
        byte serializerAlgorithm = byteBuf.readByte();
        //获取指令
        byte command = byteBuf.readByte();
        //获取数据长度
        int length = byteBuf.readInt();
        //获取数据
        byte[] data = new byte[length];
        byteBuf.readBytes(data);

        Class<? extends BaseFrame> requestType = RequestUtil.getRequestType(command);
        Serializer serializer = SerializerUtil.getSerializer(serializerAlgorithm);

        if (requestType != null && serializer != null){
            return serializer.deserialize(requestType, data);
        }

        return null;
    }
}

修改后的Client.java

/**
 * @program: learnnetty
 * @description: 客户端
 * @create: 2020-05-06 15:39
 **/
public class Client {
    private static final int MAX_RETRY = 5;

    public static void main(String[] args) {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();
        bootstrap
                .group(workerGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                        nioSocketChannel
                                .pipeline()
                                .addLast(new ClientHandler());
                    }
                });

       connect(bootstrap, "127.0.0.1", 8080, MAX_RETRY);
    }

    private static void connect(final Bootstrap bootstrap, final String host, final int port, final int retry){
        bootstrap.connect(host, port).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()){
                    System.out.println("客户端连接成功");
                }else if (retry == 0){
                    System.out.println("客户端连接失败次数过多,放弃连接");
                }else {
                    System.out.println("客户端连接失败,尝试重连");
                    int order = MAX_RETRY - retry + 1;
                    int delay = 1 << order;
                    System.out.println(new Date() + " 连接失败,第" + order + "次连接");
                    bootstrap.config().group().schedule(new Runnable() {
                        @Override
                        public void run() {
                            connect(bootstrap, host, port, retry - 1);
                        }
                    }, delay, TimeUnit.SECONDS);
                }
            }
        });
    }
}

修改后的ClientHandler.java

/**
 * @program: learnnetty
 * @description: 客户端业务处理
 * @create: 2020-05-06 15:40
 **/
public class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(new Date() + ",客户端开始登录");

        LoginRequestFrame loginFrame = new LoginRequestFrame();
        loginFrame.setUserId(UUID.randomUUID().toString());
        loginFrame.setUserName("zcd");
        loginFrame.setPassword("zzz");

        ByteBuf buf = ParseFrame.getInstance().encode(ctx.alloc(), loginFrame);

        ctx.channel().writeAndFlush(buf);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;

        BaseFrame frame = ParseFrame.getInstance().decode(byteBuf);
        if (frame instanceof LoginResponseFrame){
            LoginResponseFrame responseFrame = (LoginResponseFrame) frame;

            if (responseFrame.isSuccess()){
                System.out.println("客户端登录成功");
            }else {
                System.out.println("客户端登录失败,原因:" + responseFrame.getMsg());
            }
        }
    }
}

修改后的Server.java

/**
 * @program: learnnetty
 * @description: 服务端
 * @create: 2020-05-06 17:18
 **/
public class Server {

    public static void main(String[] args) {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                        nioSocketChannel
                                .pipeline()
                                .addLast(new ServerHandler());
                    }
                });

        bind(serverBootstrap, 8080);
    }

    private static void bind(final ServerBootstrap serverBootstrap, final int port){
        serverBootstrap.bind(port).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()){
                    System.out.println("端口[" + port + "]绑定成功");
                }else {
                    System.out.println("端口[" + port + "]绑定失败,尝试绑定[" + (port + 1) + "]端口");
                    bind(serverBootstrap, port + 1);
                }
            }
        });
    }
}

修改后的ServerHandler.java

/**
 * @program: learnnetty
 * @description: 服务端处理
 * @create: 2020-05-06 17:19
 **/
public class ServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(new Date() + ",服务端开始处理登录请求");
        ByteBuf buf = (ByteBuf)msg;

        BaseFrame baseFrame = ParseFrame.getInstance().decode(buf);
        if (baseFrame instanceof LoginRequestFrame){
            LoginRequestFrame requestFrame = (LoginRequestFrame)baseFrame;
            LoginResponseFrame responseFrame = new LoginResponseFrame();
            responseFrame.setUserId(requestFrame.getUserId());
            responseFrame.setUserName(requestFrame.getUserName());

            if(requestFrame.getUserName().equals("zcd") && requestFrame.getPassword().equals("zzz")){
                responseFrame.setSuccess(true);
                responseFrame.setMsg("登录成功");
                System.out.println(new Date() + ", zcd客户端登录成功");
            }else {
                responseFrame.setSuccess(false);
                responseFrame.setMsg("密码或用户名错误");
                System.out.println(new Date() + ", zcd客户端登录失败");
            }

            ByteBuf respByteBuf = ParseFrame.getInstance().encode(ctx.alloc(), responseFrame);
            ctx.channel().writeAndFlush(respByteBuf);
        }
    }
}

客户端输出:

客户端连接成功
Wed May 06 17:34:25 CST 2020,客户端开始登录
客户端登录成功

服务端输出:

端口[8080]绑定成功
Wed May 06 17:34:26 CST 2020,服务端开始处理登录请求
Wed May 06 17:34:26 CST 2020, zcd客户端登录成功
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值