您的位置:首页 > 编程语言 > Java开发

ddpush 学习之路 5 Message.java

2015-03-18 01:16 369 查看
我们今天来学习ddpush的地四个类。

Message.java Message.java存放在org.ddpush.im.v1.client.appuser包中

今天弄的这个类让我比较费劲。好吧 其实是以前没怎么用过ByteBuffer 今天把 ByteBuffer的api从新看了一遍。其实没有看完。sun官网的ByteBuffer的api我只看了 ByteBuffer.wrap(byte[] data, int offset, int length);

说实话。我看完官网的api之后。我给理解错误了。我看完官网api之后我的理解是 ByteBuffer.wrap(byte[] data, int offset, int length); 会产生一个新的ByteBuffer 这个新的ByteBuffer的内容是data得offset开始 length长度的数据,应该是一个length这么长的一个ByteBuffer 但是我错了。再我给代码注释完成之后。我写简单测试代码的时候。发现。我怎么用。怎么改。这个方法。生成的ByteBuffer array() 之后。获得的结果的内容和data本身一模一样。这就让我陷入了一个误区。我一度认为eclipse出问题了。我重启eclipse 重启电脑。还是这样。无奈之下我网上手动搜索ByteBuffer.wrap(byte[] data, int offset, int length); 这个方法的解释。

然后才让我发现了。我理解错误了。 ByteBuffer.wrap(byte[] data, int offset, int length); 的作用并不是我想象的那样。而是 产生的ByteBuffer使用 data这块地方做数据缓存区。offset 是下次操作缓存的position length 是这个Buffer能操作的这个data的offset - length的长度 limit 。换句话说。length规定了ByteBuffer.wrap(byte[] data, int offset, int length);产生的ByteBuffer可以操作data的最大位置。

ByteBuffer.wrap(byte[] data, int offset, int length); 是让产生的ByteBuffer可以操作data 中 从 offset开始 到 length 这一段数据

陷入这个误区,让我浪费了几个小时都在想我写的简单测试代码的结果怎么和我想的不对。原来是我想的不对。执行结果是对的。。。 好吧。

由于上面的坑。让我感觉到了我可能还在坑里没有出来。所以如果大家看到了我理解错误的地方。欢迎您再下方留言指正。不胜感激。谢谢。

好了。接下来我们开始上Message 这个类我理解并添加的注释和代码。然后就是老规矩。这个类的简单调用测试和结果展示。OK 来看Message这个类和注释。

