先回顾下NIO中的"三剑客"模型:selector、channel、buffer

p.p1 { margin: 0; font: 13px "Helvetica Neue"; color: rgba(17, 142, 255, 1) }

对于网络通讯而言,代码最常处理的就是3件事:管理连接、读取数据、写入数据。上图中,selector就是用来管理连接的(通常只需要一个selector线程处理就行,可避免上下文切换),selector上注册了一堆channel(通道),channel是双向的(in/out),读写数据时,channel必须通过buffer(缓冲) 。selector通过Event事件轮询来选择(或者叫激活)某个channel。

这篇主要回顾buffer的用法,先看类图:

Buffer是一个抽象类,下面派生出了几种基本类型的子类,比如网络通讯中最常用的ByteBuffer,每个子类中真正用于存放数据的,是一个叫hb的数组。Buffer本身是可读可写的,为了方便标识读/写的位置,里面有3个非常重要的标识变量:

position :表示下1个读或写的位置(即:hb数组的下标索引)

limit:hb数组中有效数据的最大位置

capacity:hb数组的容量(注:capacity与limit的区别,比如1个数组的容量是10,但是里面只存放了6个有效数据,那么在读取时,capacity为10,而limit则为6)

jdk源码上,已经明确说明: position <= limit <= capacity。

经常容易忽视的方法:flip()

由于buffer是可读可写的,以IntBuffer为例,“写入时”每向buffer中放入1个int数字,position就向后移1位,所有数据写完后,position就指向最后1个数字的末尾了。“读取时”需要调用flip()方法把position重新移到位置0,才能读到数据。

package com.cnblogs.yjmyzz;

import java.nio.IntBuffer;

public class BufferTest {

    public static void main(String[] args) {
IntBuffer intBuffer = IntBuffer.allocate(6); System.out.println("after init => ");
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity() + "\n"); System.out.println("begin write => ");
for (int i = 0; i < 4; i++) {
intBuffer.put(i + 1);
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity());
} System.out.println("\n-------before flip----------");
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity());
intBuffer.flip();
System.out.println("\n-------after flip----------");
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity() + "\n"); System.out.println("begin read =>");
for (int i = 0; i < 4; i++) {
System.out.println(intBuffer.get());
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity());
} }
}

输出结果:

after init =>
pos:0,limit:6,cap:6 begin write =>
pos:1,limit:6,cap:6
pos:2,limit:6,cap:6
pos:3,limit:6,cap:6
pos:4,limit:6,cap:6 -------before flip----------
pos:4,limit:6,cap:6 -------after flip----------
pos:0,limit:4,cap:6 begin read =>
1
pos:1,limit:4,cap:6
2
pos:2,limit:4,cap:6
3
pos:3,limit:4,cap:6
4
pos:4,limit:4,cap:6

图解如下,这是刚初始化后:

写入4个元素后:

flip之后:

所有数据读取完成后:

接下来说一说mark(),这个方法必须与reset()结合才能看出效果,字面理解:mark就是在某个位置打个标志(做个记号),数据读取过程中,position会不断向前移,如果又想回到刚才做了记号的位置,调用reset即可。

IntBuffer intBuffer = IntBuffer.allocate(6);
for (int i = 0; i < 4; i++) {
intBuffer.put(i + 1);
}
intBuffer.flip(); System.out.println(intBuffer.get());
System.out.println(intBuffer.get());
System.out.println("-------after read 2 times----------");
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity() + "\n");
intBuffer.mark();
System.out.println("-------after read 3 times----------");
System.out.println(intBuffer.get());
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity() + "\n"); intBuffer.reset();
System.out.println("-------after reset----------");
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity() + "\n");

输出:

仔细观察上面的输出,在读取了2个数字后,position向前移动到了位置2,这时调用mark做了标记,然后再读取1个数,position继续向前移到3,然后再调用reset,position又退回到刚才的位置2。利用mark和reset,可以在缓冲区中,重复读取某一段数据。

ByteBuffer的数据类型问题

网络传输中ByteBuffer恐怕是用得最多的一种缓冲,特别要注意下“数据类型”问题。写入数据时,除了put方法外,ByteBuffer还提供了一系列的putXXX方法

类似的,读取数据时除了get之外,还有一系列的getXXX方法

比如在第位置1,调用了putChar('杨')写入数据,相应的读取位置1时,要调用getChar(),如果不匹配,可能会有异想不到的问题。

ByteBuffer buffer = ByteBuffer.allocate(5);
buffer.putShort((short) 1); // (1)
buffer.putChar('杨'); // (2) buffer.flip(); System.out.println(buffer.getShort()); //这里要与(1)处put的类型一致
System.out.println(buffer.getChar()); //这里要与(2)处put的类型一致

  

参考文档:

https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/Buffer.html

