当前位置: 首页 > news >正文

NIO详解


前期处理

对应的思维导图地址: https://www.processon.com/view/link/62247e810e3e74108ca1b5d7

对应的 Github地址: https://github.com/yuejianli/NIO

依赖

全局 pom.xml 依赖

<dependencies><dependency\><groupId\>junit</groupId\><artifactId\>junit</artifactId\><version\>4.13</version\></dependency\><dependency\><groupId\>log4j</groupId\><artifactId\>log4j</artifactId\><version\>1.2.17</version\></dependency\><dependency\><groupId\>org.slf4j</groupId\><artifactId\>slf4j-log4j12</artifactId\><version\>1.7.32</version\></dependency\><!--添加 lombok 依赖,用于日志打印--><dependency\><groupId\>org.projectlombok</groupId\><artifactId\>lombok</artifactId\><version\>1.18.22</version\></dependency\><!--用于统计时间--><dependency\><groupId\>cn.hutool</groupId\><artifactId\>hutool-all</artifactId\><version\>5.7.20</version\></dependency\></dependencies\>

日志配置

log4j.properties

# priority  :debug<info<warn<error#you cannot specify every priority with different file for log4jlog4j.rootLogger\=debug,stdout,info,debug,warn,error #consolelog4j.appender.stdout\=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout\=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern\= \[%d{yyyy-MM-dd HH:mm:ss a}\]:%p %l%m%n#info loglog4j.logger.info\=infolog4j.appender.info\=org.apache.log4j.DailyRollingFileAppender log4j.appender.info.DatePattern\='\_'yyyy-MM-dd'.log'log4j.appender.info.File\=./src/com/hp/log/info.loglog4j.appender.info.Append\=truelog4j.appender.info.Threshold\=INFOlog4j.appender.info.layout\=org.apache.log4j.PatternLayout log4j.appender.info.layout.ConversionPattern\=%d{yyyy-MM-dd HH:mm:ss a} \[Thread: %t\]\[ Class:%c >> Method: %l \]%n%p:%m%n

项目代码

一. FileChannel

读取文件

 //1. 使用 RandomAccessFile 创建 FileChannel//2. 创建缓冲区 ByteBuffer//3.将数据读到Buffer 里面//3.1 循环读取,然后反转,写入,清空。/\*\*\* 读文件测试\*\* @date 2022/3/24 9:23\* @author zk\_yjl\* 读文件测试\*/@Testpublic void readFileTest() throws IOException {RandomAccessFile randomAccessFile \=new RandomAccessFile("F:\\\\Java\\\\NIO\\\\Code\\\\NIO\\\\read.txt", "rw");//创建通道FileChannel fileChannel \= randomAccessFile.getChannel();//创建缓冲区 通过 allocate 指定大小  一次性读取 1024个字节,不是一个个读// ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// allocate(4) 时会多读写几回。ByteBuffer byteBuffer \= ByteBuffer.allocate(4);//读取到byteBuffer 缓冲区里面  如果读取不到信息,返回 -1int readCount \= fileChannel.read(byteBuffer);while (readCount \> \-1) {//信息log.info(">>读取字节数" + readCount);//调用 flip 进行写入byteBuffer.flip();while (byteBuffer.hasRemaining()) {//读取, 中文会乱码.log.info(">>>写出数据:" + (char) byteBuffer.get());}byteBuffer.clear();//清空之后,继续读取, 第二回是 -1 ,读取不到数据。readCount \= fileChannel.read(byteBuffer);}fileChannel.close();randomAccessFile.close();log.info(">>>读出数据成功");}

写入数据到文件

