Packet

Packet 类用于表示业务数据结构。我们通过继承 Packet 来实现自己的业务数据结构。对于大家而言,可以将 Packet 看作一个普通的 VO(Value Object)对象。

注意:不建议直接使用 Packet 类,而是应该继承它来创建自定义的 Packet。

一个简单的 Packet 可能如下:

import com.litongjava.tio.core.intf.Packet;
/**
 * 一个简单的 HelloPacket 示例
 *
 * 作者:tanyaowu
 */
public class HelloPacket extends Packet {
    private static final long serialVersionUID = -172060606924066412L;

    public static final int HEADER_LENGTH = 4; // 消息头的长度
    public static final String CHARSET = "utf-8";

    private byte[] body;

    /**
     * 获取消息体
     *
     * @return 消息体的字节数组
     */
    public byte[] getBody() {
        return body;
    }

    /**
     * 设置消息体
     *
     * @param body 要设置的消息体字节数组
     */
    public void setBody(byte[] body) {
        this.body = body;
    }
}

可以结合 AioHandler 接口来理解 Packet 的作用:

package com.litongjava.tio.core.intf;

import java.nio.ByteBuffer;
import com.litongjava.tio.core.ChannelContext;
import com.litongjava.tio.core.TioConfig;
import com.litongjava.tio.core.exception.TioDecodeException;

/**
 * 定义编码、解码和处理消息包的方法
 *
 * 作者:tanyaowu
 * 日期:2017年10月19日
 */
public interface AioHandler {

    /**
     * 根据 ByteBuffer 解码成业务需要的 Packet 对象。
     * 如果收到的数据不完整,导致解码失败,请返回 null,框架会在下次收到数据时自动拼接之前的数据。
     *
     * @param buffer          参与本次解码的 ByteBuffer
     * @param limit           ByteBuffer 的 limit
     * @param position        ByteBuffer 的 position,不一定是 0
     * @param readableLength  本次可读的有效数据长度(limit - position)
     * @param channelContext  通道上下文
     * @return 解码后的 Packet 对象,若数据不完整则返回 null
     * @throws TioDecodeException 解码异常
     */
    Packet decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws TioDecodeException;

    /**
     * 编码方法
     *
     * @param packet          要编码的 Packet 对象
     * @param tioConfig       Tio 配置
     * @param channelContext  通道上下文
     * @return 编码后的 ByteBuffer
     */
    ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext);

    /**
     * 处理收到的消息包
     *
     * @param packet          收到的 Packet 对象
     * @param channelContext  通道上下文
     * @throws Exception      处理异常
     */
    void handler(Packet packet, ChannelContext channelContext) throws Exception;
}

常见的 Packet 实现

以下是一些常见的 Packet 类的内部实现示例,供大家参考。

ByteBufferPacket

package com.litongjava.tio.boot.tcp;

import java.nio.ByteBuffer;
import com.litongjava.tio.core.intf.Packet;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

/**
 * 一个使用 ByteBuffer 的 Packet 实现
 */
@NoArgsConstructor
@AllArgsConstructor
public class ByteBufferPacket extends Packet {
    private static final long serialVersionUID = 1L;

    private ByteBuffer byteBuffer;

    /**
     * 获取 ByteBuffer
     *
     * @return ByteBuffer 对象
     */
    public ByteBuffer getByteBuffer() {
        return byteBuffer;
    }

    /**
     * 设置 ByteBuffer
     *
     * @param byteBuffer 要设置的 ByteBuffer 对象
     */
    public void setByteBuffer(ByteBuffer byteBuffer) {
        this.byteBuffer = byteBuffer;
    }
}

EncodedPacket

package com.litongjava.tio.core.intf;

/**
 * 一个包含原始字节数据的 Packet 实现
 *
 * 作者:tanyaowu
 * 日期:2017年4月1日
 */
public class EncodedPacket extends Packet {
    private static final long serialVersionUID = 1014364783783749718L;

    private byte[] bytes;

    /**
     * 构造方法,使用指定的字节数组初始化
     *
     * @param bytes 原始字节数据
     */
    public EncodedPacket(byte[] bytes) {
        this.bytes = bytes;
    }

    /**
     * 获取字节数据
     *
     * @return 字节数组
     */
    public byte[] getBytes() {
        return bytes;
    }

    /**
     * 设置字节数据
     *
     * @param bytes 要设置的字节数组
     */
    public void setBytes(byte[] bytes) {
        this.bytes = bytes;
    }
}

通过以上示例,可以更好地理解如何通过继承 Packet 类来实现自定义的业务数据结构,以及在 Tio 框架中如何编码、解码和处理这些数据包。