Fork me on GitHub

网络编程模型之NIO

1.简单介绍

同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

2.模型图

 

3.Buffer缓冲区

  a.Buffer在Java NIO的作用

  在Java NIO中,缓冲区的作用也是用来临时存储数据,可以理解为是I/O操作中数据的中转站。缓冲区直接为通道(Channel)服务,写入数据到通道或从通道读取数据,这样的操利用缓冲区数据来传递就可以达到对数据高效处理的目的。在NIO中主要有八种缓冲区类(其中MappedByteBuffer是专门用于内存映射的一种ByteBuffer):

 

 

 

  b.Buffer的属性

容量(capacity):缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变
上界(limit):缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数
位置(position):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新
标记(mark):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新一个备忘位置。调用 mark( )来设定 mark = postion。调用 reset( )设定 position =mark。标记在设定前是未定义的(undefined)。这四个属性之间总是遵循以下关系:0 <= mark <= position <= limit <= capacity

 c.Buffer的简单实例

//Debug可以看出属性变化
public class BufferBasic {
    public static void main(String[] args) {
        //创建一个长度为5,类型为int的buffer
        IntBuffer intBuffer=IntBuffer.allocate(5);
        //向buffer存放数据
        for (int i = 0; i < intBuffer.capacity(); i++) {
            intBuffer.put(i*2);
        }
    }
}

 

  

4.NIO通道

java.nio.channels包中的Channel接口的已知实现类:
FileChannel从文件中读写数据。
DatagramChannel:能通过UDP读写网络中的数据。
SocketChannel:能通过TCP读写网络中的数据。
ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

 

a.  FileChannel

a-1. 从文件中读取信息

/**
 * 在通道中使用buffer读数据
 */
public class FileChannelNIO02 {
    public static void main(String[] args) throws IOException {
        //获取输入流
        File file = new File("f:\\file01.txt");
        FileInputStream fileInputStream = new FileInputStream(file);
        //获取对应的channel
        FileChannel fileChannel=fileInputStream.getChannel();
        //创建缓冲区
        ByteBuffer byteBuffer=ByteBuffer.allocate((int) file.length());
        //将通道数据读取到buffer
        fileChannel.read(byteBuffer);
        //将字节转为字符串
        String str=new String(byteBuffer.array());
        System.out.println(str);
        fileInputStream.close();


    }
}

 

 a-2. 写入数据到文件中

public class FileChannelNIO {
    public static void main(String[] args) throws IOException {
        String str="dcascs";
        //创建一个输出流
        FileOutputStream  fileOutputStream=new FileOutputStream("f:\\file01.txt");
        //获取对应的filechannel
        FileChannel fileChannel=fileOutputStream.getChannel();
        //创建缓冲区
        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
        //将str放入缓冲区
        byteBuffer.put(str.getBytes());
        //对byteBuffer进行flip;
        byteBuffer.flip();
        //将bytebuffer写入到fileChannel
        fileChannel.write(byteBuffer);
        //关闭流
        fileOutputStream.close();
    }
}

  

  a-3. 数据从文件中读取或者写入到文件中

public class FileChannelNIO03 {
    public static void main(String[] args) throws IOException {
        //获取输入流
        FileInputStream fileInputStream = new FileInputStream("1.txt");
        //获取对应的channel
        FileChannel fileChannel1=fileInputStream.getChannel();
        //创建一个输出流
        FileOutputStream fileOutputStream=new FileOutputStream("2.txt");
        FileChannel fileChanne2= fileOutputStream.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        while (true){
            //重置标志位
          //  byteBuffer.clear();
            //循环读取
            int read = fileChannel1.read(byteBuffer);
            System.out.println(read);
            if (read==-1){//表示读完
                break;
            }
            //将buffer数据写入到fileChanne2
            byteBuffer.flip();
            fileChanne2.write(byteBuffer);
        }
        fileInputStream.close();
        fileOutputStream.close();


    }
}

 

 a-4. 使用transferFrom完成拷贝

/**
 * 使用transferFrom完成拷贝
 */
public class FileChannelNIO04 {
    public static void main(String[] args) throws IOException {
        //获取输入流
        FileInputStream fileInputStream = new FileInputStream("f:\\a.jpg");
        FileChannel fileChannel1=fileInputStream.getChannel();
        //创建一个输出流
        FileOutputStream fileOutputStream=new FileOutputStream("f:\\b.jpg");
        FileChannel fileChanne2= fileOutputStream.getChannel();
        //使用transferFrom完成拷贝,将a.jpg拷贝到f盘下,名字为b.jpg
        fileChanne2.transferFrom(fileChannel1,0,fileChannel1.size());
        fileChannel1.close();
        fileChanne2.close();
        fileInputStream.close();
        fileOutputStream.close();
    }
}

 

 a-5. ReadOnlyBuffer 只读模式

//只读
public class ReadOnlyBuffer {
    public static void main(String[] args) {
        //创建一个长度为5,类型为int的buffer
        ByteBuffer buffer= ByteBuffer.allocate(50);
        //向buffer存放数据
        for (int i = 0; i < buffer.capacity(); i++) {
            buffer.put((byte)i);
        }
        //读取转换
        buffer.flip();
        ByteBuffer readOnly=buffer.asReadOnlyBuffer();
        System.out.println(readOnly);
        while (readOnly.hasRemaining()){
            System.out.println(readOnly.get());
        }
    }
}

 a-6. MappedByteBuffer 让文件直接在内存(堆外的内存)中进行修改; 

/**
 *  MappedByteBuffer直接在堆外内存修改,操作系统不需要拷贝一次
 */
public class MappedByteBufferTest {
    public static void main(String[] args) throws IOException {
        /**
         * RandomAccessFile随机访问文件类
         * rw代表读写模式,不存在则新建
         */
        RandomAccessFile randomAccessFile=new RandomAccessFile("1.txt","rw");
        //获取对应的通道
        FileChannel fileChanne=randomAccessFile.getChannel();
        /**
         * 参数1:读写模式
         * 参数2:修改文件的起始位置
         * 参数3:映射到内存的大小,即文件的字节数
         */
        MappedByteBuffer map = fileChanne.map(FileChannel.MapMode.READ_WRITE, 0, 3);
        /**
         * 修改开始
         * 参数一:修改的起始位置
         * 参数二:该位置要修改成的数据
         */
        map.put(0,(byte)'H');
        randomAccessFile.close();
    }
}
 

未完待续...............

  

 

posted @ 2020-06-07 00:21  亲爸爸  阅读(226)  评论(0编辑  收藏  举报