Java教程之NIO的基本用法

article/2025/9/7 17:00:01

NIO的基本用法

NIO是New I/O的简称,与旧式基于流的I/O相对,从名字上来看,它表示新的一套I/O标准。它是从JDK1.4中被纳入到JDK中的。

与旧式的IO流相比,NIO是基于Block的,它以块为单位来处理数据,最为重要的两个组件是缓冲区Buffer和通道Channel。缓冲区是一块连续的内存块,是NIO读写数据的载体;通道表示缓冲数据的源头和目的地,它用于向缓冲区读取或者写入数据,是访问缓冲区的接口。

在这里插入图片描述

Buffer的基本原理

Buffer中最重要的3个参数:位置(position)、容量(capacity)、上限(limit)。他们3者的含义如下

位置(position): 表示当前缓冲区的位置,从position位置之后开始读写数据。
容量(capacity): 表示缓冲区的最大容量
上限(limit):		表示缓冲区的实际上限,它总是小于或等于容量

在这里插入图片描述

缓冲区的容量(capacity)是不变的,而位置(position)和上限(limit)和以根据实际需要而变化。也就是说,可以通过改变当前位置和上限来操作缓冲区内任意位置的数据。

Buffer的常用方法

NIO提供一系列方法来操作Buffer的位置(position)和上限(limit),以及向缓冲区读写数据。

put() //向缓冲区position位置添加数据。并且position往后移动,不能超过limit上限。
get() //读取当前position位置的数据。并且position往后移动,不能超过limit上限。
flip() //将limit置位为当前position位置,再讲position设置为0
rewind() //仅将当前position位置设置为0
remaining //获取缓冲区中当前position位置和limit上限之间的元素数(有效的元素数)
hasRemaining() //判断当前缓冲区是否存在有效的元素数
mark() //在当前position位置打一个标记
reset() //将当前position位置恢复到mark标记的位置。
duplicate() //复制缓冲区

创建缓冲区

//创建一个容量为10的缓冲区
ByteBuffer byteBuffer1=ByteBuffer.allocate(10);//创建一个包裹数据的缓冲区
ByteBuffer byteBuffer2 = ByteBuffer.wrap("abcde".getBytes());

获取/设置缓冲区参数

//创建一个容量为10的缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(10);
System.out.println("位置:"+byteBuffer.position());  //0
System.out.println("上限:"+byteBuffer.limit()); //10
System.out.println("容量:"+byteBuffer.capacity());//10

在这里插入图片描述

添加数据到缓冲区

//创建一个容量为10的缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(10);
//添加数据到缓冲区
byteBuffer.put("abcde".getBytes());
System.out.println("position位置:"+byteBuffer.position()); //5
System.out.println("limit上限:"+byteBuffer.limit()); //10
System.out.println("capacity容量:"+byteBuffer.capacity()); //10

在这里插入图片描述

rewind重置缓冲区

rewind函数将position置为0位置,并清除标记。

//创建一个容量为10的缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(10);
//添加数据到缓冲区
byteBuffer.put("abcde".getBytes());System.out.println("position位置:"+byteBuffer.position()); //5
System.out.println("limit上限:"+byteBuffer.limit()); //10
System.out.println("capacity容量:"+byteBuffer.capacity()); //10System.out.println("------------");//重置缓冲区
byteBuffer.rewind();
System.out.println("position位置:"+byteBuffer.position()); //0
System.out.println("limit上限:"+byteBuffer.limit()); //10
System.out.println("capacity容量:"+byteBuffer.capacity());//10

在这里插入图片描述

flip重置缓冲区

flip函数现将limit设置为position位置,再将position置为0位置,并清除mar标记。

