NIO核心的三大组件
  • Channels
  • Buffers
  • Selectors
NIO都是从一个channel开始,数据可以从channel 写入到 buffer ,也可以从buffer 写入到 channel

Channel
  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel
socketChannel 的非阻塞模式
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
while(! socketChannel.finishConnect() ){
    //wait,&nbs***bsp;do something else...
}

serverSocketChannel 的非阻塞模式
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
while(true){
    SocketChannel socketChannel = serverSocketChannel.accept();
    if(socketChannel != null){
        //do something with socketChannel...
    }
}
ServerSocketChannel可以设置成非阻塞模式。在非阻塞模式下,accept() 方***立刻返回,如果还没有新进来的连接,返回的将是null。 因此,需要检查返回的SocketChannel是否是null

datagramChannel    UDP在数据传送方面没有任何保证
接收数据
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);
发送数据
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80));

Buffer
  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer
  • MappedByteBuffer (内存映射文件)
buffer的基本使用
  1. 写入数据到buffer
  2. 调用 flip 方法从写模式切换到读模式
  3. 从buffer中读取数据
  4. 调用clear 或者 compact方法 清除数据 , clear是清除所有数据,compact是清除已读数据,未读的数据会移动到buffer起始位置
allocate 分配缓冲区
rewind 将position置为0 ,重新读取数据
mark  标记一个指定的position
reset  将position 重置回到mark标记的位置

Selector
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方***一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件
Selector selector = Selector.open()  创建选择器
向selector 注册通道
channel.configureBlocking(false);
SelectionKey key = channel.register(selector,Selectionkey.OP_READ);
与 selector 一起使用,channel必须是非阻塞的,这意味着 FileChannel 不能和 selector 一起使用,
第二个参数 是 interest 集合,标识监听的感兴趣的事件
  • SelectionKey.OP_CONNECT    连接就绪
  • SelectionKey.OP_ACCEPT   接收就绪
  • SelectionKey.OP_READ  读就绪
  • SelectionKey.OP_WRITE  写就绪
如果对多个感兴趣,SelectionKey.OP_READ | SelectionKey.OP_WRITE;
SelectionKey
注册完之后就会返回一个selectionKey对象,
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept  = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead    = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite   = interestSet & SelectionKey.OP_WRITE;
ready集合
selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();
附加对象
selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();
监听就绪事件
Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.
    } else if (key.isConnectable()) {
        // a connection was established with a remote server.
    } else if (key.isReadable()) {
        // a channel is ready for reading
    } else if (key.isWritable()) {
        // a channel is ready for writing
    }
    keyIterator.remove();
}
注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。





分散和聚集 (Scatter / Gather
分散就是一个channel 写到 多个buffer 
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);
聚集就是多个buffer 写入同一个 channel

通道之间数据传输
transferFrom
transferTo

管道  Pipe

向管道写数据,需要用到sinkChannel
Pipe.SinkChannel sinkChannel = pipe.sink();
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
    sinkChannel.write(buf);
}
从管道读取数据,需要用到SourceChannel
Pipe.SourceChannel sourceChannel = pipe.source();
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = sourceChannel.read(buf);