一站式学习Java网络编程-学习手记(五)
2021/10/7 17:40:48
本文主要是介绍一站式学习Java网络编程-学习手记(五),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1. 概述
1.1 什么叫NIO?
NIO:我认为翻译成Non-Blocking
,更加的通俗直白,相比于BIO,也有一个对比,叫他非阻塞IO最好不过了
- 它和BIO有以下的区别
- Channel是
双向
的,即可以读又可以写,相比于Stream,它并不区分出输入流和输出流,而且Channel可以完成非阻塞的读写,也可以完成阻塞的读写
1.2 Buffer简介
- Channel的读写是离不开Buffer的,Buffer实际上是内存上一块用来读写的区域。
1.2.1 写模式
- 其中三个指针我们要了解一下,
position
为当前指针位置,limit
用于读模式,用它来标记可读的最大范围,capacity
是最大的可写范围阈值
当我们写数据写了四个格子时,我们执行flip()
方法,即可转变为读模式
,limit指针就直接变到了我们刚刚写数据的极限位置,position指针回到初始位置,这样我们就可以将数据读出来了
1.2.2 读模式到写模式的两种切换
- 当我们将数据全部读完时,切换到写模式
调用clear()
方法,它会使position指针回到初始位置,limit回到最远端,这样就可以重新开始数据了,虽然clear意为清除,但是其实它只是将指针的位置移动了,并没有将数据清除,而是会覆盖原来的位置 - 只读了部分数据,我想将未读的部分保留,而现在我又要开始先进行写模式的操作了,这样可以执行
compact()
方法
这个方法会将没有读到的数据保存到初始位置
,而position指针的位置将会移动到这些数据的后面位置
,从未读的数据后开始进行写数据
之后再读数据的时候,我们就能将上次没有读到的数据读出来了
1.3 Channel简介
Channel间的数据交换,都需要依赖Buffer
1.3.1 几个重要的Channel
- FileChannel:用于文件传输
- ServerSocketChannel和SocketChannel:用于网络编程的传输
2. 文件拷贝实战
- 一个字节一个字节的拷贝实在是慢的不行。
import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; interface FileCopyRunner{ void copyFile(File source,File target); } public class FileCopyDemo { private static void close(Closeable closeable){ if(closeable != null) { try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } //不使用任何缓冲的留的拷贝 private static FileCopyRunner noBufferStreamCopy = new FileCopyRunner() { @Override public void copyFile(File source, File target) { InputStream fin = null; OutputStream fout = null; try { fin = new FileInputStream(source); fout = new FileOutputStream(target); int result; while((result = fin.read()) != - 1){ fout.write(result); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { close(fin); close(fout); } } }; //使用缓冲区的流的拷贝 private static FileCopyRunner bufferStreamCopy = new FileCopyRunner() { @Override public void copyFile(File source, File target) { InputStream fin = null; OutputStream fout = null; try { fin = new FileInputStream(source); fout = new FileOutputStream(target); //创建缓冲区 byte[] buffer = new byte[1024]; int result; while((result = fin.read(buffer)) != -1){ //result这里表示从中读出来的具体字节数 //虽然缓冲区中能缓存1024,但是我们读取的时候不一定就有这么多字节 //所以我们使用result做下面的参数 fout.write(buffer,0,result); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { close(fin); close(fout); } } }; //使用带有缓冲区的channel复制 nio private static FileCopyRunner nioBufferCopy = new FileCopyRunner() { @Override public void copyFile(File source, File target) { FileChannel fin = null; FileChannel fout = null; try { fin = new FileInputStream(source).getChannel(); fout = new FileOutputStream(target).getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); while(fin.read(byteBuffer) != -1){ byteBuffer.flip();//转变为读模式 while (byteBuffer.hasRemaining()){ fout.write(byteBuffer); } byteBuffer.clear();//转变为写模式 } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { close(fin); close(fout); } } }; //使用没有缓冲区的channel复制文件 private static FileCopyRunner nioTransferCopy = ((source, target) -> { FileChannel fin = null; FileChannel fout = null; try { fin = new FileInputStream(source).getChannel(); fout = new FileOutputStream(target).getChannel(); long transferred = 0L; long size = fin.size(); while(transferred != size){ //如果拷贝的大小没有达到源文件的大小就要一直拷贝 transferred += fin.transferTo(0,size,fout); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { close(fin); close(fout); } }); public static void main(String[] args) { File source = new File("J:\\StudySpace\\Java秒杀系统方案优化-高性能高并发实战\\project.zip"); File target = new File("J:\\StudySpace\\Java秒杀系统方案优化-高性能高并发实战\\p1.zip"); File target2 = new File("J:\\StudySpace\\Java秒杀系统方案优化-高性能高并发实战\\p2.zip"); File target3 = new File("J:\\StudySpace\\Java秒杀系统方案优化-高性能高并发实战\\p3.zip"); File target4 = new File("J:\\StudySpace\\Java秒杀系统方案优化-高性能高并发实战\\p4.zip"); new Thread(() -> noBufferStreamCopy.copyFile(source,target)).start(); new Thread(() -> bufferStreamCopy.copyFile(source,target2)).start(); new Thread(() -> nioBufferCopy.copyFile(source,target3)).start(); new Thread(() -> nioTransferCopy.copyFile(source,target4)).start(); } }
3. Selector概述
- Channel需要在Selector上注册
- 注册的同时,要告诉Selector监听的状态
- Channel对应的状态有:
CONNECT
:socketChannel已经与服务器建立连接的状态;ACCEPT
:serverSocketChannel已经与客户端建立连接的状态;READ
:可读状态;WRITE
:可写状态 - Channel在Selector上注册完成后,会返回一个SelectKey对象,其中有几个重要的方法:interestOps:查看注册的Channel绑定的状态;readyOps:查看哪些是可操作的状态;channel:返回channel对象;selector:返回selector对象;attachment:附加对象
- 调用Selector的select方法,返回它监听的事件的数量,可同时响应多个事件。不过它是阻塞式的调用,当监听的事件中没有可以用来响应请求的,则会被阻塞,直到有可用的channel能够响应该请求,才会返回
这篇关于一站式学习Java网络编程-学习手记(五)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-22项目:远程温湿度检测系统
- 2024-12-21《鸿蒙HarmonyOS应用开发从入门到精通(第2版)》简介
- 2024-12-21后台管理系统开发教程:新手入门全指南
- 2024-12-21后台开发教程:新手入门及实战指南
- 2024-12-21后台综合解决方案教程:新手入门指南
- 2024-12-21接口模块封装教程:新手必备指南
- 2024-12-21请求动作封装教程:新手必看指南
- 2024-12-21RBAC的权限教程:从入门到实践
- 2024-12-21登录鉴权实战:新手入门教程
- 2024-12-21动态权限实战入门指南