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("你好"));}
}