public final class Message {
//消息版本号
public static int version = 1;
//服务器消息最小长度
public static final int SERVER_MESSAGE_MIN_LENGTH = 5;
//客户端消息最小长度
public static final int CLIENT_MESSAGE_MIN_LENGTH = 21;
//消息类型
public static final int CMD_0x00 = 0;//心跳包
public static final int CMD_0x10 = 16;//通用信息
public static final int CMD_0x11 = 17;//分类信息
public static final int CMD_0x20 = 32;//自定义信息
//IP套接字地址
protected SocketAddress address;
//消息数据
protected byte[] data;
//使用一个 IP套接字地址SocketAddress 和一个 消息数据byte[] 来创建一个 Message对象
public Message(SocketAddress address, byte[] data){
this.address = address;
this.data = data;
}

/**
* 取出data 的 SERVER_MESSAGE_MIN_LENGTH - 2
* 到之后的2个byte 转换成ByteBuffer 然后获取这个新ByteBuffer的值
* 也就是说 Message的 data 中的 SERVER_MESSAGE_MIN_LENGTH - 2
* 这个位置到之后的2位 用来保存这个Message的Data的数据长度
* 就是 data的前5(SERVER_MESSAGE_MIN_LENGTH)个元素的最后两个元素
* 保存的是这个Message的数据长度
* */
public int getContentLength(){
return (int)ByteBuffer.wrap(data, SERVER_MESSAGE_MIN_LENGTH - 2, 2).getChar();
}

/**
* 取出data的第三个元素 转成 int 并返回
* 看样子是这里存储的是 Message 的类型
* 也就是  data 的 第三个元素 保存的时这个Message的类型
* */
public int getCmd(){
byte b = data[2];
// byte 转成 int  byte只有8位 直接转成int时
//int是32位 不够的java会自动补位。
//所以需要将byte转成int的高于byte原本的8位的全部清除
return b & 0xff;
}

//检测当前Message数据包的格式是否正确
public boolean checkFormat(){
//如果这个Message的address是null 或者data是null
//或者data的长度小于SERVER_MESSAGE_MIN_LENGTH 就返回false
if(address == null || data == null || data.length < Message.SERVER_MESSAGE_MIN_LENGTH){
return false;
}
//获取Message的类型
int cmd = getCmd();
//如果从data中得到的Message类型不是已经定义的几种类型,就返回false
if(cmd != CMD_0x00
&& cmd != CMD_0x10
&& cmd != CMD_0x11
&& cmd != CMD_0x20){
return false;
}
//获取data中 Message消息数据的长度
int dataLen = getContentLength();
//如果data的长度和从data中读取到得Message消息数据长度 + SERVER_MESSAGE_MIN_LENGTH(消息头)
//长度相等 就返回false
//所以 写入 data的Message消息数据长度 是纯消息数据的长度 不带消息包头的长度
if(data.length != dataLen + SERVER_MESSAGE_MIN_LENGTH){
return false;
}
//如果消息类型是 16 通用信息 并且Message消息数据长度 != 0 就返回false
//从这里看出 Message消息类型为 16 时 Message消息数据长度必须为 0
if(cmd ==  CMD_0x10 && dataLen != 0){
return false;
}
//如果消息类型是 17 分类消息 并且Message消息数据长度  != 8 就返回false
//从这里可以看出 Message消息类型为 17 时 Message消息数据长度必须为 8
if(cmd ==  CMD_0x11 && dataLen != 8){
return false;
}
// 如果消息类型是  32 自定义消息 并且Message消息数据长度 < 1 就返回false
//从这里可以看出 Message消息类型为 32 时 Message消息数据长度必须 >= 1 才行
//就是说 消息类型为 自定义消息时 必须有消息数据
if(cmd ==  CMD_0x20 && dataLen < 1){//must has content
return false;
}
//如果以上各种检测判断都正确了。就表明这个数据包格式正确 返回true
return true;
}
//修改Message的data 数据包
public void setData(byte[] data){
this.data = data;
}
//获取Message的data数据包
public byte[] getData(){
return this.data;
}
//设置 IP套接字地址
public void setSocketAddress(SocketAddress address){
this.address = address;
}
//获取 IP套接字地址
public SocketAddress getSocketAddress(){
return this.address;
}
//设置版本号 版本号必须 >= 1 && < 255
public static void setVersion(int v){
if(v < 1 || v > 255){
return;
}
version = v;
}
//获取版本号
public static int getVersion(){
return version;
}
}


好了。以上就是我理解的并添加注释的ddpush的Message.java这个类。 按照惯例。下面我就开始来上我们学习的这个类的简单调用测试代码 和 测试的结果。让我们更确定我们的理解。

//消息数据
String str = "TestData";
System.out.println("msgData:" + str);

int strLen = str.getBytes().length;  //长度是 8
System.out.println("msgDataLen:" + strLen);

//存放消息长度  两位
//这里 为什么吧数字放到第二位。首先由于是测试。内容长度小于byte的最大值128
//就不去管超过127 我们的数字只有8 按2位来写。应该是 08 而不是 80
//同时 我们读取的时候。也是从前向后读取的。所以需要吧数字写到第二个。
byte[] bb = new byte[2];
bb[0] = 0;
bb[1] = (byte)strLen;

//创建一个byte缓冲区
ByteBuffer buf = ByteBuffer.allocate(16);
buf.put((byte)0);
buf.put((byte)0);
buf.put((byte)17);  //信息类型  信息分类
buf.put(bb, 0, 2);  //两个byte  Message消息数据长度
buf.put(str.getBytes());    //消息内容

//创建一个Message消息对象
Message msg = new Message(null, buf.array());

//获取这个消息长度
int len = msg.getContentLength();
System.out.println("getMessageContentLength:" + len);

//获取消息类型
int cmd = msg.getCmd();
System.out.println("消息类型" + cmd);


结果为

msgData:TestData
msgDataLen:8
getMessageContentLength:8
消息类型:17


以上就是在我的理解之下的Message.java 这个类,我添加的注释。 这个类是一个消息数据包类。

消息版本、消息类型、消息长度、消息内容、地址 这基本上算是一个比较精简的消息数据包类

类里面提供了获取消息长度、消息内容、消息地址、以及修改这些信息的函数。 还有一个 检测这个数据包的格式是否合法。说白了其实是检测 传入的 data 这个消息数据包的格式是否合法。

对了。大家如果发现我理解的或者我上面的代码有问题的。欢迎留言告诉我。非常感谢!

好了。以上就是ddpush的第四个类 Message.java

算一算。26 - 4 = 22 我们还有22个类。就基本上把ddpush的基础流程思路学习了一遍。想想都兴奋。加油!你能行!

by brok1n 20150318
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ByteBuffer ddpush