手机端小强原创文章,java小强个人博客站点
当前位置: 首页 >> NET >> ByteBuffer 到底怎么用?网络编程中一点总结!

ByteBuffer 到底怎么用?网络编程中一点总结!

29500 NET | 2014-6-18

做tcp网络编程,要解析一批批的数据,可是数据是通过Socket连接的InputStream一次次读取的,读取到的不是需要转换的对象,而是要直接根据字节流和协议来生成自己的数据对象。
按照之前的编程思维,总是请求然后响应,当然Socket也是请求和响应,不过与单纯的请求响应是不同的。
这里Socket连接往往是要保持住的,也就是长连接,然后设置一个缓冲区,网络流不断的追加到缓冲区。然后后台去解析缓冲区的字节流。

如图所示,网络的流一直在传递,我们收到也许是完成的数据流,也可能是没有传递完的。这里就需要监视管道,不断读取管道中的流数据,然后向缓冲区追加。程序从头开始解析,如果目前缓冲区包含了数据,则解析,没有则放弃继续读取管道流。
就算管道中包含了数据,也不一定包含了完成的数据。例如,100个字节是一个数据体,可是目前缓冲区内包含了120个字节,这就是说缓冲区包含了一条数据,但是还有没有传递完的字节流。那么就要把前100个字节拿出来解析,然后从缓冲区清除这100个字节。那缓冲区就剩下20个字节了,这些数据可能在下次流中补充完成。
如何建立缓冲?

/**
 * 全局数据缓冲区 占用 1M 内存
 */
private static ByteBuffer bbuf = ByteBuffer.allocate(10240);
/**
 * 线程安全的取得缓冲变量
 */
public static synchronized ByteBuffer getByteBuffer() {
 return bbuf;
}

 

写一个Socket客户端,该客户端得到Socket连接,然后读取流,一直向缓冲中追加字节流,每次追加后调用一个方法来解析该流

