Netty(6)-客户端登陆并与服务端收发信息

一.实现流程图

在这里插入图片描述

二.代码实现

  • 整体结构图
    在这里插入图片描述

  • utils/Attributes

/**
 * @Auther: ARong
 * @Date: 2020/2/7 9:47 下午
 * @Description: channel的参数
 */
public interface Attributes {
    AttributeKey<Boolean> LOGIN = AttributeKey.newInstance("login");
}
  • utils/Command
/*
 * @Author ARong
 * @Description 定义指令集
 * @Date 2020/2/4 8:58 下午
 **/
public interface Command {
    byte LOGIN_REQUEST = 1; // 登陆请求指令
    byte LOGIN_RESPONSE = 2; // 登陆响应指令
    byte MESSAGE_REQUEST = 3; // 普通消息请求指令
    byte MESSAGE_RESPONSE = 4; // 普通消息响应指令
}
  • utils/MyJsonSerializer
/**
 * @Auther: ARong
 * @Date: 2020/2/4 9:09 下午
 * @Description:
 */
public class MyJsonSerializer implements MySerializer {
    /*
     * @Author ARong
     * @Description 自定义JSON序列化器 将Java对象转化为二进制字节流
     * @Date 2020/2/4 9:07 下午
     * @Param object
     * @return byte[]
     **/
    @Override
    public byte[] serialize(Object object) {
        return JSON.toJSONBytes(object);
    }

    /*
     * @Author ARong
     * @Description 二进制转换成 java 对象
     * @Date 2020/2/4 9:08 下午
     * @Param [clazz, bytes]
     * @return T
     **/
    @Override
    public <T> T deserialize(Class<T> clazz, byte[] bytes) {
        return JSON.parseObject(bytes, clazz);
    }
}
  • utils/MySerializer
/*
 * @Author ARong
 * @Description 定义序列化器
 * @Date 2020/2/4 9:06 下午
 **/
public interface MySerializer {

    /*
     * @Author ARong
     * @Description 将Java对象转化为二进制字节流
     * @Date 2020/2/4 9:07 下午
     * @Param object
     * @return byte[]
     **/
    byte[] serialize(Object object);


    /*
     * @Author ARong
     * @Description 二进制转换成 java 对象
     * @Date 2020/2/4 9:08 下午
     * @Param [clazz, bytes]
     * @return T
     **/
    <T> T deserialize(Class<T> clazz, byte[] bytes);
}
  • utils/PacketUtil
/**
 * @Auther: ARong
 * @Date: 2020/2/4 9:14 下午
 * @Description: 用于包编码和解码
 */
public class PacketUtil {
    /*
     * @Author ARong
     * @Description 将Packet按照约定序列化方式编码成ByteBuf
     * @Date 2020/2/4 9:15 下午
     * @Param [packet]
     * @return io.netty.buffer.ByteBuf
     **/
    public static ByteBuf encode(Packet packet) {
        // 创建ByteBuf对象
        ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
        // 获取序列化器序列化对象
        MySerializer serializer = SerializerFactory.getSerializer(packet.getSerMethod());
        byte[] data = serializer.serialize(packet);
        // 按照通信协议填充ByteBUf
        byteBuf.writeInt(packet.getMagic());// 魔数
        byteBuf.writeByte(packet.getVersion()); // 版本号
        byteBuf.writeByte(packet.getSerMethod()); // 序列化方式
        byteBuf.writeByte(packet.getCommand()); // 指令
        byteBuf.writeInt(data.length);// 数据长度
        byteBuf.writeBytes(data); // 数据
        return byteBuf;
    }

    /*
     * @Author ARong
     * @Description 将ByteBuf按照约定序列化方式解码成Packet
     * @Date 2020/2/4 9:21 下午
     * @Param [byteBuf]
     * @return io_learn.netty.part4_protocol.packet.Packet
     **/
    public static Packet decode(ByteBuf byteBuf) {
        // 暂不判断魔数,跳过
        byteBuf.skipBytes(4);
        // 暂不判断魔数版本号,跳过
        byteBuf.skipBytes(1);
        // 获取序列化方式
        byte serMethod = byteBuf.readByte();
        // 获取指令
        byte command = byteBuf.readByte();
        // 获取数据包长度
        int length = byteBuf.readInt();
        // 获取存储数据的字节数组
        byte[] data = new byte[length];
        byteBuf.readBytes(data);
        // 反序列化数据,获取Packet
        Class<? extends Packet> packetType = getPacketType(command);
        Packet res = SerializerFactory.getSerializer(serMethod).deserialize(packetType, data);
        return res;
    }


