Cindy Message/MessageRecognizer interface

CindyMessage接口解析
本文详细解释了Cindy中Message接口的作用及其实现方法,包括如何将对象转换为字节流进行网络传输,并探讨了与Java Serializable接口的区别。
 
Cindy Message/MessageRecognizer interface

在用户对我的反馈中,其中最常见的就是对Message和MessageRecongizer接口的误用。
 
大家对Java里面的Serializable接口应该都不陌生吧,通过实现序列化接口,一个Java对象可以与字节流做双向转换。在网络传输中是没有对象的概念的,最基本的传输单位是字节,所以如果要通过网络来传输一个对象,就得按照如下三步来进行:

  1. 将对象转换为字节流
  2. 传输字节流
  3. 接收方接收字节流,并转换为对象

Cindy中Message接口就是这个转换的桥梁。Message接口的toByteBuffer方法就实现了该如何把这个对象转换为字节 流,readFromBuffer方法就实现了如何把字节流转换为该对象,至于传输,则由相应的Session来完成。这里要指出的Message和 Serializable接口不同的是,如果某类实现了Serializable接口,这个双向转换的过程可以由虚拟机来自动完成,而Message接口 则需要用户通过编程来明确指定转换方式。

假设有这个一个User对象需要在网络上传输:

public User {
    private String name; //假设不超过255个字节
    private byte age;
    ...get / set
}

我们可以定义若干种不同的方式将其转换为字节流,这取决于我们的业务需要。比如我们可以将其按如下的方式转换:

name UTF-8编码长度(byte) + name UTF-8编码(byte[]) + age(byte)

也可以按如下方式转换:

user对象总长度(4 byte) + age(byte) + name UTF-8编码(byte[])

如果是第一种方式,那么我们可以让这个User类实现Message接口,并且toByteBuffer方法可以这样写:

  public ByteBuffer[] toByteBuffer() {
        //转换为UTF-8编码
        ByteBuffer nameBuffer = CharsetUtils.UTF-8.encode(name); 
        //第一个字节是长度,后面跟着UTF-8编码,再后面跟着age
        ByteBuffer buffer = ByteBuffer.allocate(1 + nameBuffer.remaining() + 1);
        buffer.put((byte)nameBuffer.remaining());
        buffer.put(nameBuffer);
        buffer.put(age);
        return new ByteBuffer[] {buffer};
   }

这样我们就定义好了如何将这个User对象转换为字节流了。

当从网络上读取这个User对象时,要注意的地方就要稍微多一点了。读取的时候不能假定所有的数据都已经传输完成了,很有可能这个User对象传输 到了一半的时候,就开始识别对象了,所以readFromBuffer方法返回值是boolean值,只有在该对象已经完全传输完毕的时候才返回 true,通知框架该对象已传输完毕。

比如这个User对象的readFromBuffer方法可以这样写:

public boolean readFromBuffer(ByteBuffer buffer) {
if (!buffer.hasRemaining())
    return false;
byte length = buffer.get(buffer.position());
if (buffer.remaining() < 1 + length + 1)  //消息没有接收完全,返回false
    return false;
//消息已经全部接收完,读取消息
ByteBuffer nameBuffer = (ByteBuffer) buffer.slice().position(1).limit(length+1);
name = CharsetUtils.UTF-8.decode(nameBuffer).toString();
buffer.position(buffer.position+1+length);
age = buffer.get();
return true;
}

注意红色的代码,在这里并没有用buffer.get(),而是使用了buffer.get(buffer.position())。因为这个时候 还没有确定消息是不是接收完毕,如果直接通过buffer.get()读取后,buffer的position就会发生变化,如果发现消息没有接收完毕, 返回前就需要重新将position移回到原来的位置。

比如如果要接收的User对象字节流是 3 a b c 20,第一次触发readFromBuffer方法是由于接收到了3 a b,如果这时通过buffer.get()来读取,读完后缓冲区就变成了a b,然后返回false;第二次又接收到了c 20,这时触发readFromBuffer时,缓冲区就变成了a b c 20,这时再通过buffer.get()来读取就会发生异常了。所以消息没接收完毕前,不要移动缓冲区的指针。(在这里我发现了一个似乎可以改进的地方,即如果readFromBuffer返回false,框架可以将buffer的指针重置为原值。具体影响有待考量

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值