NIO的简单应用

来源:互联网 发布:淘宝装修定时发布 编辑:程序博客网 时间:2024/05/21 11:06

    NIO的简单应用

      NIO的创建目的是为了让Java程序员可以实现高速I/O而无需编写自定义的本机代码,NIO将最耗时的I/O操作(即填充和提取缓冲 区)转移到操作系统,因而可以极大地提高速度。

     原来的I/O库与NIO的最大的区别就是数据打包和传输的方式。原来的I/O以流的形式处理数据,而NIO以块的形式处理数据。

    通道和缓冲区

     通道和缓冲区是NIO中的核心对象,几乎在每一个I/O操作中都需要使用它们。

     通道是对原I/O中的流的模拟,到任何目的地的所有数据都必须通过一个Channel 对象,一个Buffer实质上是一个容器对象,发送给一个通道的所有对象都必须首先放到缓冲区中,同样地,从通道中读取的任何数据都要读到缓冲区中。

     缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。

     通道是一个Channel对象,可以通过它读取和写入数据。拿NIO与原来的I/O做比较,通道就像是流。跟操作系统中的通道相像,都是双向的。

     一个缓冲区的例子:

    

package com.nio;import java.nio.ByteBuffer;public class ByteBufferTest{public static void main(String[] args) throws Exception{ByteBuffer buffer = ByteBuffer.allocate(265);while (true){// 开始读取数据int c = System.in.read();// 读完输入流,就退出循环if (c == 1)break;// 把读到的数据放入缓冲区ByteBuffer中buffer.put((byte) c);if (c == '\n'){// buffer重置position的位置,方便下面的写入操作buffer.flip();byte[] content = new byte[buffer.limit()];// 把buffer中内容写入content中,这里需要使用byte数组buffer.get(content);System.out.println(new String(content));buffer.clear();}}}}

      缓冲区有很多基本的数组类型,ByteBuffer只是其中一个。

     三个状态变量:

     Position

         缓冲区数组的下标,相当于普通数组的下标,这里区别有所区别。

     Limit

        limit变量表示还有多少数据需要取出,或者还有多少数据可以存放,position总是小于limit。

     Capacity

         缓冲区的capacity表明我们可以存储在缓冲区的最大数据量。    

      一个通道的例子:

    

package com.nio;import java.io.PrintWriter;import java.net.InetAddress;import java.net.InetSocketAddress;import java.net.Socket;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.spi.SelectorProvider;import java.util.Date;import java.util.Iterator;import java.util.Set;public class ChannelTest{public static void accpetConnection(int port) throws Exception{// 打开一个SelectorSelector accept = SelectorProvider.provider().openSelector();// 创建一个ServerSocketChannel,并且打开绑定的状态,这样可以进行非阻塞的IO操作。ServerSocketChannel ssc = ServerSocketChannel.open();ssc.configureBlocking(false);// 将IP地址和端口绑定到ServerSocketChannel去。InetAddress address = InetAddress.getLocalHost();InetSocketAddress socketAddress = new InetSocketAddress(address, port);ssc.socket().bind(socketAddress);// 将ServerSocketChannel注册到Selector。ssc.register(accept, SelectionKey.OP_ACCEPT);int keyAdd = 0;// 查看Selector上的监控的channel(通道)的个数。while ((keyAdd = accept.select()) > 0){// 每个 SelectionKey 代表了一个可以进行 IO 操作的 channel。Set<?> readKey = accept.selectedKeys();// 遍历每个可以操作的channel,利用Socket进行TCP连接,并返回当前时间 。Iterator<?> iter = readKey.iterator();while (iter.hasNext()){SelectionKey sk = (SelectionKey) iter.next();iter.remove();ServerSocketChannel nextReady = (ServerSocketChannel) sk.channel();Socket s = nextReady.accept().socket();PrintWriter out = new PrintWriter(s.getOutputStream(), true);Date now = new Date();out.println(now);out.close();}}}}

       通道的用法属于异步的I/O操作,异步的I/O操作不会导致阻塞,原理就是通过一个线程来监听数据的输入,当用户有数据输入时,则调用监听本Channel的线程来处理这些数据,比如说连接。TCP连接过来时,则调用Socket.accept()方法进行处理。

    

    

    

   

原创粉丝点击