    /*
     * @Author ARong
     * @Description 通过指令获取相应的Packet
     * @Date 2020/2/4 9:31 下午
     * @Param [commond]
     * @return io_learn.netty.part4_protocol.packet.Packet
     **/
    public static Class<? extends Packet> getPacketType(byte commond) {
        if (commond == Command.LOGIN_REQUEST) {
            return LoginRequestPacket.class;
        }
        if (commond == Command.LOGIN_RESPONSE) {
            return LoginResponsePacket.class;
        }
        if (commond == Command.MESSAGE_REQUEST) {
            return MessageRequestPacket.class;
        }
        if (commond == Command.MESSAGE_RESPONSE) {
            return MessageResponsePacket.class;
        }
        return null;
    }
}
  • utils/Serialize
/*
 * @Author ARong
 * @Description 定义序列化方式
 * @Date 2020/2/4 9:05 下午
 **/
public interface Serialize {
    byte JSON = 1;
}
  • utils/SerializerFactory
/*
 * @Author ARong
 * @Description 序列化器工厂
 * @Date 2020/2/4 8:56 下午
 **/
public class SerializerFactory {
    /*
     * @Author ARong
     * @Description 通过序列化名称获取相应的序列化器
     * @Date 2020/2/4 9:17 下午
     * @Param [serName]
     * @return io_learn.netty.part4_protocol.utils.MySerializer
     **/
    public static MySerializer getSerializer(byte serName) {
        if (serName == Serialize.JSON) {
            return new MyJsonSerializer();
        }
        return null;
    }
}
  • packet/Packet
/**
 * @Auther: ARong
 * @Date: 2020/2/4 8:44 下午
 * @Description: 自定义通信协议的抽象包
 */
@Data
public abstract class Packet {
    private int magic; // 魔数
    private byte version; // 版本号
    private byte serMethod; // 的序列化/反序列化方式
    private byte command; // 指令
}
  • packet/MessageResponsePacket
/**
 * @Auther: ARong
 * @Date: 2020/2/4 8:52 下午
 * @Description: 用以数据请求响应的协议包
 */
@Data
public class MessageResponsePacket extends Packet {
    private int magic = 20202020; // 魔数
    private byte version = 1; // 版本号
    private byte serMethod = Serialize.JSON; // 序列化反序列化方式为Json
    private byte command = Command.MESSAGE_RESPONSE; // 登陆响应指令
    // ---------以下为返回的响应数据----------
    private String message;

    @Override
    public String toString() {
        return "MessageRequestPacket{" +
                "magic=" + magic +
                ", version=" + version +
                ", serMethod=" + serMethod +
                ", command=" + command +
                ", message='" + message + '\'' +
                '}';
    }
}
  • packet/MessageRequestPacket
/**
 * @Auther: ARong
 * @Date: 2020/2/4 8:52 下午
 * @Description: 用以数据请求的协议包
 */
@Data
public class MessageRequestPacket extends Packet {
    private int magic = 20202020; // 魔数
    private byte version = 1; // 版本号
    private byte serMethod = Serialize.JSON; // 序列化反序列化方式为Json
    private byte command = Command.MESSAGE_REQUEST; // 登陆响应指令
    // ---------以下为请求数据----------
    private String message;

    @Override
    public String toString() {
        return "MessageRequestPacket{" +
                "magic=" + magic +
                ", version=" + version +
                ", serMethod=" + serMethod +
                ", command=" + command +
                ", message='" + message + '\'' +
                '}';
    }
}
  • packet/LoginResponsePacket
/**
 * @Auther: ARong
 * @Date: 2020/2/4 8:52 下午
 * @Description: 用以登陆响应协议包
 */
@Data
public class LoginResponsePacket extends Packet {
    private int magic = 20202020; // 魔数
    private byte version = 1; // 版本号
    private byte serMethod = Serialize.JSON; // 序列化反序列化方式为Json
    private byte command = Command.LOGIN_RESPONSE; // 登陆响应指令
    // ---------以下为登陆响应数据----------
    private boolean status;
    private String message;

    @Override
    public String toString() {
        return "LoginResponsePacket{" +
                "magic=" + magic +
                ", version=" + version +
                ", serMethod=" + serMethod +
                ", command=" + command +
                ", loginStatus=" + status +
                ", message='" + message + '\'' +
                '}';
    }
}
  • packet/LoginRequestPacket
/**
 * @Auther: ARong
 * @Date: 2020/2/4 8:52 下午
 * @Description: 用以登陆的登陆协议包
 */
@Data
public class LoginRequestPacket extends Packet {
    private int magic = 20202020; // 魔数
    private byte version = 1; // 版本号
    private byte serMethod = Serialize.JSON; // 序列化反序列化方式为Json
    private byte command = Command.LOGIN_REQUEST; // 登陆指令
    // ---------以下为登陆数据----------
    private long userId;
    private String name;
    private String password;


