IO流最后一些内容--AIO等

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有关的所有内容,不足之处,欢迎指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值