/*** 写入数据*/@Testpublic void writeTest() throws IOException {RandomAccessFile randomAccessFile =new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\write.txt", "rw");//1. 获取通道FileChannel fileChannel = randomAccessFile.getChannel();//2. 定义数据,并写入到 ByteBuffer 里面ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//放置信息byteBuffer.clear();byteBuffer.put("Hello NIO".getBytes());//进行读取转换byteBuffer.flip();log.info(">>>开始写入数据");//将数据写入到 Channel 里面while (byteBuffer.hasRemaining()) {//写入到 channel 里面fileChannel.write(byteBuffer);}log.info(">>>写入数据完成");fileChannel.close();}

写入大量数据

/*** 写入大量数据*/@Testpublic void writeMoreTest() throws IOException {RandomAccessFile randomAccessFile =new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\write.txt", "rw");//1. 获取通道FileChannel fileChannel = randomAccessFile.getChannel();//2. 定义数据,并写入到 ByteBuffer 里面ByteBuffer byteBuffer = ByteBuffer.allocate(4);byte[] bytes = "Hello NIO !!!".getBytes();log.info(">>>开始写入数据");for (int i = 0; i < bytes.length; i = i + 4) {//放置信息byteBuffer.clear();byteBuffer.put(ArrayUtil.sub(bytes, i, Math.min(i + 4,bytes.length)));//进行读取转换byteBuffer.flip();//将数据写入到 Channel 里面while (byteBuffer.hasRemaining()) {//写入到 channel 里面fileChannel.write(byteBuffer);}log.info(">>>写入一次");}log.info(">>>写入数据完成");fileChannel.close();}

其它方法操作

/*** position()   返回当前的位置* position(index) 设置当前的位置。* 如果超过文件 结束符, 读取时,会返回  -1* 写入时,会从 index 处开始写入,将文件增大。会造成 "文件空洞" 现象* <p>* size () 获取关联的文件 的大小, 与position() 位置无关。 truncate() 截取,会导致 size() 发生改变。* <p>* truncate(index) 截取, 会将源文件进行截取, size() 会改变。 截取后再读取 position** @throws IOException 异常*/@Testpublic void otherTest() throws IOException {//读取RandomAccessFile randomAccessFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\read.txt", "rw");FileChannel fileChannel = randomAccessFile.getChannel();//获取对应的位置信息log.info(">>>获取文件的大小:" + fileChannel.size());log.info(">>获取位置:" + fileChannel.position());//设置位置, 即前四个不读fileChannel.position(fileChannel.position() + 4);
​log.info(">>>获取文件的大小:" + fileChannel.size());//截取,只要前四个。 后面的不要。fileChannel.truncate(8);log.info(">>>截取文件内容,只要前八个");log.info(">>>获取文件的大小:" + fileChannel.size());ByteBuffer byteBuffer = ByteBuffer.allocate(128);int readCount = fileChannel.read(byteBuffer);while (readCount > -1) {log.info(">>>读取信息:" + readCount);byteBuffer.flip();while (byteBuffer.hasRemaining()) {log.info(">>获取读取的信息:" + (char) byteBuffer.get());}byteBuffer.clear();readCount = fileChannel.read(byteBuffer);}//进行关闭fileChannel.close();randomAccessFile.close();log.info(">>>读取文件内容结束");}

文件复制1

/*** 文件复制, target.transferFrom(sourceChannel,position,count)** @throws IOException count 时,数目为 当前 channel 的最大内容数目。*                     并且只是,当前接收到的数目。*/
@Test
public void transferFromTest() throws IOException {//定义两个文件流RandomAccessFile randomAccessFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\read.txt", "rw");FileChannel sourceChannel = randomAccessFile.getChannel();RandomAccessFile copyFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\readCopy.txt", "rw");FileChannel targetChannel = copyFile.getChannel();//获取文件的大小log.info(">>>>开始进行文件");int position = 0;long count = sourceChannel.size();//调用,进行复制targetChannel.transferFrom(sourceChannel, position, count);log.info(">>>复制文件成功");
}

文件复制2

/*** 文件复制, source.transferTo(position,count,targetChannel)** @throws IOException 异常*/@Testpublic void transferToTest() throws IOException {//定义两个文件流RandomAccessFile randomAccessFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\read.txt", "rw");FileChannel sourceChannel = randomAccessFile.getChannel();RandomAccessFile copyFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\readCopy.txt", "rw");FileChannel targetChannel = copyFile.getChannel();//获取文件的大小log.info(">>>>开始进行文件");int position = 0;long count = sourceChannel.size();//调用,进行复制sourceChannel.transferTo(position, count, targetChannel);log.info(">>>复制文件成功");}

依次读取

 /*** 会依次读取** @throws IOException Scattering Reads 在移动下一个 buffer 前,必须填满当前的 buffer,这也意味着它*                     不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体,*                     消息头必须完成填充(例如 128byte),Scattering Reads 才能正常工作。*/@Testpublic void scatterTest() throws IOException {RandomAccessFile randomAccessFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\read.txt", "rw");FileChannel fileChannel = randomAccessFile.getChannel();//创建 ByteBuffer 数组ByteBuffer[] byteBuffers = new ByteBuffer[3];byteBuffers[0] = ByteBuffer.allocate(4);byteBuffers[1] = ByteBuffer.allocate(4);byteBuffers[2] = ByteBuffer.allocate(10);//读取数据到 fileChannellog.info(">>>读取数据到 byteBuffers");fileChannel.read(byteBuffers);//输出信息//写入数据log.info(">>> 打印第一个 ByteBuffer 里面的数据");//需要转换 flipbyteBuffers[0].flip();while (byteBuffers[0].hasRemaining()) {//写入数据log.info("读取数据:" + (char) (byteBuffers[0].get()));}log.info(">>> 打印第二个 ByteBuffer 里面的数据");byteBuffers[1].flip();while (byteBuffers[1].hasRemaining()) {//写入数据log.info("读取数据:" + (char) (byteBuffers[1].get()));}log.info(">>> 打印第三个 ByteBuffer 里面的数据");byteBuffers[2].flip();while (byteBuffers[2].hasRemaining()) {//写入数据log.info("读取数据:" + (char) (byteBuffers[2].get()));}fileChannel.close();randomAccessFile.close();}

依次写入


/*** 会依次写入* <p>* buffers 数组是 write()方法的入参,write()方法会按照 buffer 在数组中的顺序,将数* 据写入到 channel,注意只有 position 和 limit 之间的数据才会被写入。因此,如果* 一个 buffer 的容量为 128byte,但是仅仅包含 58byte 的数据,那么这 58byte 的数* 据将被写入到 channel 中。因此与 Scattering Reads 相反,Gathering Writes 能较* 好的处理动态消息** @throws IOException 异常*/@Testpublic void gatherTest() throws IOException {RandomAccessFile randomAccessFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\write.txt", "rw");FileChannel fileChannel = randomAccessFile.getChannel();//创建 ByteBuffer 数组ByteBuffer[] byteBuffers = new ByteBuffer[3];byteBuffers[0] = ByteBuffer.allocate(4);byteBuffers[1] = ByteBuffer.allocate(4);byteBuffers[2] = ByteBuffer.allocate(10);//读取数据到 fileChannel   不能超过对应的长度byteBuffers[0].put("Hell".getBytes());byteBuffers[1].put("NIOO".getBytes());byteBuffers[2].put("!!!!!!".getBytes());log.info(">>>写入数据到 byteBuffers");//不要忘记 调用  flip () 处理byteBuffers[0].flip();byteBuffers[1].flip();byteBuffers[2].flip();fileChannel.write(byteBuffers);log.info(">>写入数据成功");fileChannel.close();randomAccessFile.close();}

ServerSocketChannel

简单的配置

/*** 测试处理** @throws Exception 异常*/@Testpublic void simpleTest() throws Exception {//1. 准备数据  ByteBuffer  包装到缓冲区ByteBuffer byteBuffer = ByteBuffer.wrap("Hello NIO!!!".getBytes());//创建 ServerSocketChannel  .open ()ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//进行绑定 指定的端口serverSocketChannel.socket().bind(new InetSocketAddress(9999));//配置为异步信息  false 为异步, true 为同步//  serverSocketChannel.configureBlocking(false);serverSocketChannel.configureBlocking(true);log.info(">>>>开始连接");while (true) {SocketChannel socketChannel = serverSocketChannel.accept();if (null == socketChannel) {log.info(">>>>没有客户端连接,2s后重新检测");TimeUnit.SECONDS.sleep(2);} else {log.info("获取远程连接:" + socketChannel.socket().getRemoteSocketAddress());//倒回数据,即每次都重新取.byteBuffer.rewind();socketChannel.write(byteBuffer);//连接之后,就关闭。socketChannel.close();}}}

SocketChannel

同步阻塞


/*** 同步,阻塞, 会一直等待下去*/@Testpublic void blockingTest() throws IOException {//1. 创建 SocketChannel 连接SocketChannel socketChannel =SocketChannel.open(//没有协议new InetSocketAddress("pre.zkong.com",80));//2. 创建 ByteBuffer 连接ByteBuffer byteBuffer = ByteBuffer.allocate(102400);//3. 将数据写入到 ByteBuffer 里面log.info(">>>>将数据写入到 ByteBuffer 缓冲里面");socketChannel.read(byteBuffer);byteBuffer.flip();while (byteBuffer.hasRemaining()) {log.info((char) byteBuffer.get());}socketChannel.close();log.info(">>>将数据输出成功");}

异步阻塞


/*** 异步,会输出打印** @throws IOException 异常*/@Testpublic void asyncTest() throws IOException {//1. 创建 SocketChannel 连接SocketChannel socketChannel =SocketChannel.open(//没有协议new InetSocketAddress("pre.zkong.com",80));socketChannel.configureBlocking(false);//2. 创建 ByteBuffer 连接ByteBuffer byteBuffer = ByteBuffer.allocate(102400);//3. 将数据写入到 ByteBuffer 里面log.info(">>>>将数据写入到 ByteBuffer 缓冲里面");int readCount = socketChannel.read(byteBuffer);while (readCount > 0) {byteBuffer.flip();while (byteBuffer.hasRemaining()) {log.info((char) byteBuffer.get());}byteBuffer.clear();readCount = socketChannel.read(byteBuffer);}socketChannel.close();log.info(">>>将数据输出成功");}

DatagramChannel

发送数据

/*** 发送数据** @throws Exception 异常*/@Testpublic void sendTest() throws Exception {DatagramChannel datagramChannel = DatagramChannel.open();//发送到哪个, 指定地址InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9999);ByteBuffer byteBuffer = ByteBuffer.allocate(128);byteBuffer.put(Charset.forName("UTF-8").encode("两个蝴蝶飞,你好啊"));while (true) {// 调用 send 方法,进行发送datagramChannel.send(byteBuffer, inetSocketAddress);//重置byteBuffer.rewind();log.info(">>>发送数据成功");TimeUnit.SECONDS.sleep(1);}}

接收数据


@Testpublic void receiveTest() throws Exception {//1. 创建连接DatagramChannel datagramChannel = DatagramChannel.open();//2. 绑定端口号datagramChannel.socket().bind(new InetSocketAddress(9999));//3. 创建ByteBuffer 缓冲区ByteBuffer byteBuffer = ByteBuffer.allocate(1024);while (true) {//清空byteBuffer.clear();//接收数据SocketAddress socketAddress = datagramChannel.receive(byteBuffer);byteBuffer.flip();log.info(">>>发送方:" + socketAddress.toString());log.info(">>>具体数据:" + Charset.forName("UTF-8").decode(byteBuffer));
​datagramChannel.send(ByteBuffer.wrap("接收到数据了".getBytes("UTF-8")), socketAddress);TimeUnit.MILLISECONDS.sleep(500);}}

端口绑定处理


@Testpublic void portTest() throws Exception {DatagramChannel datagramChannel = DatagramChannel.open();// datagramChannel.configureBlocking(false);//绑定到  9998, 当前发送方的 端口号datagramChannel.socket().bind(new InetSocketAddress(9998));//连接到 9999, 连接服务器端datagramChannel.connect(new InetSocketAddress("127.0.0.1",9999));
​log.info(">>>>写入数据");
​
​//接收数据ByteBuffer readBuffer = ByteBuffer.allocate(1024);while (true) {datagramChannel.write(ByteBuffer.wrap("两个蝴蝶飞,你好啊222!!!".getBytes("UTF-8")));//先清空, 再写入进去readBuffer.clear();datagramChannel.read(readBuffer);
​readBuffer.flip();
​log.info(">>输出信息:" + Charset.forName("UTF-8").decode(readBuffer));TimeUnit.MILLISECONDS.sleep(500);}
​
​}

Buffer

ByteBuffer 缓冲区处理

/*** byte 缓冲区处理** @throws IOException IO异常*/@Testpublic void byteBufferTest() throws IOException {RandomAccessFile randomAccessFile =new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\read.txt", "rw");//写入到数据里面FileChannel fileChannel = randomAccessFile.getChannel();//2. 创建 ByteBufferByteBuffer byteBuffer = ByteBuffer.allocate(32);//将数据写入到 byteBufferint readCount = fileChannel.read(byteBuffer);while (readCount > 0) {//调转,切换成读模式byteBuffer.flip();//读出数据while (byteBuffer.hasRemaining()) {log.info((char) byteBuffer.get());}//清空byteBuffer.clear();
​readCount = fileChannel.read(byteBuffer);}fileChannel.close();randomAccessFile.close();log.info(">>>读取数据成功");}

Int 缓冲区处理


/*** int 缓冲区处理*/@Testpublic void intBufferTest() {// 设置容量为 10IntBuffer intBuffer = IntBuffer.allocate(10);//放置数据for (int i = 0; i < intBuffer.capacity(); i++) {//放置信息intBuffer.put(i * 2);}//切换成写模式
​intBuffer.flip();//读出数据while (intBuffer.hasRemaining()) {log.info(">>>输出:" + intBuffer.get());}
​intBuffer.clear();
​log.info(">>>读取数据成功");}

rewind 重读

/*** 重读**/@Testpublic void rewindTest() {// 设置容量为 10IntBuffer intBuffer = IntBuffer.allocate(10);//放置数据for (int i = 0; i < intBuffer.capacity(); i++) {//放置信息intBuffer.put(i * 2);}//切换成写模式intBuffer.flip();log.info(">>>> 第一回输出");while (intBuffer.hasRemaining()) {log.info(">>>1输出:" + intBuffer.get());}log.info(">>>> 再次进行输出");// 没有 数据while (intBuffer.hasRemaining()) {log.info(">>>2输出:" + intBuffer.get());}//重置intBuffer.rewind();log.info(">>>> rewind() 后再次进行输出");while (intBuffer.hasRemaining()) {log.info(">>>3输出:" + intBuffer.get());}
​
​intBuffer.clear();
​log.info(">>>读取数据成功");}

clear 清空


/*** 全部清空  clear  和 compact 清空已读**/@Testpublic void clearAndCompactTest() {// 设置容量为 10IntBuffer intBuffer = IntBuffer.allocate(10);//放置数据for (int i = 0; i < intBuffer.capacity(); i++) {//放置信息intBuffer.put(i * 2);}//切换成写模式intBuffer.flip();log.info(">>>>全部清空");intBuffer.clear();while (intBuffer.hasRemaining()) {log.info(">>>全部清空输出:" + intBuffer.get());}intBuffer = IntBuffer.allocate(10);//放置数据for (int i = 0; i < intBuffer.capacity(); i++) {//放置信息intBuffer.put(i * 2);}//切换成写模式intBuffer.flip();//先读两个// 0 2for (int i = 0; i < 2; i++) {log.info(">>>先读两个输出:" + intBuffer.get());}log.info(">>>>已读清空");/** 调用 compact 之前,    position 为 2, limit 为 10*  调用之后   position  为  10-2 =8, limit 为 10*/log.info(">>>调用前 limit 输出位置:" + intBuffer.limit());log.info(">>>调用前 position 输出位置:" + intBuffer.position());intBuffer.compact();log.info(">>>调用后 limit 输出位置:" + intBuffer.limit());log.info(">>>调用后 position 输出位置:" + intBuffer.position());while (intBuffer.hasRemaining()) {//  16  18log.info(">>>已读清空输出:" + intBuffer.get());}log.info(">>>读取数据成功");}

mark 标记


/*** mark 标记* 和 reset 重置标记**/@Testpublic void markAndResetTest() {// 设置容量为 10IntBuffer intBuffer = IntBuffer.allocate(10);//放置数据for (int i = 0; i < intBuffer.capacity(); i++) {//放置信息intBuffer.put(i * 2);}//切换成写模式intBuffer.flip();// 0 2//先读两个for (int i = 0; i < 2; i++) {log.info(">>>先读两个输出:" + intBuffer.get());}log.info(">>>读两个之后,设置标记 mark ");intBuffer.mark();//读剩下的while (intBuffer.hasRemaining()) {log.info(">>>读取剩下的:" + intBuffer.get());}//重置标记,继续读以前的 2~9intBuffer.reset();log.info(">>>重置  reset 之后,继续读取");while (intBuffer.hasRemaining()) {log.info(">>>重读剩下的:" + intBuffer.get());}intBuffer.clear();
​log.info(">>>读取数据成功");}

缓冲区分类

子缓冲区

/*** 子缓冲区大小*/@Testpublic void sliceTest() {IntBuffer intBuffer = IntBuffer.allocate(10);//放置信息for (int i = 0; i < intBuffer.capacity(); i++) {intBuffer.put(i);}//这是原有的信息//3. 创建子缓冲区   只在 3~7 时创建intBuffer.position(3);intBuffer.limit(7);IntBuffer sliceBuffer = intBuffer.slice();//将这一段子缓冲区的信息改变log.info("改变子缓冲区的内容信息");for (int i = 0; i < sliceBuffer.capacity(); i++) {//扩大10倍int index = sliceBuffer.get();sliceBuffer.put(i, index * 10);}//重新读intBuffer.position(0);intBuffer.limit(intBuffer.capacity());while (intBuffer.hasRemaining()) {log.info(">>>读取信息:" + intBuffer.get());}// 输出信息:  0  1  2 30 40 50 60 7 8 9}
只读缓冲区

/*** 只读缓冲区*/@Testpublic void readOnlyTest() {IntBuffer intBuffer = IntBuffer.allocate(10);//放置信息for (int i = 0; i < intBuffer.capacity(); i++) {intBuffer.put(i);}//这是原有的信息//3. 创建子缓冲区   只在 3~7 时创建IntBuffer readOnlyBuffer = intBuffer.asReadOnlyBuffer();//将这一段子缓冲区的信息改变log.info("改变子缓冲区的内容信息");intBuffer.flip();for (int i = 0; i < intBuffer.capacity(); i++) {//扩大10倍int index = intBuffer.get();intBuffer.put(i, index * 10);}//重新读readOnlyBuffer.position(0);readOnlyBuffer.limit(intBuffer.capacity());intBuffer.flip();while (readOnlyBuffer.hasRemaining()) {log.info(">>>只读缓冲区信息:" + readOnlyBuffer.get());}// 输出信息:  0  10  20 30 40 50 60 70 80 90}
直接缓冲区
/*** 直接缓冲区** @throws Exception 异常*/@Testpublic void allocateDirectTest() throws Exception {//定义两个文件流RandomAccessFile randomAccessFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\read.txt", "rw");FileChannel sourceChannel = randomAccessFile.getChannel();RandomAccessFile copyFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\readCopy.txt", "rw");FileChannel targetChannel = copyFile.getChannel();//1. 创建一个直接缓冲区ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);//往里面写入数据while (true) {int readCount = sourceChannel.read(byteBuffer);//读取不到,则退出循环,完成复制if (readCount > -1) {break;}byteBuffer.flip();//写入进去targetChannel.write(byteBuffer);}log.info(">>>>写入数据成功");}
内存映射IO

/*** 内存映射IO** @throws Exception 异常信息*/@Testpublic void mapTest() throws Exception {//定义两个文件流RandomAccessFile randomAccessFile = new RandomAccessFile("F:\\Java\\NIO\\Code\\NIO\\read.txt", "rw");FileChannel fileChannel = randomAccessFile.getChannel();//内存映射IOMappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);//更改相应部分的数据。  不能超过原文件的大小mappedByteBuffer.putChar(2, 'L');mappedByteBuffer.putChar(4, 'O');mappedByteBuffer.putChar(6, 'V');mappedByteBuffer.putChar(8, 'E');log.info(">>>>写入数据成功");}

Selector

服务器端


@Testpublic void serverTest() throws Exception {//1. 构建 ServerSocketChannelServerSocketChannel serverSocketChannel \= ServerSocketChannel.open();//进行绑定serverSocketChannel.socket().bind(new InetSocketAddress(9997));//2. 设置异步serverSocketChannel.configureBlocking(false);//3. 创建 SelectorSelector selector \= Selector.open();//4. 注册事件  接收连接事件serverSocketChannel.register(selector, SelectionKey.OP\_ACCEPT);//5. 构建可读,可写的 Buffer 信息ByteBuffer readBuffer \= ByteBuffer.allocate(1024);ByteBuffer writeBuffer \= ByteBuffer.allocate(1024);writeBuffer.put("Hello,I am Server".getBytes());//切换writeBuffer.flip();while (true) {//6.获取信息int select \= selector.select();log.info("输出数量" + select);Set<SelectionKey\> selectionKeySet \= selector.selectedKeys();//如果有的话,进行处理Iterator<SelectionKey\> iterator \= selectionKeySet.iterator();while (iterator.hasNext()) {//获取下一下SelectionKey selectionKey \= iterator.next();//移除iterator.remove();//对类型处理判断if (selectionKey.isAcceptable()) {//连接状态SocketChannel socketChannel \= serverSocketChannel.accept();log.info("获取一个连接: " + socketChannel);//注册读事件socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP\_READ);}//是读的话if (selectionKey.isReadable()) {SocketChannel socketChannel \= (SocketChannel) selectionKey.channel();//读信息readBuffer.clear();socketChannel.read(readBuffer);//flipreadBuffer.flip();//写数据log.info(">>>读取的数据:" + new String(readBuffer.array()));//注册写事件selectionKey.interestOps(SelectionKey.OP\_WRITE);}if (selectionKey.isWritable()) {//写事件SocketChannel socketChannel \= (SocketChannel) selectionKey.channel();writeBuffer.rewind();socketChannel.write(writeBuffer);log.info(">>>写数据数据:" + new String(writeBuffer.array()));selectionKey.interestOps(SelectionKey.OP\_READ);}}TimeUnit.SECONDS.sleep(1);}}

客户端

@Testpublic void clientTest() throws Exception {//1. 创建 SocketChannelSocketChannel socketChannel \= SocketChannel.open();socketChannel.connect(new InetSocketAddress("127.0.0.1",9997));socketChannel.configureBlocking(false);//读写 BufferByteBuffer readBuffer \= ByteBuffer.allocate(1024);ByteBuffer writeBuffer \= ByteBuffer.allocate(1024);writeBuffer.put("Hello,I am Client".getBytes());//写数据,然后读数据while (true) {writeBuffer.rewind();readBuffer.clear();socketChannel.read(readBuffer);socketChannel.write(writeBuffer);TimeUnit.SECONDS.sleep(1);}}

Pipe 管道

Pipe测试


@Testpublic void pipe() throws Exception {//1. 创建 PipePipe pipe \= Pipe.open();//2. 创建写入数据的 BufferByteBuffer writeBuffer \= ByteBuffer.allocate(1024);writeBuffer.put("Write Data".getBytes());writeBuffer.flip();//3.  获取 sink() 写通道, 调用 write() 进行写入Pipe.SinkChannel sinkChannel \= pipe.sink();sinkChannel.write(writeBuffer);log.info(">>>>写入数据成功");//4. 创建 readBuffer 的读取数据存放位置ByteBuffer readBuffer \= ByteBuffer.allocate(1024);readBuffer.clear();//5. 获取 source() 读通道 ,调用 read() 进行读取Pipe.SourceChannel sourceChannel \= pipe.source();int length \= sourceChannel.read(readBuffer);//6. 获取数据信息log.info(">>>获取数据信息:" + new String(readBuffer.array(), 0, length));}

Path

Path简单构建

@Testpublic void pathTest() {Path path \= Paths.get("D:\\\\filelock.txt");log.info("path:" + path);Path path2 \= Paths.get("D:\\\\", "filelock.txt");log.info("path2:" + path2);//标准化输出Path path3 \= Paths.get("D:\\\\\\\\\\\\filelock.txt");path3 \= path3.normalize();log.info("path3:" + path3);}

Path转换成File


@Testpublic void pathToFileTest() {Path path \= Paths.get("D:\\\\filelock.txt");log.info("path:" + path);File file \= path.toFile();log.info("文件:" + file.length());//置换成 pathPath path1 \= file.toPath();log.info("path1:" + path1);}

文件锁

写文件锁


@Testpublic void writeFile() throws Exception {//1. 构建 ByteBuffer,写的内容信息ByteBuffer byteBuffer \= ByteBuffer.allocate(1024);byteBuffer.put(" I am FileLock".getBytes());//2. 构建文件 ChannelString filePath \= "D:\\\\filelock.txt";Path path \= Paths.get(filePath);//  StandardOpenOption  定义参数,  写入,追加内容FileChannel fileChannel \= FileChannel.open(path,StandardOpenOption.WRITE,StandardOpenOption.APPEND);// FileLock fileLock = fileChannel.tryLock();// FileLock fileLock = fileChannel.lock();FileLock fileLock \= fileChannel.tryLock(0, Long.MAX\_VALUE, false);if (null \== fileLock) {log.info(">>>>>没有获取到锁");return;}//写入内容log.info(">>>>获取到锁了");byteBuffer.flip();fileChannel.write(byteBuffer);//释放锁fileLock.release();}

读文件锁

@Testpublic void readTest() throws Exception {String filePath \= "D:\\\\filelock.txt";//包装类,  FileReader   BufferedReaderFileReader fileReader \= new FileReader(filePath);BufferedReader bufferedReader \= new BufferedReader(fileReader);String content \= bufferedReader.readLine();while (content != null && content.length() \> 0) {log.info(content);content \= bufferedReader.readLine();}fileReader.close();bufferedReader.close();}

Files 工具类

创建目录

/*** 创建目录* 传入的是 Path 类, 返回一个新的 Path 实例。* <p>* 如果目录存在,抛出  FileAlreadyExistsException* <p>* 如果新目录的父目录不存在, IOException* <p>* 其他系统信息,抛出 IOException*/@Testpublic void createDirectoryTest() throws IOException {Path path \= Paths.get("D:\\\\newPath");// Path path2 = Paths.get("D:\\\\a\\\\b");   // 创建不成功Path directory \= Files.createDirectory(path);log.info(directory);}

文件复制

/*** 如果目标文件已经存在,抛出 FileAlreadyExistsException 异常。* <p>* 文件复制到不存在的目录,抛出 IOException* <p>* 有其他错误,抛出 IOException*/@Testpublic void copyTest() throws IOException {Path source \= Paths.get("D:\\\\filelock.txt");Path target \= Paths.get("D:\\\\filelockcopy.txt");//复制文件// Path copy = Files.copy(source, target);//替换Path copy2 \= Files.copy(source, target, StandardCopyOption.REPLACE\_EXISTING);}

文件移动

/*** 可以从一个路径移动到另外一个路径。* 也可以移动到相同的路径,但是文件名不同,这个时候,表示重命名*/@Testpublic void moveTest() throws IOException {Path source \= Paths.get("D:\\\\filelock.txt");Path target \= Paths.get("D:\\\\filelockcopy.txt");// Path move = Files.move(source, target);Path move2 \= Files.move(source, target, StandardCopyOption.REPLACE\_EXISTING);}

文件删除

/*** 删除文件* <p>* 不能直接删除有文件的目录*/@Testpublic void deleteTest() throws IOException {// Path delFile1 = Paths.get("D:\\\\a\\\\b\\\\a.txt");Path delFile2 \= Paths.get("D:\\\\a\\\\b");//Path delFile3 = Paths.get("D:\\\\a");//删除文件Files.delete(delFile2);}

文件遍历

/*** 文件遍历处理* @throws Exception*/@Testpublic void walkFileTreeTest() throws Exception {Path rootPath \= Paths.get("D:\\\\a");List<String\> searchFileList \= new ArrayList<>();Files.walkFileTree(rootPath, new SimpleFileVisitor<Path\>() {@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {// 获取文件的绝对路径String fileString \= file.toAbsolutePath().toString();//如果以 .txt 的话,进行处理if (fileString.endsWith(".txt")) {searchFileList.add(fileString);}return FileVisitResult.CONTINUE;}});searchFileList.forEach(log::info);}

Charset 字符集

获取可用编码

/*** 获取可用的编码方式*/@Testpublic void avaliableCharsetsTest() {SortedMap<String, Charset\> charsetMap \= Charset.availableCharsets();for (Map.Entry<String, Charset\> entry : charsetMap.entrySet()) {log.info(entry.getKey() + "--->" + entry.getValue());}}

字符集 UTF8

@Testpublic void charsetUtf8Test() {CharBuffer charBuffer \= CharBuffer.allocate(1024);charBuffer.put("两个蝴蝶飞".toCharArray());charBuffer.flip();//进行处理for (int i \= 0; i < charBuffer.limit(); i++) {log.info(charBuffer.get());}}

字符编码测试

/*** 进行编码测试*/@Testpublic void charsetTest() throws Exception {//指令默认的 UTF-8Charset charset \= Charset.forName("UTF-8");//获取对应的编码器和解码器CharsetEncoder charsetEncoder \= charset.newEncoder();CharsetDecoder charsetDecoder \= charset.newDecoder();CharBuffer charBuffer \= CharBuffer.allocate(1024);charBuffer.put("两个蝴蝶飞".toCharArray());charBuffer.flip();//进行重新编码ByteBuffer encodeBuffer \= charsetEncoder.encode(charBuffer);log.info("编码后:");for (int i \= 0; i < encodeBuffer.limit(); i++) {log.info(encodeBuffer.get());}log.info("解码后:");encodeBuffer.flip();//进行解码CharBuffer decodeBuffer \= charsetDecoder.decode(encodeBuffer);for (int i \= 0; i < decodeBuffer.limit(); i++) {log.info(decodeBuffer.get());}//字符串解码方法encodeBuffer.rewind();log.info(">>>解码UTF8:" + new String(decodeBuffer.array()));//指定 GBK 进行解码log.info(">>>>其他方式进行解码:");Charset charset1 \= Charset.forName("GBK");CharsetDecoder charsetDecoder1 \= charset1.newDecoder();encodeBuffer.flip();CharBuffer decodeBuffer1 \= charsetDecoder1.decode(encodeBuffer);decodeBuffer1.rewind();for (int i \= 0; i < decodeBuffer1.limit(); i++) {log.info(decodeBuffer1.get());}//无法解析,为空decodeBuffer1.flip();log.info(">>>解码GBK:" + decodeBuffer1.toString());}

异步数据 AsynchronousFileChannel

异步读1


/*** Future 异步形式读取文件*/@Testpublic void futureReadTest() throws Exception {Path path \= Paths.get("D:\\\\filelock.txt");//创建 异步读AsynchronousFileChannel asynchronousFileChannel \= AsynchronousFileChannel.open(path,StandardOpenOption.READ);//放置到 Future 里面ByteBuffer byteBuffer \= ByteBuffer.allocate(1024);Future<Integer\> readFuture \= asynchronousFileChannel.read(byteBuffer, 0);//如果没有完成,则一直循环while (!readFuture.isDone()) {}//获取信息log.info(">>>读取长度:" + readFuture.get());byteBuffer.flip();//切换之后,输出信息log.info("信息:" + new String(byteBuffer.array()));}

异步读 handler

/*** handler 形式的读*/@Testpublic void handlerReadTest() {try {Path path \= Paths.get("D:\\\\filelock.txt");AsynchronousFileChannel asynchronousFileChannel \= AsynchronousFileChannel.open(path, StandardOpenOption.READ);//构建 ByteBufferByteBuffer byteBuffer \= ByteBuffer.allocate(1024);asynchronousFileChannel.read(byteBuffer, 0, byteBuffer,new CompletionHandler<Integer, ByteBuffer\>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {log.info(">>>字节数:" + result);// 放置到 attachment 里面了attachment.flip();log.info(">>>读取内容:" + new String(attachment.array()));}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {log.error(">>>读取失败");}});//需要休眠处理TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}}

异步写


/*** Future 异步形式写入文件*/@Testpublic void futureWriteTest() throws Exception {Path path \= Paths.get("D:\\\\filelock.txt");//创建 异步读AsynchronousFileChannel asynchronousFileChannel \= AsynchronousFileChannel.open(path,StandardOpenOption.WRITE);//放置到 Future 里面ByteBuffer byteBuffer \= ByteBuffer.allocate(1024);byteBuffer.put("我是个好人".getBytes());byteBuffer.flip();Future<Integer\> writeFuture \= asynchronousFileChannel.write(byteBuffer, 0);//如果没有完成,则一直循环while (!writeFuture.isDone()) {}log.info(">>>>>写入成功");}

handler 形式异步写


/*** handler 形式的写*/@Testpublic void handlerWriteTest() {try {Path path \= Paths.get("D:\\\\filelock.txt");AsynchronousFileChannel asynchronousFileChannel \= AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);//构建 ByteBufferByteBuffer byteBuffer \= ByteBuffer.allocate(1024);byteBuffer.put("我是个好人2222".getBytes());byteBuffer.flip();asynchronousFileChannel.write(byteBuffer, 0, byteBuffer,new CompletionHandler<Integer, ByteBuffer\>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {log.info(">>>写入内容:" + new String(attachment.array()));}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {log.error(">>>写入失败");}});//需要休眠处理TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}}

聊天室应用

服务器端


@Log4j
public class ChatServer {/*** 启动服务器* @date 2022/3/16 8:40* @author zk_yjl*/public void start() throws Exception {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(9997));//设置成 异步的serverSocketChannel.configureBlocking(false);//创建   Selector 并注册Selector selector = Selector.open();
​serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println(">>>>服务器已经成功启动");//进行循环处理while (true) {int selectCount = selector.select();if (selectCount <= 0) {continue;}//进行处理Set<SelectionKey> selectionKeys = selector.selectedKeys();TimeUnit.MILLISECONDS.sleep(100);Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()) {//获取下一个SelectionKey selectionKey = iterator.next();//对条件进行判断iterator.remove();if (selectionKey.isAcceptable()) {handlerAccept(serverSocketChannel, selector);}if (selectionKey.isReadable()) {handlerRead(selectionKey, selector);}}}}/*** 接收到消息,进行处理** @param selectionKey  选择key* @param selector 选择器* @date 2022/3/16 8:49* @author zk_yjl*/private void handlerRead(SelectionKey selectionKey, Selector selector) throws Exception {//1 获取对应的通道SocketChannel socketChannel = (SocketChannel) selectionKey.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);int readLength = socketChannel.read(byteBuffer);String message = null;if (readLength > 0) {//获取信息byteBuffer.flip();CharBuffer charBuffer = StandardCharsets.UTF_8.decode(byteBuffer);//获取到信息charBuffer.rewind();message = new String(charBuffer.array());}//注册可读事件socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);if (message != null) {//对消息进行打印System.out.println(socketChannel.toString() + "发送的消息是:" + message);//将这个消息,发送给其他的人sendMessageToOther(message, selector, socketChannel);}}/*** 发送消息给其他人** @param message 消息* @param selector 选择器* @param socketChannel channel* @date 2022/3/16 8:55* @author zk_yjl*/private void sendMessageToOther(String message, Selector selector, SocketChannel socketChannel) throws Exception {//1. 获取到现在的信息Set<SelectionKey> selectionKeys = selector.keys();for (SelectionKey selectionKey : selectionKeys) {//进行判断.SelectableChannel channel = selectionKey.channel();if (channel instanceof SocketChannel && channel != socketChannel) {SocketChannel tempChannel = (SocketChannel) channel;tempChannel.write(StandardCharsets.UTF_8.encode(message));}}}/*** 接收到连接时,发送一个欢迎的信息** @param serverSocketChannel channel* @param selector  选择器* @date 2022/3/16 8:45* @author zk_yjl*/private void handlerAccept(ServerSocketChannel serverSocketChannel, Selector selector) throws Exception {SocketChannel socketChannel = serverSocketChannel.accept();Charset charset = StandardCharsets.UTF_8;socketChannel.configureBlocking(false);//注册可读事件socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);socketChannel.write(charset.encode("欢迎你来到聊天室,注意隐私安全"));
​
​}public static void main(String[] args) {try {new ChatServer().start();System.out.println(">>>>启动服务器成功");} catch (Exception e) {log.info(">>>启动服务器失败 {}", e);}}
}

客户端

客户端启动


public class ChatClient {/*** 客户端处理*启动客户端* @date 2022/3/16 9:03* @author zk_yjl*/public void start(String name) throws Exception {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("127.0.0.1",9997));socketChannel.configureBlocking(false);//创建SelectSelector selector = Selector.open();//注册可读事件socketChannel.register(selector,SelectionKey.OP_READ);new Thread(new ClientThread(selector)).start();//进行处理Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {//获取到下一行String message = scanner.nextLine();socketChannel.write(StandardCharsets.UTF_8.encode(name + ":" + message));}}
}

客户端线程

@Log4j
public class ClientThread implements Runnable {public Selector selector;public ClientThread(Selector selector){this.selector = selector;}@SneakyThrows@Overridepublic void run() {//进行循环处理while(true){int selectCount = selector.select();if(selectCount<=0){continue;}//进行处理Set<SelectionKey> selectionKeys = selector.selectedKeys();TimeUnit.MILLISECONDS.sleep(100);Iterator<SelectionKey> iterator = selectionKeys.iterator();while(iterator.hasNext()) {//获取下一个SelectionKey selectionKey = iterator.next();//对条件进行判断iterator.remove();if (selectionKey.isReadable()) {handlerRead(selectionKey, selector);}}}}/*** 接收到消息,进行处理** @param selectionKey 选择key* @param selector 选择器* @date 2022/3/16 8:49* @author zk_yjl*/private  void handlerRead(SelectionKey selectionKey, Selector selector) throws Exception {//1 获取对应的通道SocketChannel socketChannel = (SocketChannel) selectionKey.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);int readLength = socketChannel.read(byteBuffer);String message = null;if (readLength > 0) {//获取信息byteBuffer.flip();CharBuffer charBuffer = StandardCharsets.UTF_8.decode(byteBuffer);//获取到信息charBuffer.rewind();message = new String(charBuffer.array());}//注册可读事件socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);if (message != null) {//对消息进行打印System.out.println(socketChannel.toString() + "发送的消息是:" + message);}}
}

聊天室人员实例化

A 用户


public class AClient {public static void main(String\[\] args) {try {new ChatClient().start("A");} catch (Exception ignored) {}}}

B 用户


public class BClient {public static void main(String\[\] args) {try {new ChatClient().start("B");} catch (Exception ignored) {}}}

C 用户


public class CClient {public static void main(String\[\] args) {try {new ChatClient().start("C");} catch (Exception ignored) {}}}

感谢你的观看。谢谢

如果喜欢,请关注我。

相关文章:

  • 网络基础二——序列化与反序列化
  • 开源推荐榜【Sejil一个 .NET带界面的日志管理组件】
  • 扫描电镜中的样品表面如何处理以增强图像对比度
  • Harmony OS 网络编程 实验指南
  • Matlab从入门到精通课程
  • Linux 常用命令汇总(八):系统管理 性能监视
  • Vue.js 3.4的新特性
  • 选择最佳图像处理工具OpenCV、JAI、ImageJ、Thumbnailator和Graphics2D
  • 万象奥科携手RT-Thread,线下体验RK3568混合部署!
  • 21个最佳照明设计软件
  • 【学不动系列】lint-staged 使用教程
  • layui框架实战案例(25):laydate中mark的数据后端生成
  • 分别使用TCP/UDP实现互相实时发送消息,接收消息功能
  • Numpy、Matplotlib、Pandas常用函数
  • ActiViz中的数据集vtkPolyData
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • Gradle 5.0 正式版发布
  • input的行数自动增减
  • Java程序员幽默爆笑锦集
  • js操作时间(持续更新)
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • Yii源码解读-服务定位器(Service Locator)
  • Zepto.js源码学习之二
  • 十年未变!安全,谁之责?(下)
  • 时间复杂度与空间复杂度分析
  • 思维导图—你不知道的JavaScript中卷
  • 通信类
  • 一份游戏开发学习路线
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • 组复制官方翻译九、Group Replication Technical Details
  • !$boo在php中什么意思,php前戏
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #、%和$符号在OGNL表达式中经常出现
  • #14vue3生成表单并跳转到外部地址的方式
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • (23)Linux的软硬连接
  • (ibm)Java 语言的 XPath API
  • (Java数据结构)ArrayList
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (层次遍历)104. 二叉树的最大深度
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (力扣)1314.矩阵区域和
  • (七)Java对象在Hibernate持久化层的状态
  • (四)库存超卖案例实战——优化redis分布式锁
  • (循环依赖问题)学习spring的第九天
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (转载)CentOS查看系统信息|CentOS查看命令
  • .net 7 上传文件踩坑
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法