原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。 面向流 的 I/O 系统一次一个字节地处
理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。 一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的I/O 所具有的优雅性和简单性。

 

NIO主要原理和适用。

        NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。

        Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容。

 

服务器端:

package cn.com.testnio.base;

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.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * NIO是面向缓冲区的
 * User: lihaoquan
 */
public class Server {

    private Selector selector;
    /**
     * 值得注意的是 Buffer 及其子类都不是线程安全的。
     */
    private ByteBuffer readBuffer = ByteBuffer.allocate(1024);//设置缓冲区大小

    /**
     * ConcurrentHashMap是支持并发的,所以这里不使用HashMap
     */
    private Map<SocketChannel,byte[]> clientMessage
            = new ConcurrentHashMap<SocketChannel,byte[]>();

    /**
     * 启动开关
     * @throws IOException
     */
    public void start() throws IOException {
        //通过静态工厂生产服务端的channel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //设置非阻塞方式
        serverSocketChannel.configureBlocking(false);
        //绑定IP
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));
        //创建选择器
        selector = Selector.open();
        //注册监听事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (!Thread.currentThread().isInterrupted()) {
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = keys.iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                if(!key.isValid()) {
                    continue;
                }
                if(key.isAcceptable()) {
                    accept(key);
                }else if(key.isReadable()) {
                    read(key);
                }
                //去除本次keyIterator.next()的对象,但不会对下次遍历有影响
                keyIterator.remove();
            }
        }
    }

    /**
     * 读入
     * @param key
     * @throws IOException
     */
    public void read(SelectionKey key) throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
        this.readBuffer.clear();
        int numRead;
        try {
            /**
             * 获取客户点的read操作读入数据块数量
             */
            numRead = socketChannel.read(readBuffer);
        }catch (Exception e) {
            e.printStackTrace();
            key.cancel();
            socketChannel.close();
            clientMessage.remove(socketChannel);
            return;
        }

        byte[] bytes = clientMessage.get(socketChannel);
        if(bytes == null) {
            bytes = new byte[0];
        }
        if(numRead > 0) {
            byte[] newBytes  =new byte[bytes.length+numRead];
            System.arraycopy(bytes,0,newBytes,0,bytes.length);
            System.arraycopy(readBuffer.array(), 0, newBytes, bytes.length, numRead);
            clientMessage.put(socketChannel, newBytes);
            System.out.println("message>>>"+new String(newBytes));
        }else {
            String message = new String(bytes);
            System.out.println("data>>>"+message);
        }
    }

    /**
     * 接收
     * @param key
     * @throws IOException
     */
    public void accept(SelectionKey key) throws IOException {
        ServerSocketChannel serverSocketChannel
                = (ServerSocketChannel) key.channel();
        SocketChannel clientChannel = serverSocketChannel.accept();
        clientChannel.configureBlocking(false);
        clientChannel.register(selector,SelectionKey.OP_READ);
        System.out.println("已成功连接新的客户端......");
    }

    public static void main(String[] args) throws Exception{
         new Server().start();
    }
}

 

 

客户端

package cn.com.testnio.base;

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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * User: lihaoquan
 */
public class Client {

    /**
     * 启动开关
     * @throws IOException
     */
    public void start() throws IOException {
        //通过静态工厂生产客户端的channnel
        SocketChannel socketChannel = SocketChannel.open();
        //设置客户端请求为非阻塞方式
        socketChannel.configureBlocking(false);
        //绑定IP
        socketChannel.connect(new InetSocketAddress(8080));
        //创建选择器
        Selector selector = Selector.open();
        //注册监听事件
        socketChannel.register(selector, SelectionKey.OP_CONNECT);
        //键盘输入
        Scanner scanner = new Scanner(System.in);
        while (true) {
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = keys.iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                //去除本次keyIterator.next()的对象,但不会对下次遍历有影响
                keyIterator.remove();
                if(key.isConnectable()) {
                    socketChannel.finishConnect();
                    socketChannel.register(selector,SelectionKey.OP_WRITE);
                    System.out.println("客户端已经连上服务器端!!");
                    break;
                }else if(key.isWritable()) {
                    System.out.println("请输入信息!!");
                    String message = scanner.nextLine();
                    ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes());
                    socketChannel.write(writeBuffer);
                }
            }
        }
    }

    public static void main(String[] args) throws Exception{
        new Client().start();
    }
}