//创建一个容量为10的缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(10);
//添加数据到缓冲区
byteBuffer.put("abcde".getBytes());System.out.println("position位置:"+byteBuffer.position()); //5
System.out.println("limit上限:"+byteBuffer.limit()); //10
System.out.println("capacity容量:"+byteBuffer.capacity()); //10System.out.println("------------");//重置缓冲区
byteBuffer.flip();
System.out.println("position位置:"+byteBuffer.position()); //0
System.out.println("limit上限:"+byteBuffer.limit()); //5
System.out.println("capacity容量:"+byteBuffer.capacity());//10

在这里插入图片描述

clear清空缓冲区

clear方法也将position置为0,同时将limit置为capacity的大小,并清除mark标记。

//创建一个容量为10的缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(10);
//设置上限为5
byteBuffer.limit(5);
//添加数据到缓冲区
byteBuffer.put("abcde".getBytes());System.out.println("position位置:"+byteBuffer.position()); //5
System.out.println("limit上限:"+byteBuffer.limit()); //5
System.out.println("capacity容量:"+byteBuffer.capacity()); //10System.out.println("------------");//重置缓冲区
byteBuffer.clear();
System.out.println("position位置:"+byteBuffer.position()); //0
System.out.println("limit上限:"+byteBuffer.limit()); //10
System.out.println("capacity容量:"+byteBuffer.capacity());//10

在这里插入图片描述

标记和恢复

ByteBuffer buffer = ByteBuffer.allocate(10);
//添加数据到缓冲区
buffer.put("abcde".getBytes());
//打一个标记
buffer.mark();
System.out.println("标记位置:"+buffer.position()); //5
//再添加5个字节数据到缓冲区
buffer.put("fijkl".getBytes());
System.out.println("当前位置:"+buffer.position()); //10
//将position恢复到mark标记位置
buffer.reset();
System.out.println("恢复标记位置:"+buffer.position());//5

FileChannel通道

FileChannel是用于操作文件的通道,可以用于读取文件、也可以写入文件

//创建读取文件通道
FileChannel fisChannel = new FileInputStream("day05/src/a.txt").getChannel();
//创建写入文件的通道
FileChannel fosChannel = new FileOutputStream("day05/src/b.txt").getChannel();
//创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(2);
while (fisChannel.read(buffer)!=-1){System.out.println("position:"+buffer.position()); //0System.out.println("limit:"+buffer.limit());//2//重置缓冲区(为输出buffer数据做准备)buffer.flip();fosChannel.write(buffer);//重置缓冲区(为输入buffer数据做准备)buffer.clear();
}
//关闭通道
fisChannel.close();
fosChannel.close();

在这里插入图片描述

SocketChannel通道

下面代码使用SocketChannel通道上传文件到服务器

