NIO
1. Buffer
buffer 即缓冲区数组,用于存储数据
- Java提供的Buffer的实现类有:ByteBuffer
CharBUffer
ShortBuffer
…
Buffer有四个重要的属性:
mark | position | limit | capacity |
---|
- capacity –>最大容量,一旦声明不能改变。
- limit –>缓存区中可以操作数据的大小(limit后的数据不能读写)
- position –>缓冲区中正在操作数据的位置
Buffer可以是非直接缓冲区和直接缓冲区:
1. 非直接缓冲区
建立在JVM内存中,与本地磁盘读写需要通过两个缓冲区,copy耗时。
1. 通过allocate()获得
ByteBuffer buffer = ByteBuffer.allocate(1024);
2. 存入数据 put()
position –>length
buffer.put("asdasd".getBytes());
3. flip() 后读数据
limit –>position
position –>0
buffer.flip();
4. 取出数据 get()
position –>limit
buffer.get(数组);
5. rewind() 使用后重读
position –>0
6. clear() 清空缓冲区(但数据还在)
position –>0
limit –>capacity
7. mark() – reset()
- mark标记位置
- reset恢复位置
2. 直接缓冲区
建立在物理内存中(映射文件)
- 使用:长时间存在的数据
1. 效率提高
2. 内存消耗大,直到gc释放
1. 通过allocateDirect()获得
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
2. Channel
Channel即连接通道,负责buffer的运输
- FileChannel(file)、SocketChannel、ServerSocketChannel(tcp)、DatagramChannel(upd)
1. 获取通道
1. 由IO流提供getChannel()
- FileInputStream\FileOutStream
- RandomAccessFile
- Socket
- ServerSocket
- DatagramSocket
2. 针对各个通道提供的静态方法open()
//StandardOpenOption.READ读/WRITE写/CREATE_NEW没有则创建,有则报错/CREATE有则覆盖
FileChannel.open(Paths.get("文件地址"),StandardOpenOption.READ);
SocketChannel.open(new InetAddress(port));
ServerSocketChannel.open(new InetAddress(IP,port));
3. Files工具类的newByteChannel()
2. 传输(非直接)
- read() & write()
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(inChannel.read(buffer) != -1){
//buffer进入读模式
buffer.flip();
//写入通道
outChannel.write(buffer);
//清空准备下一次写入
buffer.clear();
}
- String对象的getBytes()方法提供byte[]数组
- 使用ByteBuffer提供的wrap方法生成一个Buffer
channel.write(ByteBuffer.wrap("ABCDE".getBytes()));
3. 传输(直接)
内存映射文件
<!-- 内存映射文件 -->
//读
MappedByteBuffer inMappedBuffer = inChannel.map(MapMode.READ_ONLY模式,0从,inChannel.size()长度);
//写
MappedByteBuffer outMappedBuffer outChannel.map(MapMode.READ_WRITE,0,inChannel.size());
//操作 -->直接通过缓冲区读写
byte[] dst = new byte[inMappedBuffer.limit()];
inMappedBuffer.get(dst);
outMappedBuffer.put(dst);
//关闭通道
4. 通道间传输
- transferFrom()
- transferTo()
<!-- 直接缓冲区 -->
inChannel.transferTo(position,count,target)
outChannel.transferFrom(src,position,count)
5. 分散读取(Scatter)与聚集写入 (Gather)
- Scatter 将通道中的数据分散到各个缓冲区
- Gather 将多个缓冲区的数据聚集到通道中
buffer1
buffer2
ByteBuffer[] bufs = {buffer1,buffer2};
channel.read(bufs);
for->flip()
channel.write(bufs);
6. Charset字符集
- 编码:字符串->字节数据
- 解码:字节数据->字符串
//字符集
Map<String,Charset> map = Charset.availbaleCharset();
Set<Entry<String,Charset>> set = map.entryset
for(Entry<String,Charset> entry:set){
entry.key;
entry.value;
}
Charset cs = Charset.forName("utf-8");
//编码器
CharsetEncoder ce = cs.newEncoder();
//解码器
CharsetDecoder cd = cs.newDecoder();
//缓冲区
CharBuffer charBuffer = CharBuffer.allocate(1024);
charBuffer.put("abcde");
//编码:char --> byte
charBuffer.flip();
ByteBuffer byteBuffer = ce.encode(charBuffer);
//解码:byte --> char
byteBuffer.flip();
CharBuffer buffer = cd.decode(byteBuffer)
3. 非阻塞
传统IO阻塞式,即便多线程也无法充分利用资源
1. 选择器Selector
- 把每一个通道都注册到选择器上
- 选择器监控这些通道的IO状况
2. 通道Channel
SelectableChannel:
- SocketChannel
- ServerSocketChannel
- DatagramChannel
- Pipe.SinkChannel
- Pipe.SourceChannel
3. 客户端Client
- 获取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("IP",port));
- 切换到非阻塞模式
sChannel.configureBlocking(false);
- 注册读事件
Selector selector = Selector.open();
sChannel.register(selector,SeletionKey.OP_READ);
- 轮询选择器,接收来自服务端的消息
while (true) {
while (mSelector.select() > 0) {
Iterator<SelectionKey> iterator = mSelector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isReadable()) {
//处理信息
handler.read(key);
}
}
}
}
- 新开线程进行写操作
new Thread(() -> {
//开始(写的)阻塞循环
Scanner scanner = new Scanner(System.in);
while (true) {
try {
sChannel.write(...);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
4. 服务端Server
- 获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
- 切换非阻塞模式
ssChannel.configureBlocking(false);
- 绑定连接
ssChannel.bind(new InetSocketAddress(port));
- 获取选择器
Selector selector = Selector.open();
- 注册通道到选择器上
ssChannel.register(selector,ops/*监控状态*/);
<!-- SelectionKey -->
SelectionKey{
OP_READ
OP_WRITE
OP_CONNECT
OP_ACCEPT
}
这里填写的是OP_ACCEPT,是为了监听来自SocketChannel的连接
6. 轮询式获取选择器上已经准备就绪的事件
while(true){
while(selector.select() > 0){
//返回所有事件的集合
Iterator<SelectionKey> it = selector.selectedKey().iterator();//获取迭代器
//迭代获取
while(it.hasNext()){
SelectionKey key = it.next();
iterator.remove();
<!-- 接收 -->
if(key.isAcceptable()){
//获得通道
SocketChannel sChannel = ((ServerSocketChannel) key.channel()).accept();
//切换非阻塞模式
sChannel.configureBlocking(false);
//注册,监听读事件
sChannel.register(selector,SelectionKey.OP_READ);
}
<!-- 读 -->
if(key.isReadable()){
read(key);
}
}
}
}
- 处理信息并轮流发到各个通道。