NIO复习(1):buffer的更多相关文章

  1. JAVA NIO复习笔记

    1. JAVA NIO是什么? 从JDK1.4开始,java提供了一系列改进的输入/输出处理的新功能,这些功能被统称为新IO(New IO,简称NIO),新增了许多用于处理输入/输出的类,这些类都被放 ...

  2. Java NIO Channel和Buffer

    Java NIO Channel和Buffer @author ixenos Channel和Buffer的关系 1.NIO速度的提高来自于所使用的结构更接近于OS执行I/O的方式:通道和缓冲器: 2 ...

  3. Java NIO中的Buffer 详解

    Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的.缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO ...

  4. NIO复习01

    NIO 概述: 1. Java NIO 由以下几个核心部分组成:Channels       Buffers           Selectors 2. 主要Channel的实现:FileChann ...

  5. NIO之缓冲区(Buffer)的数据存取

    缓冲区(Buffer) 一个用于特定基本数据类行的容器.有java.nio包定义的,所有缓冲区都是抽象类Buffer的子类. Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通道 ...

  6. NIO(一):Buffer缓冲区

    一.NIO与IO: IO:  一般泛指进行input/output操作(读写操作),Java IO其核心是字符流(inputstream/outputstream)和字节流(reader/writer ...

  7. nio之缓冲区(Buffer)理解

    一.缓冲区简介 Nio中的 Buffer 是用于存储特定基础类型的一个容器.为了能熟练的使用 Nio中的各种 Buffer , 我们需要理解 Buffer 中的 三个重要 的属性. 1. capaci ...

  8. Java I/O(3):NIO中的Buffer

    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来- 之前在调用Channel的代码中,使用了一个名叫ByteBuffer类,它是Buffer的子类.这个叫Buffer的类是专门用来解决高速设备与低 ...

  9. Java NIO之缓冲区Buffer

    Java NIO的核心部件: Buffer Channel Selector Buffer 是一个数组,但具有内部状态.如下4个索引: capacity:总容量 position:下一个要读取/写入的 ...

  10. NIO Channel和Buffer

    Java NIO 由以下几个核心部分组成: Buffer Channel Selector 传统的IO操作面向数据流,意味着每次从流中读一个或多个字节,直至完成,数据没有被缓存在任何地方.NIO操作面 ...

随机推荐

  1. 7.9K star!跨平台开发从未如此简单,这个开源框架让APP开发效率飙升!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 Lynx 是一个革命性的跨平台开发框架,使用 TypeScript 开发即可同时构建 iOS ...

  2. 漏洞预警 | WordPress Plugin Radio Player SSRF漏洞

    0x00 漏洞编号 CVE-2024-54385 0x01 危险等级 高危 0x02 漏洞概述 WordPress插件Radio Player是一种简单而有效的解决方案,用于将实时流媒体音频添加到您的 ...

  3. 【记录】VScode|两种缩放快捷键的功能和开启方式(Ctrl+/-,Ctrl滚轮)

    1 面板缩放 快捷键:Ctrl+'+'/'-'. 2 滚轮缩放字体 快捷键:Ctrl+滚轮 开启方式:如下图,打开设置,搜索zoom,勾选. 更多快捷键:Ctrl+K Ctrl+S打开快捷键设置(或左 ...

  4. 【经验】Office|重装后,PPT 2016后失去平滑等功能(解决方式:使用Office Tools Plus重新安装另一版本)

    重装ppt 2016后没有平滑等功能,并且在淡入/淡出中的平滑播放的还是淡入/淡出.本文记录了解决方案. 如何下载带平滑功能的Office? 首先,不能从官网微软服务和订阅下载.因为官网的必须购买才能 ...

  5. skip

    哇酷哇酷,和你的春天一样稍纵即逝的夏天 藏什么藏呢 自卑吗 你以为是缺点的 恰恰让我喜欢 但要短确实很短 说难是很难 而且烂 恰到好处吧 好男人也没的身手! 为了足以被好男人拯救 我在练习 结果是腿废 ...

  6. python 读写、创建文件

    python中对文件.文件夹(文件操作函数)的操作设计到os模块以及shutil模块 os模块提供了对目录或者文件的新建/删除/查看文件属性,还提供了对文件以及目录的路径操作,比如:绝对路径,父路径等 ...

  7. maven安装教程(亲测有用)

    先去https://maven.apache.org/download.cgi下第二个: 自己下不下来的,微信搜我公众号[勾玉技术]发送关键字[maven]获取百度云链接下载. 解压到任意文件夹,记得 ...

  8. EasyExcel读取多个sheet表数据,自定义监听器

    接口 /** * 导入 * * @param file * @return */ @PostMapping("/waitimport") public Result waitImp ...

  9. TypeScript中never类型的实用技巧

    本文由 ChatMoney团队出品 妙用一 当我们在一个项目中,可能会去改动一个在整个项目中应用很广泛的函数的参数类型,但是可能由于代码量比较庞大,我们不好排查改了之后哪些地方会出现问题,此时我们可以 ...

  10. 能提高你的创作效率的超强AI工具:ChatMoney

    本文由 ChatMoney团队出品 引言 在广告创意行业,创新和高效是赢得市场的关键.而我今天要分享的就是如何利用ChatMoney这款强大的人工智能工具,打破创新难题,赚取丰厚收益. 让我告诉你一个 ...