Java ByteBuffer网络编程用法实例解析

 更新时间:2020年10月21日 10:22:51   作者:cuisuqiang  
这篇文章主要介绍了Java ByteBuffer网络编程用法实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

做tcp网络编程,要解析一批批的数据,可是数据是通过Socket连接的InputStream一次次读取的,读取到的不是需要转换的对象,而是要直接根据字节流和协议来生成自己的数据对象。

按照之前的编程思维,总是请求然后响应,当然Socket也是请求和响应,不过与单纯的请求响应是不同的。

这里Socket连接往往是要保持住的,也就是长连接,然后设置一个缓冲区,网络流不断的追加到缓冲区。然后后台去解析缓冲区的字节流。

如图所示,网络的流一直在传递,我们收到也许是完成的数据流,也可能是没有传递完的。这里就需要监视管道,不断读取管道中的流数据,然后向缓冲区追加。程序从头开始解析,如果目前缓冲区包含了数据,则解析,没有则放弃继续读取管道流。

就算管道中包含了数据,也不一定包含了完成的数据。例如,100个字节是一个数据体,可是目前缓冲区内包含了120个字节,这就是说缓冲区包含了一条数据,但是还有没有传递完的字节流。那么就要把前100个字节拿出来解析,然后从缓冲区清除这100个字节。那缓冲区就剩下20个字节了,这些数据可能在下次流中补充完成。

如何建立缓冲?

/** 
 * 全局MVB数据缓冲区 占用 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 { 
      // 获得mvb连接引用 
      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不是我们平常的取值和设值一样,他会操纵一些属性变化。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 基于Java实现动态切换ubuntu壁纸功能

    基于Java实现动态切换ubuntu壁纸功能

    这篇文章主要为大家详细介绍了如何使用 Java 在 Ubuntu Linux 系统中实现自动切换壁纸的示例程序,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-11-11
  • 使用 Java 开发 Gradle 插件的步骤

    使用 Java 开发 Gradle 插件的步骤

    这篇文章主要介绍了使用 Java 开发 Gradle 插件的步骤,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • Java数据结构之链表详解

    Java数据结构之链表详解

    本篇文章我们将讲解一种新型的数据结构—链表,链表是一种使用广泛的通用数据结构,它可以用来作为实现栈,队列等数据结构的基础.文中有非常详细的介绍,需要的朋友可以参考下
    2021-05-05
  • Java并发之synchronized实现原理深入理解

    Java并发之synchronized实现原理深入理解

    这篇文章主要介绍了Java中synchronized实现原理详解,涉及synchronized实现同步的基础,Java对象头,Monitor,Mark Word,锁优化,自旋锁等相关内容,具有一定借鉴价值,需要的朋友可以参考下
    2021-08-08
  • 一篇文章带你了解spring事务失效的多种场景

    一篇文章带你了解spring事务失效的多种场景

    在日常编码过程中常常涉及到事务,在前两天看到一篇文章提到了Spring事务,那么在此总结下在Spring环境下事务失效的几种原因.
    2021-09-09
  • 公共POI导出Excel方法详解

    公共POI导出Excel方法详解

    这篇文章主要介绍了公共POI导出Excel方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • java实现根据ip地址获取地理位置的代码分享

    java实现根据ip地址获取地理位置的代码分享

    这篇文章主要介绍了java实现根据ip地址获取地理位置的代码分享,本文中使用的是QQ在线接口,也可以使用新浪、淘宝等提供的在线接口,需要的朋友可以参考下
    2014-08-08
  • java线程并发semaphore类示例

    java线程并发semaphore类示例

    Java 5.0里新加了4个协调线程间进程的同步装置,它们分别是Semaphore, CountDownLatch, CyclicBarrier和Exchanger,本例主要介绍Semaphore,Semaphore是用来管理一个资源池的工具,可以看成是个通行证
    2014-01-01
  • SpringMVC超详细介绍自定义拦截器

    SpringMVC超详细介绍自定义拦截器

    Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。本文将代码演示和文字描述详解拦截器的原理与使用
    2022-06-06
  • 深入理解java三种工厂模式

    深入理解java三种工厂模式

    下面小编就为大家带来一篇深入理解java三种工厂模式。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06

最新评论