public void run() {
 Socket socket = GlobalClientKeep.mvbSocket;
 if (null != socket) {
  try {
   // 获得连接引用
   OutputStream ops = socket.getOutputStream();
   InputStream ips = socket.getInputStream();
   while (true) {
    if (null != ops && null != ips) {
     // 接收返回信息
     byte[] bt = StreamTool.inputStreamToByte(ips);
     ByteBuffer bbuf = GlobalCommonObjectKeep.getByteBuffer();
     // 设置到缓冲区中
     bbuf.put(bt);
     // ////////////////////////////////////////////////////////////////////////
     // 拆包解析方法
     splitByte(ops);
     ops.flush();
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 } else {
  // 如果连接存在问题,则必须重新建立
  GlobalClientKeep.initMvbSocket();
 }
}

 

关于如何读取流,我有一篇博客专门讲解了所以这里是直接调用方法

byte[] bt = StreamTool.inputStreamToByte(ips);

 

那么解析方法是如何做的?
解析方法首先获得该缓冲中的所有可用字节,然后判断是否符合一条数据条件,符合就解析。如果符合两条数据条件,则递归调用自己。其中每次解析一条数据以后,要从缓冲区中清除已经读取的字节信息。

/**
 * @说明 拆包解析方法
 */
public static void splitByte(OutputStream ops) {
 try {
  ByteBuffer bbuf = GlobalCommonObjectKeep.getByteBuffer();
  int p = bbuf.position();
  int l = bbuf.limit();
  // 回绕缓冲区 一是将 curPointer 移到 0, 二是将 endPointer 移到有效数据结尾
  bbuf.flip();
  byte[] byten = new byte[bbuf.limit()]; // 可用的字节数量
  bbuf.get(byten, bbuf.position(), bbuf.limit()); // 得到目前为止缓冲区所有的数据
  // 进行基本检查,保证已经包含了一组数据
  if (checkByte(byten)) {
   byte[] len = new byte[4];
   // 数组源,数组源拷贝的开始位子,目标,目标填写的开始位子,拷贝的长度
   System.arraycopy(byten, 0, len, 0, 4);
   int length = StreamTool.bytesToInt(len); // 每个字节流的最开始肯定是定义本条数据的长度
   byte[] deco = new byte[length]; // deco 就是这条数据体
   System.arraycopy(byten, 0, deco, 0, length);
   // 判断消息类型,这个应该是从 deco 中解析了,但是下面具体的解析内容不再啰嗦
   int type = 0;
   // 判断类型分类操作
   if (type == 1) {
    
   } else if (type == 2) {
    
   } else if (type == 3) {
    
   } else {
    System.out.println("未知的消息类型,解析结束!");
    // 清空缓存
    bbuf.clear();
   }
   // 如果字节流是多余一组数据则递归
   if (byten.length > length) {
    byte[] temp = new byte[bbuf.limit() - length];
    // 数组源,数组源拷贝的开始位子,目标,目标填写的开始位子,拷贝的长度
    System.arraycopy(byten, length, temp, 0, bbuf.limit() - length);
    // 情况缓存
    bbuf.clear();
    // 重新定义缓存
    bbuf.put(temp);
    // 递归回调
    splitByte(ops);
   }else if(byten.length == length){ // 如果只有一条数据,则直接重置缓冲就可以了
    // 清空缓存
    bbuf.clear();
   }
  } else {
   // 如果没有符合格式包含数据,则还原缓冲变量属性
   bbuf.position(p);
   bbuf.limit(l);
  }
 } catch (Exception e) {
  e.printStackTrace();
 }
}

 

代码只是一个参考,主要讲解如何分解缓冲区,和取得缓冲区的一条数据,然后清除该数据原来站的空间。
至于缓冲区的属性,如何得到缓冲区的数据,为什么要清空,bbuf.flip();是什么意思。下面来说一下关于ByteBuffer 的一下事情。
ByteBuffer中有几个属性,其中有两个很重要。limit和position。position开始在0,填充数据后等于数据的长度,而limit是整个缓冲可用的长度。bbuf.flip();之后,position直接变为0,而limit直接等于position。JDK源码如下:

/**
 * Flips this buffer.  The limit is set to the current position and then
 * the position is set to zero.  If the mark is defined then it is
 * discarded.
 *
 * <p> After a sequence of channel-read or <i>put</i> operations, invoke
 * this method to prepare for a sequence of channel-write or relative
 * <i>get</i> operations.  For example:
 *
 * <blockquote><pre>
 * buf.put(magic);    // Prepend header
 * in.read(buf);      // Read data into rest of buffer
 * buf.flip();        // Flip buffer
 * out.write(buf);    // Write header + data to channel</pre></blockquote>
 *
 * <p> This method is often used in conjunction with the {@link
 * java.nio.ByteBuffer#compact compact} method when transferring data from
 * one place to another.  </p>
 *
 * @return  This buffer
 */
public final Buffer flip() {
 limit = position;
 position = 0;
 mark = -1;
 return this;
}

 

这样,在position和limit之间的数据就是我们要的可用数据。
但是position和limit是ByteBuffer在put和get时需要的属性,所以在使用后要么还原,要么像上面代码一样,清除一些字节信息然后重置。
ByteBuffer 的get和put不是我们平常的取值和设值一样,他会操纵一些属性变化。

 

推荐您阅读更多有关于“ InputStream tcp 网络编程 ByteBuffer ”的文章

上一篇:java InputStream读取数据问题 下一篇:InputStream,int,shot,long与byte数组之间的互相转换

猜你喜欢

发表评论:

个人资料
blogger

java小强
没有思考,人生的路会越走越难!

搜索
分类
最新微语
  • 8月1日,我已离开奋斗多年的北京。不知道是暂时的离开,还是永久的离别,反正已经离职在家,告别每日上班,每天苦累的煎熬,过一段属于自己的生活。以前是专职工作,现在专职生活。

    2017-08-18 12:47

  • 又弄完一个项目,累成狗,但是感觉又进步不少,除了很多坑已经踩过,做起来也是轻车熟路。同时也认识到,程序不在于你多牛逼,而是在乎你的细节把控度,而细节的关注,是一个优秀程序员必须要注意的。另外,要相信自己,勇敢向前,没人生下来就是成功的,而且,成功的路,比成功本身更重要。

    2017-06-30 09:46

  • 今日北京再次沙尘暴来袭,吃了几年细粮,终于能来口粗粮了,不过大早上看见这场景,还是吓我一跳,不过随后就平静了,毕竟是老朋友了。进公司又发现一股烤糊的味道,真是祸不单行啊,例外都是污染。发了两个口罩,开启保护模式。

    2017-05-04 10:16

  • 今天同学问我,最近还在写代码吗?我想了想,这个问题怎么回答呢,我好像确实很长时间,虽然写了一些,但是主要内容已经不是写代码了。然后再想想,自己也7年多了,这么多年了,我收获了什么,我的目标到底是什么。眼看就奔三了,人生啊,开启感叹模式。

    2017-03-30 22:52

  • 也许大家都已经注意到了,今年的房价,好多地方都是翻了一番,跟着就是,各地房东开始变相涨租。今年之所以搬走,就是为此,这两天同学也是如此。很多房东只认钱,别谈感情,伤钱。而对于这个城市来说,你怎么定位自己,你真把自己当成她的一份子?你来此为何?将来何去何从?自己掂量清楚。

    2016-12-05 10:03

  • 更多»

最新文章
热门文章
随机文章