Netty入门P7

服务端与客户端的通讯协议

服务端与客户端使用协议进行通讯的过程大致如下:

  1. 客户端将一个Java对象按照通讯协议转换成二进制数据包;
  2. 通过网络将二进制数据包发送到服务端,传输过程由TCP/IP协议负责,与应用层无关;
  3. 服务端接收到数据包后,按照协议取出二进制数据包中的相应字段,然后根据取出的数据生成Java对象,然后再由业务逻辑处理层解析Java对象;
  4. 待服务端处理完成后,如果要反馈响应信息,则重复123步;

通讯协议的简单设计

简单协议示意图:
简单协议

  1. 头部标志位

    头部标志位用于区分是否是目标数据包,通常为固定的几个字节的字段;

  2. 序列化算法

    表示此数据包应该使用何种序列化算法转化为Java对象;

  3. 指令

    业务相关字段,表示此数据包属于何种指令;

  4. 数据体长度

    表示数据体的长度,用于获取正确的数据体;

  5. 数据体

    信息;

通讯协议的实现

用于保存指令集常量的Command.java

public interface Command {
    /**
     * 登录请求
     */
    byte LOGIN_REQUEST = 1;
}

用于保存序列化算法常量的SerializerAlgorithm.java

public interface SerializerAlgorithm {

    /**
     * 使用JSON的序列化方式
     */
    byte JSON = 1;
}

基础数据包实体类BaseFrame.java

/**
 * @program: learnnetty
 * @description: 基础类
 * @create: 2020-05-06 10:57
 **/
public abstract class BaseFrame {
    /**
     * 版本号
     */
    private Byte version = 1;

    /**
     * 获取指令
     * @return 指令
     */
    public abstract Byte getCommand();
}

用于处理登录请求的LoginFrame.java

/**
 * @program: learnnetty
 * @description: 登录用
 * @create: 2020-05-06 10:58
 **/
public class LoginFrame extends BaseFrame {
    /**
     * 用户ID
     */
    private Integer userId;

    /**
     * 用户名
     */
    private String userName;

    /**
     * 密码
     */
    private String password;

    @Override
    public Byte getCommand() {
        return Command.LOGIN_REQUEST;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

序列化方法基础接口Serializer.java

public interface Serializer {

    byte JSON_SERIALIZER = 1;

    /**
     * 获取序列化算法
     * @return 算法编号
     */
    byte gerSerializerAlgorithm();

    /**
     * 对象序列化
     * @param o 对象
     * @return 序列化后
     */
    byte[] serialize(Object o);

    /**
     * 反序列化
     * @param clazz 目标对象类型
     * @param bytes 数据包
     * @param <T> 类型
     * @return 对象
     */
    <T> T deserialize(Class<T> clazz, byte[] bytes);

    /**
     * 默认序列化方式
     */
    Serializer DEFAULT = new JSONSerializer();
}

用于Java对象与数据包互换的ParseFrame.java

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

    private static final int HEAD_FLAG = 0x990718;

    /**
     * 数据包的序列化
     * @param frame 对象
     * @return 完成序列化
     */
    public ByteBuf encode(BaseFrame frame){
        //此处使用IO读写相关的内存,直接内存不受JVM管理,写到IO缓冲区效率更高
        ByteBuf buf = ByteBufAllocator.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;
    }
}

根据请求获取对应Java对象Class的RequestUtil.java

/**
 * @program: learnnetty
 * @description: 获取请求的类型
 * @create: 2020-05-06 11:28
 **/
public class RequestUtil {

    /**
     * 获取指定的类型
     * @param command 命令序号
     * @return 类型
     */
    public static Class<? extends BaseFrame> getRequestType(byte command){
        switch (command){
            case Command.LOGIN_REQUEST:
                return LoginFrame.class;
            default:
                return null;
        }
    }
}

用于获取序列化算法的SerializerUtil.java

/**
 * @program: learnnetty
 * @description: 序列化工具类
 * @create: 2020-05-06 11:31
 **/
public class SerializerUtil {

    /**
     * 获取序列化方式
     * @param serializeAlgorithm 序列化标志位
     * @return 序列化方式
     */
    public static Serializer getSerializer(byte serializeAlgorithm ){
        switch (serializeAlgorithm){
            case Serializer.JSON_SERIALIZER:
                return new JSONSerializer();
            default:
                return null;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值