Java NIO 基础
- 1. NIO 介绍
- 2. NIO 三大组件
- 2.1 Channel
- 2.1.1 常见的 Channel
- 2.1.2 常用方法
 
- 2.2 Buffer
- 2.2.1 常见的 Buffer
- 2.2.2 重要属性
- 2.2.3 常用方法
 
- 2.3 Selector
- 2.3.1 四种事件类型
 
 
1. NIO 介绍
NIO(non-blocking io):非阻塞IO,JDK1.4 引入。
2. NIO 三大组件
2.1 Channel
Channer是读写数据的双向通道,类似于传统IO中的Stream,但是Stream只能单向操作。如:InputStream只能读操作,OutputStream只能写操作。
2.1.1 常见的 Channel
- FileChannel:文件IO通道,用于文件的读写
- DatagramChannel:UDP协议数据报通信
- SocketChannel:网络套接字IO通道,TCP协议客户端
- ServerSocketChannel:网络套接字IO通道,TCP协议服务端
2.1.2 常用方法
- read(ByteBuffer buffer):从Channel中读取到ByteBuffer中,如果Channel中没有数据会一直堵塞到可读
- read(ByteBuffer buffer,Long timeout):从Channel中读取到ByteBuffer中,超过时间会报错
- write(ByteBuffer buffer):将数据写到Channel中,如果Channel中没有可写空间会一直堵塞到可写
- write(ByteBuffer buffer, Long timeout):将数据写到Channel中,超过时间会报错
- flush():将Channel中缓冲区数据刷到底层设备
- register(Selector selector, SelectionKey key):将Channel注册到Selector
- configureBlocking(boolean b):设置Channel是否为阻塞模式
- socket():获取底层的Socket对象
- isConnected():Channel是否已经连接上
- isReadable():Channel是否可读
- isWriteable():Channel是否可写
- getRemoteAddress():Channel对应的远程地址
- getLocalAddress():Channel对应的本地地址
- open():Channel是否打开
2.2 Buffer
Buffer:缓冲读写数据,每一个Buffer对象关联一个字节数组。
 
2.2.1 常见的 Buffer
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
2.2.2 重要属性
- capacity:Buffer所占的内存大小,设置后不能修改
- limit:Buffer中可以操作的数据大小
- position:下一个要读/写的数据索引
- mark:标记当前position的位置,可以通过reset()把position恢复到mark位置
2.2.3 常用方法
- capacity():返回capacity的值
- limit():返回limit的值
- limit(int n):设置limit的值
- position():返回position的值
- position(int n): 设置position的值
- mark():对Buffer做标记
- reset():把position恢复到mark位置
- rewind():设置position为0,取消mark标记
- hasRemaining():判断Buffer中是否有元素
- get():从Buffer中读取一个字节
- get(byte[] b):从Buffer中读取多个字节
- get(int index):从Buffer中读取指定索引位的字节
- put(byte b):往Buffer中存一个字节
- put(byte[] b):往Buffer中存多个字节
- put(int index, byte b):往Buffer指定索引位存字节
- clear():清空Buffer
- compact():清空position之前的字节
- flip():将limit设置为position的值,position设置为0
2.3 Selector
Selector配合一个线程管理多个Channel,获取Channel上发生的事件
2.3.1 四种事件类型
- OP_CONNECT:连接成功后(只客户端使用)
- OP_ACCEPT:客户端请求连接时(只服务端使用)
- OP_READ:读缓冲区有可读数据时
- OP_WRITE:写缓冲区有可写空间时
  
package com.learn.wesay;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.nio.charset.StandardCharsets;
import java.util.Iterator;public class Server {public static void main(String[] args) throws IOException {// 创建Selector管理多个ChannelSelector selector = Selector.open();// 创建服务端套接字通道ServerSocketChannel server = ServerSocketChannel.open();// 设置为非阻塞server.configureBlocking(false);// 绑定端口server.bind(new InetSocketAddress(8888));// Channel注册到Selector,只处理accept事件server.register(selector, SelectionKey.OP_ACCEPT);while (true) {// 没有事件发生线程阻塞,有事件线程才会恢复运行selector.select();// 所有发生的事件Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey selectionKey = iterator.next();if (selectionKey.isAcceptable()) {handlerAccept(selectionKey, selector);}if (selectionKey.isReadable()) {handlerRead(selectionKey, selector);}iterator.remove();}}}private static void handlerAccept(SelectionKey selectionKey, Selector selector) {ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();try {SocketChannel channel = server.accept();channel.configureBlocking(false);channel.register(selector, SelectionKey.OP_READ);System.out.println(channel);} catch (IOException e) {throw new RuntimeException(e);}}private static void handlerRead(SelectionKey selectionKey, Selector selector) {SocketChannel channel = (SocketChannel) selectionKey.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);try {int read = channel.read(byteBuffer);if (read == -1) {// 客户端socket正常断开selectionKey.cancel();} else {byteBuffer.flip();System.out.println(StandardCharsets.UTF_8.decode(byteBuffer));}} catch (IOException e) {// 客户端socket异常断开selectionKey.cancel();}}
}public class Client {public static void main(String[] args) throws IOException {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 8888));socketChannel.write(Charset.defaultCharset().encode("你好"));}
}