    @Override
    public String toString() {
        return "LoginPacket{" +
                "magic=" + magic +
                ", version=" + version +
                ", serMethod=" + serMethod +
                ", command=" + command +
                ", userId=" + userId +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
  • handler/MyServerHandler
public class MyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 解码
        ByteBuf response = ByteBufAllocator.DEFAULT.ioBuffer();
        Packet packet = PacketUtil.decode((ByteBuf) msg);
        if (packet instanceof LoginRequestPacket) {
            System.out.println("=====服务端接收到客户端登陆请求,正在校验=====");
            LoginRequestPacket request = (LoginRequestPacket) packet;
            LoginResponsePacket responsePacket = new LoginResponsePacket();
            if (check(request)) {
                System.out.println("登陆成功:"+request);
                // 为channel设置登陆成功属性,下次请求时进行校验
                ctx.channel().attr(Attributes.LOGIN).set(true);
                responsePacket.setStatus(true);
            } else {
                System.out.println("登陆失败:"+request);
                responsePacket.setStatus(false);
            }
            // 编码登陆响应返回
            response = PacketUtil.encode(responsePacket);
        }

        if (packet instanceof MessageRequestPacket) {
            // 首先获取登陆状态
            if (ctx.channel().attr(Attributes.LOGIN).get() == null) {
                System.out.println("=====客户端尚未登陆,无法进行数据通信=====");
            } else {
                System.out.println("=====客户端登陆成功,进行数据通信=====");
                MessageRequestPacket request = (MessageRequestPacket) packet;
                System.out.println("收到:"+request.toString());
                // 编码
                MessageResponsePacket responsePacket = new MessageResponsePacket();
                responsePacket.setMessage("数据通信响应测试Reponse Test1");
                response = PacketUtil.encode(responsePacket);
            }
        }
        ctx.writeAndFlush(response);
    }

    private boolean check(LoginRequestPacket request) {
        // 硬编码校验条件
        if ("ARong".equals(request.getName()) && "2020".equals(request.getPassword())) {
            return true;
        }
        return false;
    }
}
  • hanlder/MyClientHandler
/**
 * @Auther: ARong
 * @Date: 2020/2/5 1:17 下午
 * @Description: 客户端的Handler
 */
public class MyClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 第一次连接,首先尝试登陆
        System.out.println("=====客户端连接成功,尝试登陆=====");
        // 收集需要编码的登陆消息
        Scanner input = new Scanner(System.in);
        LoginRequestPacket packet = new LoginRequestPacket();
        System.out.println("请输入登陆的用户ID");
        packet.setUserId(Long.parseLong(input.nextLine()));
        System.out.println("请输入登陆的用户账户");
        packet.setName(input.nextLine());
        System.out.println("请输入登陆的用户密码");
        packet.setPassword(input.nextLine());
        System.out.println("=====客户端开始发送登陆数据包=====");
        // 编码数据包进行发送
        ByteBuf request = PacketUtil.encode(packet);
        ctx.writeAndFlush(request);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 解码
        Packet packet = PacketUtil.decode((ByteBuf) msg);
        if (packet instanceof LoginResponsePacket) {
            LoginResponsePacket response = (LoginResponsePacket) packet;
            if (response.isStatus()) {
                System.out.println("=====客户端登陆成功=====");
            } else {
                System.out.println("=====客户端登陆失败=====");
            }
            // 编码
            MessageRequestPacket request = new MessageRequestPacket();
            request.setMessage("数据通信请求测试Request Test1");
            ByteBuf byteBuf = PacketUtil.encode(request);
            ctx.writeAndFlush(byteBuf);
        }

        if (packet instanceof MessageResponsePacket) {
            MessageResponsePacket request = (MessageResponsePacket) packet;
            System.out.println("收到:"+request.toString());
        }
    }
}
  • server/NettyServer
/**
 * @Auther: ARong
 * @Date: 2020/2/4 9:13 下午
 * @Description: Netty Server
 */
public class NettyServer {
    public static void main(String[] args) {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(
                        new ChannelInitializer<SocketChannel>() {
                            @Override
                            // 初始化channel
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new MyServerHandler());
                            }
                        }
                ).bind(8000);
    }
}

  • server/NettyClient
/**
 * @Auther: ARong
 * @Date: 2020/2/5 1:28 下午
 * @Description:
 */
public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        Bootstrap bootstrap = new Bootstrap();
        NioEventLoopGroup group = new NioEventLoopGroup();

        bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new MyClientHandler());
            }
        });
        // 连接服务端
        bootstrap.connect("127.0.0.1", 8000);
    }
}

在这里插入图片描述

在这里插入图片描述

发布了309 篇原创文章 · 获赞 205 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/pbrlovejava/article/details/104217576