public class Client {public static void main(String[] args) throws IOException {//创建通道SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 10000));//创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//读取本地文件数据到缓冲区FileChannel fisChannel = new FileInputStream("day05/src/a.txt").getChannel();while (fisChannel.read(buffer)!=-1){buffer.flip(); //为写入数据做准备socketChannel.write(buffer);buffer.clear(); //为读取数据做准备}//关闭本地通道fisChannel.close();//socketChannel.shutdownOutput();//读取服务端回写的数据buffer.clear();int len = socketChannel.read(buffer);System.out.println(new String(buffer.array(), 0, len));//关闭socket通道socketChannel.close();}
}

ServerSocketChannel通道

下面代码使用ServerSocketChannel通道接收文件并保存到服务器

public class Server {public static void main(String[] args) throws IOException {//1.创建ServerSocketChannel通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//2.绑定端口号serverSocketChannel.bind(new InetSocketAddress(10000));//3.设置非阻塞serverSocketChannel.configureBlocking(false);System.out.println("服务器已开启");while (true) {//4.获取客户端通道,如果有客户端连接返回客户端通道,否则返回nullSocketChannel socketChannel = serverSocketChannel.accept();if(socketChannel!=null){socketChannel.configureBlocking(false);//创建本地通道,用于往文件中写数据UUID uuid = UUID.randomUUID();FileChannel fosChannel=new FileOutputStream("day05/src/"+uuid+".txt").getChannel();ByteBuffer buffer=ByteBuffer.allocate(1024);while (socketChannel.read(buffer)>0){buffer.flip(); //准备把缓冲区数据输出fosChannel.write(buffer);buffer.clear();//准备读取数据到缓冲区}fosChannel.close();//回写数据到客户端ByteBuffer resultBuffer=ByteBuffer.wrap("上传文件成功".getBytes());socketChannel.write(resultBuffer);//关闭客户端通道socketChannel.close();}}}
}

NIO Selector选择器

Selector 一般称 为选择器 ,当然你也可以翻译为 多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。

在这里插入图片描述

使用Selector的服务器模板代码

有了模板代码我们在编写程序时,大多数时间都是在模板代码中添加相应的业务代码

ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress("localhost", 8080));
ssc.configureBlocking(false);Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);while(true) {int readyNum = selector.select();if (readyNum == 0) {continue;}Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> it = selectedKeys.iterator();while(it.hasNext()) {SelectionKey key = it.next();if(key.isAcceptable()) {// 接受连接} else if (key.isReadable()) {// 通道可读} else if (key.isWritable()) {// 通道可写}it.remove();}
}

NIO Selector服务端

按照上面的模板代码,改写接收文件的服务端。

public class Server {public static void main(String[] args) {try {ServerSocketChannel ssc = ServerSocketChannel.open();ssc.socket().bind(new InetSocketAddress("localhost", 10000));ssc.configureBlocking(false);Selector selector = Selector.open();ssc.register(selector, SelectionKey.OP_ACCEPT);FileChannel fosChannel=null;while (true) {int readyNum = selector.select();System.out.println(readyNum);if (readyNum == 0) {continue;}Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> it = selectedKeys.iterator();while (it.hasNext()) {SelectionKey key = it.next();SocketChannel socketChannel1=null;SocketChannel socketChannel2=null;if (key.isAcceptable()) {System.out.println("isAcceptable");// 创建新的连接,并且把连接注册到selector上,而且,// 声明这个channel只对读操作感兴趣。socketChannel1 = ssc.accept();socketChannel1.configureBlocking(false);socketChannel1.register(selector, SelectionKey.OP_READ);UUID uuid = UUID.randomUUID();fosChannel=new FileOutputStream("day05/src/"+uuid+".txt").getChannel();} else if (key.isReadable()) {System.out.println("isReadable");// 通道可读socketChannel2 = (SocketChannel) key.channel();//创建本地通道,用于往文件中写数据ByteBuffer readBuff=ByteBuffer.allocate(1024);while (socketChannel2.read(readBuff)>0){readBuff.flip(); //准备把缓冲区数据输出fosChannel.write(readBuff);readBuff.clear();//准备读取数据到缓冲区}fosChannel.close();key.interestOps(SelectionKey.OP_WRITE);} else if (key.isWritable()) {System.out.println("isWritable");// 通道可写ByteBuffer writeBuff=ByteBuffer.allocate(1024);writeBuff.put("上传成功".getBytes());writeBuff.flip();SocketChannel socketChannel = (SocketChannel) key.channel();socketChannel.write(writeBuff);key.interestOps(SelectionKey.OP_READ);}it.remove();}}} catch (Exception e) {//e.printStackTrace();} finally {}}
}

http://chatgpt.dhexx.cn/article/vtFMUEQu.shtml

相关文章

关于vp8,vp8与264比较总结

1 Other Codecs l MSN 使用的video codec “x-rtvc1”,09之前的版本使用的ML20.参考网址&#xff1a; http://www.amsn-project.net/forums/index.php?topic6612.0 l Yahoo messenger 使用GIPS的LSVX codec. l 这两个codecs技术保密性强&#xff0c;找不到有用的信息&#xff…

PCM(脉冲编码调制)、iLBC编解码、opus(声音编码格式)、VP8视频压缩格式、H.264数字视频压缩格式

目录 PCM&#xff08;脉冲编码调制&#xff09; 发展史 工作原理 iLBC编解码 基本介绍 技术优势 Opus&#xff08;声音编码格式&#xff09; 特性 播放 技术细节 VP8视频压缩格式 简介 突破创新 技术分析 H.264数字视频压缩格式 背景介绍 优势 特点 PCM&…

JavaCV音视频开发宝典:录制vp8和vp9编码的webm格式视频,以mp4转webm为例

《JavaCV音视频开发宝典》专栏目录导航 《JavaCV音视频开发宝典》专栏介绍和目录 ​ 前言 由于现代浏览器对webm格式的视频支持较好,如下图: 因此使用webm格式来作为主要的存储和回放视频格式。本章将使用mp4文件转webm为例,来讲一下JavaCV如何录制webm格式视频。 webm…

有关 VP8 的一些帧 Golden AltRef 的说明

---------------------------------------------------------------------------------------------------------------------- 一分钟快速搭建 rtmpd 服务器: https://blog.csdn.net/freeabc/article/details/102880984 软件下载地址: http://www.qiyicc.com/download/rtmpd…

主流编解码器(H.264 AVC, H.265 HEVC, VP8, VP9)比较

主流编解码器&#xff08;H.264 AVC, H.265 HEVC, VP8, VP9&#xff09;比较 本文转自&#xff1a;http://houh-1984.blog.163.com/blog/static/31127834201321995354105/ 概述 H.264(MPEG 4, class 10 )是目前嵌入式和移动设备中采用最多的视频编解码算法标准。目前超过50家…

即时通讯音视频开发(十七):视频编码H.264、VP8的前世今生

前言 目前从开发者的角度来说&#xff0c;音视频编码选H.264还是VP8几乎没有悬念&#xff08;个人认为这当然是H.264了&#xff09;。本文重在为读者从技术角度讲解H.264和VP8的发展渊源以及现时所面临的问题&#xff0c;相信读完此文后&#xff0c;对于即时通讯&#xff08;IM…

WebRTC 视频编解码类型的选择 VP8 H264 还是其他?(openh264编码,ffmpeg解码)

在你的WebRTC应用中,选择正确的视频编解码器很重要,但是如何选择又是一个棘手的问题。 WebRTC 视频编解码器 – 简要回顾 WebRTC 曾经很容易。你有 VP8、Opus 和 G.711。 G.711 被删除是因为我不想让你使用它。真的没有理由这样做。 后来,H.264 被添加为强制实现视频编解码器…

视音频编解码H264,265,MPEG-4,VP8,VP9知识总结

首先澄清几个基础知识&#xff1a; 一&#xff1a;封装格式&#xff1a; 我们常见的音视频文件格式例如&#xff1a;mp4 &#xff0c;flv,rmvb,avi等称为封装格式。封装格式里面封装了各种编码器编码的视频源信息的宽高比&#xff0c;视频轨&#xff0c;音频轨。例如视频源为…

MSVC2017 编译WebRTC Release VP8编码崩溃的问题

问题描述&#xff1a; 使用msvs2017编译webrtc。release版本使用VP8会出现奔溃&#xff0c;H264无问题。现象如下&#xff1a; 编译选项&#xff1a; "--argstarget_cpu\"x86\" is_debugfalse use_rttitrue is_clang false " 原因&#xff1a; MSVC编…

音视频基础1:H264、H265、MPEG-4、VP8、VP9编码基础知识

这里写自定义目录标题 个人认知&#xff0c;程序员职业发展出路编码器发展史编码原理H264H265 个人认知&#xff0c;程序员职业发展出路 随着5G时代的到来&#xff0c;音视频成功走上风口&#xff0c;程序员如何发展&#xff0c;其实不管是入门级选手还是30岁&#xff0c;35岁…

音视频基础:H264、H265、MPEG-4、VP8、VP9编码基础知识

编码器发展史 Android中创建编码器 MediaCodec.createEncoderByType("video/av"); //创建H264编码器 MediaCodec.createEncoderByType("video/hevc"); //创建H265编码器为什么会有这么多种编码器&#xff1f;看看他们的发展史的。 ITU-T这个组织是专门…

webrtc代码走读九(vp8 rtp 报文解析)

一、wireshark解析VP8报文方法 首先webrtc里面默认开启了FEC和SRTP功能&#xff0c;导致wireshark无法正常解析VP8的报文。所以若想了解VP8的RTP报文格式&#xff0c;还需要先关闭FEC、SRTP。 1、关闭FEC。 internalencoderfactory.cc文件屏蔽kRedCodecName、kUlpfecCodecNa…

vp8 的下载

1. vp8 的下载 页面地址 https://github.com/webmproject/libvpx git 下载 git clone https://github.com/webmproject/libvpx.git2. 在 android 上面搭建的 vp8 环境 页面地址 https://github.com/cmeng-git/vpx-android git 下载 git clone https://github.com/cmeng-gi…

VP8视频格式初探

作者&#xff1a; 阮一峰日期&#xff1a; 2010年5月20日 昨天&#xff0c;Google发布了一个开源项目WebM。 这个项目的目的&#xff0c;是在文件格式方面&#xff0c;为制作和发布互联网视频提供了一个开源的解决方案。 WebM采用MKV作为封装格式&#xff0c;里面的音频编码用V…

深入了解 VP8

部分翻译&#xff1a;http://x264dev.multimedia.cx/?p377 译者&#xff1a;delectate 问题一&#xff1a;vp8到底怎么样&#xff1f; 难道他真的比x264拥有更高的压缩比率&#xff0c;是个优秀的编码器吗&#xff1f;他真的比h264优秀吗&#xff1f;似乎On2自己都羞于承认……

VP8的前途与使命

文 / 金尹 VP8是视频压缩解决方案厂商On2推出的视频压缩格式。今年年初&#xff0c;Google完成了对On2的收购&#xff0c;随即开放了VP8视频编码技术源代码并免费提供给开发者使用。但业界对此一直褒贬不一&#xff0c;本文作者从多个角度进行阐述&#xff0c;对VP8的前途与发展…

如何使用Win10剪切板

几乎所有接触电脑的人都会使用CtrlC和CtrlV&#xff0c;但是每次CtrlC只能复制一次&#xff0c;并且会覆盖之前已经复制的内容。如果需要多次复制不同内容&#xff0c;那就操作多次。如果需要切换页面进行复制&#xff0c;特别不方便。其实&#xff0c;Win10有内置剪切板&#…

windows剪切板的历史记录

windows剪切板的历史记录 最近遇到一件比较坑的事情。当然可能也是我本人粗心大意了吧。但是这种事情难免要发生。比如说你要移动一个比较重要的东西&#xff0c;然后按了ctrlx&#xff0c;但是之间因为别的事情耽搁了一下&#xff0c;而自己的重要的东西还放在剪切板里面&…

win10如何查看剪切板内容?

cv大法我们经常用&#xff0c;但如果复制了新的内容但又想粘贴使用上次复制的内容怎么办&#xff1f; 其实复制新的内容并没有覆盖掉之前复制的内容&#xff0c;可以使用CtrlV快捷键打开剪切板&#xff0c;在剪切板里可以找到以前复制过的内容。 注意&#xff1a;这里的剪切板…

windows 剪贴板监控

迅雷下载或者旋风下载都有一个很有意思的剪贴板监控功能&#xff0c;当你打开剪贴板监控时&#xff0c;如果你复制了一个下载的URL&#xff0c;这两个程序都会弹出来下载框来让你去下载&#xff0c;显得很智能&#xff0c;昨天发现灵格斯词霸也有个剪贴板取词功能。 上个月写了…