文章目录
PrintStream类
这个类我们其实已经使用过,你是否还记得我们刚刚学习java时的hello world!,是怎么打印出来的。
System.out.println("hello world !");
其中的System.out就是使用的PrintStream,而println()是PrintStream类打印且换行的方法。
PrintStream是IO包里面的类,意味着我们可以使用System.out.println(“hello world !”);
将hello world写入到文件中。
show time!
package demo7_PrintStream类;
import java.io.FileNotFoundException;
import java.io.PrintStream;
/*
* PrintStream类:
* 平时我们在控制台打印输出,是调用`print`方法和`println`方法完成的,
* 这两个方法都来自于`java.io.PrintStream`类,该类能够方便地打印各种数据类型的值,
* 是一种便捷的输出方式。
*构造方法:
* public PrintStream(String fileName): 使用指定的文件名创建一个新的打印流。
*
* 常用方法
* println(); print(); 前者 换行输出 后者 不换行输出
* System.out 解释: System类下面的一个属性 out 其类型的 PrintStream
*
* 若想将 System.out.println()打印的 对象打印 在文件中
* 则需要改变System类下面的属性的Out的值 等于 PrintStream的一个对象,该对象指向了一个文件
* 即:可以将结果打印在这个文件中。
* */
public class demo1 {
public static void main(String[] args) throws FileNotFoundException {
//将类与文件之间 建立连接
PrintStream ps = new PrintStream("days6\\a.txt");
// 将数据输出到文件中 两种方式: 换行 or 不换行
ps.print(100);
ps.print(false);
ps.println();// 换行
ps.println(97);
ps.println(3.14);
ps.println('a');
ps.println(true);
ps.println("jack");
// 关闭流,释放资源
ps.close();
System.out.println("========================练习--玩一玩======================");
// 获取系统的打印流对象:
System.out.println(100);// 打印到控制台 100
// 需求: 把System.out.println()打印的目的地从控制台改成day13\\eee\\b.txt
PrintStream ps2 = System.out;
ps2.println(100);// 打印到控制台 100
//将 sout 打印的内容 输出到对应的文件中
PrintStream ps3 = new PrintStream("days6\\b.txt");
System.setOut(ps3);
System.out.println(100);// 打印b.txt文件中
}
}
commons-io jar包
前辈们对于io处理早就已经写好了类,提供我们使用,但原理,我们还是需要好好学习的,最好从超链接File类处开始学习,体会文件读写的乐趣。
jar包
链接:https://pan.baidu.com/s/18rPjp5udxrxtHXGiNm5iNw
提取码:JH66
下载之后,在idea中创建一个文件夹 lib ,将jar包放入其中,然后鼠标右键,add as Library…就可以正常使用了。
package demo9_commons_io包;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.*;
/*
* commons-io提供了一个工具类 org.apache.commons.io.IOUtils,
* 封装了大量IO读写操作的代码。其中有两个常用方法:
* 1. public static int copy(InputStream in, OutputStream out)
* 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
*
* 2. public static long copyLarge(InputStream in, OutputStream out);
* 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
*
* commons-io还提供了一个工具类org.apache.commons.io.FileUtils,封装了一些对文件操作的方法:
* 1. public static void copyFileToDirectory(final File srcFile, final File destFile) //复制文件到另外一个目录下。
* 2. public static void copyDirectoryToDirectory( file1 , file2 );//复制file1目录到file2位置。
*
*
*
* */
public class demo1 {
public static void main(String[] args) throws IOException {
//利用 jar包中的静态方法 复制 文件的内容到另外一个文件中
//赋值小于 2GB的文件
IOUtils.copy(new FileInputStream("D:\\code\\code1\\new_life\\days6\\src\\demo9_commons_io包\\a.txt"),new FileOutputStream("D:\\code\\code1\\new_life\\days6\\src\\demo9_commons_io包\\b.txt"));
//赋值大于 2GB的文件
IOUtils.copyLarge(new FileInputStream("D:\\code\\code1\\new_life\\days6\\src\\demo9_commons_io包\\a.txt"),new FileOutputStream("D:\\code\\code1\\new_life\\days6\\src\\demo9_commons_io包\\b.txt"));
//利用 jar包中的静态方法 复制 文件 到 目录中
FileUtils.copyFileToDirectory(new File("D:\\code\\code1\\new_life\\days6\\src\\demo9_commons_io包\\a.txt"),new File("D:\\code\\code1\\new_life\\days6\\src\\demo9_commons_io包\\a"));
// 复制 目录 到目录
FileUtils.copyDirectoryToDirectory(new File("D:\\code\\code1\\new_life\\days6\\src\\demo9_commons_io包"),new File("D:\\code\\code1\\new_life\\days6\\src\\demo9_commons_io包\\b"));
}
}
完毕!
NIO
NIO(New Input Output)是对之前IO的一个加强,是在JDK7之后出现的增加了一些类用于提高,读取时的效率问题,主要的类为Buffer(缓冲区),Channel(通道),Selector(选择器),同时也引入了同步与异步,阻塞与非阻塞的的概念。最终目的都是为了提高效率。
/*
*IO :同步阻塞
- NIO:同步非阻塞
- AIO:异步非阻塞
- 4. IO流的一小段历史:
-
- java1.4之前 都是一个一个字节的读取
- java1.4之后 都是推出了NIO 系统以块的方式处理数据
- 9. java7中 NIO有了一个进步 引入了 AIO: 异步 IO操作 基于 事件和回调机制,可以简单理解为,应用操作直接返回,
- 而不会阻塞在那里,当后台处理完成,操作系统会通知相应线程进行后续工作。
- 12. 先了解一下NIO的三个主要组成部分:Buffer(缓冲区),Channel(通道),Selector(选择器)
- 注意:
- NIO 是在 访问个数特别大的时候才使用,比如流行的软件或者流行的游戏中会有高并发和大量的连接
- */
Buffer类
概述:Buffer是一个对象,它是对某种基本类型的数组进行了封装。
作用:在NIO中,就是通过Buffer来读写数据的。所有的数据都是用Buffer来处理的,它是NIO读写数据的中转池。
通常使用字节数组:
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
创建对象:
package demo2_Buffer类;
import java.nio.ByteBuffer;
/*
* 创建ByteBuffer的几种方式(都是ByteBuffer的静态方法):
* 1、在堆中创建缓冲区: ByteBuffer.allocate(int capacity) //指定容量 -->推荐使用
* 2、在系统内存创建缓冲区:ByteBuffer.allocatDirect(int caoacity) //指定容量
* 3、通过数组创建缓冲区: ByteBuffer.wrap(byte[] arr) //传入数组 数组的长度就是缓冲区的大小
*
*
* 创建与销毁效率 : 间接缓冲区>直接缓存区 -->间接缓存区 更靠近 Java程序 且 缓存区都是由java程序 创建的
* 工作效率: 直接缓冲区>间接缓冲区
*
*
* */
public class d1创建 {
public static void main(String[] args) {
ByteBuffer allocate = ByteBuffer.allocate(10); //在堆区创建----间接缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10); //在系统中创建 ----直接缓冲区
byte[] b=new byte[10];
ByteBuffer bB=ByteBuffer.wrap(b); //在系统中创建----间接缓冲区
}
}
常用的方法:
三个概念必须熟练于心:
- position: 表示的是待写入的位置,会随着添加的元素操作,不断的往后移。
- capacity:表示容量,即你创建出来的容器有多大
- limit :用于对位置进行一些限制,开始时,等于 capacity的值,后期可以通过方法flip方法,令 limit等于position的位置 position等于0
方法演示:
package demo2_Buffer类;
import java.nio.ByteBuffer;
import java.util.Arrays;
/*
*
* 添加:
* public ByteBuffer put(byte b):向当前可用位置添加数据。
* public ByteBuffer put(byte[] byteArray):向当前可用位置添加一个byte[]数组
* public ByteBuffer put(byte[] byteArray,int offset,int len):添加一个byte[]数组的一部分
*容量:
* public int capacity(): 返回缓存区的容量 在对象创建时 定义 之后不可改变
*限制:
* public int limit():获取此缓冲区的限制。 -->获取对缓存区的限制
* public Buffer limit(int newLimit):设置此缓冲区的限制。
* 注意:在为设置限制之前,limit等于缓冲区的容量,在限制(1-capacity) 之后只能对 limit个数进行操作
*位置:
* public int position():获取当前可写入位置索引。
* public Buffer position(int p):更改当前可写入位置索引。
*标记:
* 作用:标记mark是指:当调用缓冲区的reset()方法时,会将缓冲区的position位置重置为该索引。
* 令 position=mart
* public Buffer mark():设置此缓冲区的标记为当前的position位置。
* public Buffer reset() : 将此缓冲区的位置重置为以前标记的位置。
*
*其他方法:
* public int remaining():获取position与limit之间的元素数。 -->还剩多少个元素可以填充 remaining :剩余的
* public boolean isReadOnly():获取当前缓冲区是否只读。
* public boolean isDirect():获取当前缓冲区是否为直接缓冲区。
*
* public Buffer rewind():重置此缓冲区。
* - 将position位置设置为:0
* - 限制limit不变。
* - 丢弃标记。
*
* public Buffer flip():缩小limit的范围。
* - 将limit设置为当前position位置;
* - 将当前position位置设置为0;
* - 丢弃标记。
* public Buffer clear():还原缓冲区的状态。
* - 将position设置为:0
* - 将限制limit设置为容量capacity;
* - 丢弃标记mark。
* */
public class d2常用方法 {
public static void main(String[] args) {
//其他方法:
ByteBuffer allocate = ByteBuffer.allocate(10); //下标从0开始
allocate.put((byte)10);
allocate.put((byte)20);
allocate.put((byte)30);
System.out.println("还剩多少个数可以添加:"+allocate.remaining());
allocate.limit(6);
ByteBuffer rewind = (ByteBuffer)allocate.rewind();
System.out.println("重置后的结果:"+rewind); //java.nio.HeapByteBuffer[pos=0 lim=6 cap=10]
//上下两个都是重置不同的是 上面的不会重置limit的值 下面的会重置 limit的值
// 即 部分重置 与 完全重置
ByteBuffer clear = (ByteBuffer)allocate.clear();
System.out.println("清除后的结果:"+clear); //java.nio.HeapByteBuffer[pos=0 lim=10 cap=10]
System.out.println(Arrays.toString(allocate.array())); //清除 结果后 原数据 不会被清空
allocate.put((byte)20);
allocate.put((byte)30);
ByteBuffer flip = (ByteBuffer)allocate.flip();
System.out.println("缩小范围后的结果:"+flip); //java.nio.HeapByteBuffer[pos=0 lim=2 cap=10]
}
public static void add3() {
//mark 和 reset
ByteBuffer allocate = (ByteBuffer)ByteBuffer.allocate(10); //下标从0开始
allocate.put((byte)10);
allocate.put((byte)20);
allocate.put((byte)30);
allocate.mark();
allocate.put((byte)40);
System.out.println("当前位置:"+allocate.position()); //4
allocate.put((byte)40);
ByteBuffer reset = (ByteBuffer)allocate.reset();
System.out.println(reset); //java.nio.HeapByteBuffer[pos=3 lim=10 cap=10] 返回的是 原缓冲区的一些参数
System.out.println("当前位置:"+allocate.position()); //3 因为在position等于3 的时候做了一个标记
}
public static void add2() {
// 容量
ByteBuffer allocate = ByteBuffer.allocate(10); //下标从0开始
System.out.println(allocate.capacity());
allocate.put((byte)10);
allocate.put((byte)10);
allocate.put((byte)10);
allocate.put((byte)10);
// 限制
int l = allocate.limit();
System.out.println("最初的限制:"+l);
allocate.limit(3); //只能存储3个数字 若再存 数据会报错 而之前 存的数据依旧存在
//allocate.put((byte)10); 报错
byte[] array = allocate.array();
System.out.println(Arrays.toString(array));
// 位置
allocate.position(0); //令写入的位置重新归零
int position = allocate.position();
System.out.println("当前准备写入的位置:"+position);
}
public static void add() {
ByteBuffer allocate = ByteBuffer.allocate(10);
allocate.put((byte)10);
allocate.put((byte)20);
allocate.put((byte)30);
allocate.put((byte)40);
allocate.put((byte)50);
byte[] array = allocate.array(); //将元素以数组的形式返回
System.out.println(Arrays.toString(array));
}
}
Channel类
Channel(通道):Channel是一个对象,可以通过它读取和写入数据,可以把它看做是IO中的流,不同的是:Channel是双向的, Channel对象既可以调用读取的方法,也可以调用写出的方法 。
输入流: 读
输出流: 写
Channel: 读,写
Channel类的分类:
- FileChannel:从文件读取数据的 输入流和输出流
- DatagramChannel:读写UDP网络协议数据 DatagramPackge
- SocketChannel:读写TCP网络协议数据 Socket
- ServerSocketChannel:可以监听TCP连接 ServerSocket
FileChannel
ByteBuffer与FileChannel共同读取数据:
package demo3_Channel类.d1_FileChannle_ByteBuffer;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/*
* java.nio.channels.FileChannel (抽象类):用于读、写文件的通道。
*
* FileChannel是抽象类,
* 我们可以通过FileInputStream和FileOutputStream的getChannel()方法方便的获取一个它的子类对象。
*
* 我们将通过CopyFile这个示例让大家体会NIO的操作过程。
* CopyFile执行三个基本的操作:创建一个Buffer,然后从源文件读取数据到缓冲区,
* 然后再将缓冲区写入目标文件。
*
* 原理:
* 将源文件从磁盘读取到缓冲区,
* 然后再从缓冲区 写文件到磁盘中 //2次访问磁盘 浪费时间 可以采用MappedByteBuffer再次优化
* */
public class d2_FileChannle类 {
public static void main(String[] args) throws IOException {
//采用 通道的方式 进行数据 写入
FileInputStream fi=new FileInputStream(new File("D:\\code\\new_life\\days8\\src\\demo3_Channel类\\file\\1.txt"));
FileOutputStream fo=new FileOutputStream(new File("D:\\code\\new_life\\days8\\src\\demo3_Channel类\\file\\2.txt"));
//获得传输通道channel
FileChannel inChannel=fi.getChannel(); //此时的inChannel类型为 FileChannelImpl 而不是 Channel类型
FileChannel outChannel=fo.getChannel();
ByteBuffer allocate = ByteBuffer.allocate(100);
//原先版本
/*byte[] bys = new byte[8192];
int len;
while ((len = fis.read(bys)) != -1){
fos.write(bys,0,len);
}
fos.close();
fis.close();*/
//此时的读写已经 被缓冲区简化了 但是只能写 2g 以下的数据
while((inChannel.read(allocate))!=-1){
allocate.flip(); //会 缩小范围 将 limit 等于position的位置 即 将 写入的的数据 画上一个圈
outChannel.write(allocate);
allocate.clear(); // 清除 原先的limit position 等限制 即 将原先的缓冲区 变为一个新的 但是 原先里面额数据还在 但是 再次写入的话 会在第一个 位置写数据
}
//关闭流释放资源
fi.close();
fo.close();
inChannel.close();
outChannel.close();
}
}
MappedByteBuffer类
MappedByteBuffer类高效读写操作
package demo3_Channel类.d2_FileChannle_MappedByteBuffer高效读写;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/*
* 上例直接使用FileChannel结合ByteBuffer实现的管道读写, 但并不能提高文件的读写效率。
*
* ByteBuffer有个抽象子类:MappedByteBuffer,
* 它可以将文件直接映射至内存, 把硬盘中的读写变成内存中的读写, 所以可以提高大文件的读写效率。
*
* 获取 MappedByteBuffer类
* FileChannel类中的方法 map()
*
* MappedByteBuffer map(MapMode mode, long position, long size);
说明:将节点中从position开始的size个字节映射到返回的MappedByteBuffer中,
* mode 等于FileChannel.MapMode. 点属性
*
* 为什么使用RandomAccessFile?
* 1). 使用InputStream获得的Channel可以映射,使用map时只能指定为READ_ONLY模式,不能指定为READ_WRITE和PRIVATE,否则会抛出运行时异常!
* 2). 使用OutputStream得到的Channel不可以映射!并且OutputStream的Channel也只能write不能read!
* 3). 只有RandomAccessFile获取的Channel才能开启任意的这三种模式!
*
* 若要复制 2G以上的文件则需要将文件拆分为 大小一致的块(最后一块不同)
* :如拆分 2.4G的文件 每个分为500M
* 第一块 0-499M
* 第二块 500-999M
* 第三块 1000-1499M
* 第四块 1500-1999M
* 第五块 2000-2399M //最后一块只有 400M
*
* 一共需要拆分 (24000/500)+1次 (zong/size)==0?(zong/size):((zong/size)+1)
* start=cishu*size end=zong-start>size?size:zong-start
*
* */
public class test1 {
public static void main(String[] args) throws IOException {
}
//可以用于复制 2g以上文件
public static void store2() throws IOException {
//java.io.RandomAccessFile类,可以设置读、写模式的IO流类。
//"r"表示:只读--输入流,只读就可以。
RandomAccessFile r1 = new RandomAccessFile("D:\\code\\new_life\\days8\\src\\demo3_Channel类\\file\\1.txt","r");
//"rw"表示:读、写--输出流,需要读、写。
RandomAccessFile r2 = new RandomAccessFile("D:\\code\\new_life\\days8\\src\\demo3_Channel类\\file\\5.txt","rw");
// 获得FileChannel管道对象
FileChannel c1 = r1.getChannel();
FileChannel c2 = r2.getChannel();
long size = c1.size();//获取文件大小
int everysize=1024*1024*500;
long count=(size%everysize)==0?(size/everysize):((size/everysize)+1);
for (long i = 0; i < count; i++) {
//每次开始的位置
long start=i*everysize;
//实际存储的多少数
long end=(size-start)>everysize?everysize:(size-start);
//映射文件到内存中
//参数说明:模式, 开始位置 , 最后的位置
MappedByteBuffer map = c1.map(FileChannel.MapMode.READ_ONLY,start,end);
MappedByteBuffer map1 = c2.map(FileChannel.MapMode.READ_WRITE, start, end);
for (long j = 0; j <end ; j++) {
byte b = map.get(); //读取数据
map1.put(b); //写入数据
}
}
//释放资源
r1.close();
r2.close();
c1.close();
c2.close();
}
//只能复制2G以下文件
public static void Store() throws IOException {
//java.io.RandomAccessFile类,可以设置读、写模式的IO流类。
//"r"表示:只读--输入流,只读就可以。
RandomAccessFile r1 = new RandomAccessFile("D:\\code\\new_life\\days8\\src\\demo3_Channel类\\file\\1.txt","r");
//"rw"表示:读、写--输出流,需要读、写。
RandomAccessFile r2 = new RandomAccessFile("D:\\code\\new_life\\days8\\src\\demo3_Channel类\\file\\3.txt","rw");
// 获得FileChannel管道对象
FileChannel c1 = r1.getChannel();
FileChannel c2 = r2.getChannel();
long size = c1.size();//获取文件大小
//映射文件到内存中
//参数说明:模式, 开始位置 , 最后的位置
MappedByteBuffer map = c1.map(FileChannel.MapMode.READ_ONLY,0,size);
MappedByteBuffer map1 = c2.map(FileChannel.MapMode.READ_WRITE, 0, size);
for (long i = 0; i <size ; i++) {
byte b = map.get(); //读取数据
map1.put(b); //写入数据
}
//释放资源
r1.close();
r2.close();
c1.close();
c2.close();
}
}
ServerSocketChannel和ServerChannel类
client端的读写操作演示
package demo3_Channel类.d3_ServerSocketChannel和ServerChannel类;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
/*
*SocketChannel类似于客户端
* 1). 先调用SocketChannel的open()方法打开通道:
* 2). 调用SocketChannel的实例方法connect(SocketAddress add)连接服务器:
*
* 该对象可读 可写
* */
public class Client {
public static void main(String[] args) throws IOException {
SocketChannel skc = SocketChannel.open();
skc.connect(new InetSocketAddress("127.0.0.1",9999));
System.out.println("发出请求");
}
}
server端的读写操作演示
package demo3_Channel类.d3_ServerSocketChannel和ServerChannel类;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/*
*ServerSocketChannel类 类似于服务器:
* 获取对象:ServerSocketChannel的静态方法 //不可绑定端口号
* 绑定端口:public void bind(InetSocketAddress(端口号))
* 获取请求: public void accept()
*
*
* */
public class Server {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocketChannel open = ServerSocketChannel.open();
open.bind(new InetSocketAddress(9999));
while (true) {
open.configureBlocking(true); //如果为false 非阻塞 如果true 则为阻塞 阻塞的话accept会一直等待 请求而不会往下走 默认是true的
SocketChannel accept = open.accept();
if(accept!=null){
System.out.println("接收到了请求");
}else{
System.out.println("未收到了请求");
Thread.sleep(5000);
}
}
}
}
Selector类
Selector是NIO(同步非阻塞)的最要技术之一 应用于服务器的技术,它实现了与SelectableChannel联合实现了非阻塞的多路复用,,如果不使用多路复用,服务器需要开很多进程处理每一个请求,若高并发,将会造成系统性能下降,使用了多路复用,一个selector可以监听多个服务器端口
创建于注册
package demo4_Selector类;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
/*
*
* Selector的获取和注册:
* 获取:
* Selector的静态方法 open() 获取Selector对象
* 注册Channel到Selector
* 通过Channel的register(Selector sel, int ops)方法把Channel注册到指定的选择器上
* 参数1: 表示选择器
* 参数2: 选择器要监听Channel的什么事件
* 1. 连接就绪--常量:SelectionKey.OP_CONNECT
* 2. 接收就绪--常量:SelectionKey.OP_ACCEPT (ServerSocketChannel在注册时只能使用此项)
* 3. 读就绪--常量:SelectionKey.OP_READ
* 4.写就绪--常量:SelectionKey.OP_WRITE
* 注意:
* 1.对于ServerSocketChannel在注册时,只能使用OP_ACCEPT,否则抛出异常。
* 2.ServerSocketChannel要设置成非阻塞
*
*
* */
public class test1_获取与注册 {
public static void main(String[] args) throws IOException {
//获取服务器 通道对象
ServerSocketChannel ssc = ServerSocketChannel.open();
//绑定端口
ssc.bind(new InetSocketAddress(9999));
//设置为非堵塞
ssc.configureBlocking(false);
//获取选择器
Selector sc = Selector.open();
//将channel对象注册到 选择器
ssc.register(sc, SelectionKey.OP_ACCEPT);
//多注册几个
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ssc2.bind(new InetSocketAddress(6666));
ssc2.configureBlocking(false);
ssc2.register(sc, SelectionKey.OP_ACCEPT);
ServerSocketChannel ssc3 = ServerSocketChannel.open();
ssc3.bind(new InetSocketAddress(8888));
ssc3.configureBlocking(false);
ssc3.register(sc, SelectionKey.OP_ACCEPT);
//将多个服务器 绑定到 Selector中。
}
}
常用方法:
package demo4_Selector类;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
/*
* Selector的常用方法:
* select()方法:类似于 ServerSocket的accept方法
* 三种状态:- 在连接到第一个客户端之前,会一直阻塞
* - 当连接到客户端后,如果客户端没有被处理,该方法会计入不阻塞状态
* - 当连接到客户端后,如果客户端有被处理,该方法又会进入阻塞状态
*
* Selector的selectedKeys()方法 --- 获取已连接的所有通道集合
* Selector的keys()方法-------------获取已注册的所有通道集合
*
*
*
* */
public class test2_常用方法 {
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(6666));
ssc.configureBlocking(false);
Selector se = Selector.open();
ssc.register(se, SelectionKey.OP_ACCEPT);
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ssc2.bind(new InetSocketAddress(7777));
ssc2.configureBlocking(false);
ServerSocketChannel ssc3 = ServerSocketChannel.open();
ssc3.bind(new InetSocketAddress(8888));
ssc3.configureBlocking(false);
ssc2.register(se, SelectionKey.OP_ACCEPT);
ssc3.register(se, SelectionKey.OP_ACCEPT);
while(true){
System.out.println("刚刚进入循环");
//服务器等待用户连接 阻塞
se.select();
System.out.println("等待到了用户请求");
//因为监听了三个端口 不知道是那一个 所以
/*1、先获得所有的连接
* 2、再查看是哪一个端口 被请求了
* 3、处理
* */
Set<SelectionKey> selectionKeys = se.selectedKeys();
// for (SelectionKey s : selectionKeys) { //有几个连接就会有几个元素
// ServerSocketChannel ssc0=(ServerSocketChannel) s.channel(); //向下转型
// SocketChannel accept = ssc0.accept(); //接受请求与客户端的绑定
// System.out.println("开始处理数据。。。。。");
// //读取客户端的发送的数据
// ByteBuffer bb = ByteBuffer.allocate(1024);
// int len = accept.read(bb); //如果 两个不同的端口号请求,则此处会报错空指针异常 原因是之前的请求处理完之后没有删除,同时不是原来的端口发出请求所以读取不到数据
// //又因为 foreach 不能对元素进行修改 所以采用迭代器的方式
// System.out.println(new String(bb.array(), 0, len));
// }
//处理完这一个请求以后 需要将其删除掉 增强for循环不能够 对其中的数据进行 增删改 所以运用迭代器的方式
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while(iterator.hasNext()){
SelectionKey key=iterator.next();
ServerSocketChannel channel = (ServerSocketChannel)key.channel();
SocketChannel sc = channel.accept();
ByteBuffer bb = ByteBuffer.allocate(1024);
int len = sc.read(bb);
System.out.println(new String(bb.array(), 0, len));
//删除这一个请求
iterator.remove();
}
}
}
}
AIO
异步非阻塞:
同步:调用方法之后,必须要得到一个返回值。
异步:调用方法之后,没有返回值,但是会有回调函数。回调函数指的是满足条件之后会自动执行的方法
阻塞:如果没有达到方法的目的,就一直停在这里【等待】。
非阻塞:不管有没有达到目的,都直接【往下执行】
Server端
package demo5_AIO.AIO的概述;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
/*
* 异步通道增加了四个类
* AsynchronousSocketChannel
* AsynchronousServerSocketChannel
* AsynchronousFileChannel
* AsynchronousDatagramChannel
*
* AIO socket编程中,服务端通道是AsynchronousServerSocketChannel,
* 这个类提供了一个open()静态工厂,一个bind()方法用于绑定服务端IP地址(还有端口号),
* 另外还提供了accept()用于接收用户连接请求。
* accept()函数有两个方法:
* 同步接受请求 有返回值:Future<AsynchronousSocketChannel> accept()
* 异步接受请求 无返回值:void accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler)
* //第一个参数是添加评论的 传入null即可
* //第二个参数传入匿名对象 即可
*
* 客户端使用的通道是AsynchronousSocketChannel,这个通道处理提供open静态工厂方法外,
* 还提供了read和write方法。
* read方法也有两个:
* 同步读取 有返回值: Future<Integer> read(ByteBuffer dst)
* 异步读取 无返回值:void read(ByteBuffer dst, A attachment, CompletionHandler<Integer,? super A> handler)
* connect方法也有两个
* 同步连接:
* 异步连接:
*
* AIO编程中,发出一个事件(accept read write.connect等)之后要指定事件处理类(回调函数),
* IO中的事件处理类是CompletionHandler<V,A>,这个接口定义了如下两个方法,
* 分别在异步操作成功和失败时被回调。
* 成功:void completed(V result, A attachment);
* 失败:void failed(Throwable exc, A attachment);
* */
//异步-AIO 异步非阻塞连接和异步读
public class test1_server {
public static void main(String[] args) throws IOException {
AsynchronousServerSocketChannel assk = AsynchronousServerSocketChannel.open();
assk.bind(new InetSocketAddress(9999));
//异步连接
assk.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel s, Object attachment) {
//连接成功调用这个方法
System.out.println("连接成功");
ByteBuffer bb = ByteBuffer.allocate(1024);
//异步读取
s.read(bb, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer s, Object attachment) {
System.out.println("读取成功");
System.out.println(new String(bb.array(),0,s));
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("读取失败");
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
//连接失败调用这个方法
System.out.println("连接失败");
}
});
/*不让程序马上结束,使其处于等待接收请求的状态*/
while(true){
}
}
}
Client端
package demo5_AIO.AIO的概述;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
//完成异步非阻塞客户端请求连接(没有意义) 因为一台客户端只会发送一个请求(即大多数情况都是从上到下的同步非阻塞的)
//所以 写一个 异步非阻塞客户端请求连接 没有多大的意义
public class test2_Client {
public static void main(String[] args) throws IOException {
//客户端也有AIO对象
AsynchronousSocketChannel asc = AsynchronousSocketChannel.open();
System.out.println(1);
//指定要连接的服务器的ip和端口
asc.connect(new InetSocketAddress("127.0.0.1",8000), null, new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
//成功就执行这个方法
System.out.println(3);
}
@Override
public void failed(Throwable exc, Object attachment) {
//失败就执行这个方法
System.out.println(4);
}
});
System.out.println(2);
//写一个循环让main方法别结束
while(true){
}
}
}
//如果连接成功:
// 1
// 2
// 3
//
//如果连接失败:
// 1
// 2
// 4
以上为IO有关的所有内容,不足之处,欢迎指出。