Java NIO知识总结

记博客的目的:知识只有发布,才会让自己记得更牢固,才便于自己回头来巩固提高!

参考:

https://www.cnblogs.com/shihuc/p/6559025.html

https://www.cnblogs.com/wangyang108/p/6031013.html


NIO--new IO  no BlockingIO ----非阻塞式IO----基于通道和缓冲区

1BUFFER缓冲区

2:Channel 通道

3:Selector----选择器

优点

1:能够进行数据的双向传输-----减少了流的数量,降低服务器的内存消耗。

2. 数据存储在缓冲区。可以针对 缓冲区的数据 做定向操作!!

  文件头,文件尾巴。大视频切成多个小视频,将一个视频文件切开后,没法播放。

3.利用一个或者少量的服务器完成大量的用户的请求处理(一个服务端,多个客户端------适用于短任务场景,比如聊天。  

Channel的比重最大,NIO的功能主要基于Channel来实现,进行业务逻辑操作。

Selector主要是IO事件选择器,当一个Channel创建并配置好后,注册到Selector上,与Selector相关的重要概念是SelectionKey,这个上面绑定了IO事件相关的Channel。在获取到Channel后,进行数据的读写操作,Channel的数据读写是不能直接操作数据的,必须基于ByteBuffer进行,然而,Java NIO原生的ByteBuffer操作比较繁琐,要flip和clear操作。

1. 而我们在业务逻辑操作中,用到的channel,主要有ServerSocketChannel,SocketChannel

2. Selector,是事件选择器,创建Selector后,在调用select之前,在注册Channel到这个Selector上时,必须指定关注的事件类型(interestOps)。通过这个类的select函数,可以获取选择上监听到的IO事件。一旦select函数检测到事件,就可以从Selector上获取到具体有哪些IO事件,这些事件通过SelectionKey承载,SelectionKey上标记出该事件的类型,比如是OP_CONNECT,OP_ACCEPT还是OP_READ等。另外,SelectionKey还记录了对应该IO事件发生的Channel,可以通过SelectionKey得到该Channel。

3. ByteBuffer。 因为字节操作,是操作系统与IO设备之间进行通信的基本数据单元,在Java NIO中,各通道Channel之间进行数据通信时,指定必须是基于ByteBuffer的。 ByteBuffer有两个重要的函数,flip和clear。当Channel调用read函数,将数据读到ByteBuffer中后,ByteBuffer的数据长度指针将会移动到数据长度所在的位置,这个位置是小于等于ByteBuffer容量capacity值的。当业务逻辑操作读取到的数据前,需要对ByteBuffer做一下flip操作,就是将limit指针指向当前数据指针position的位置,然后,将position指针指向0的位置。数据逻辑结束后,一般要恢复ByteBuffer,即调用clear函数。

缓冲区: 在操作系统中缓冲区是为了解决CPU的计算速度和外设输入输出速度不匹配的问题,因为外设太慢了,如果没有缓冲区,那么CPU在外设输入的时候就要一直等着,就会造成CPU处理效率的低下,引入了缓冲之后,外设直接把数据放到缓冲中,当数据传输完成之后,给CPU一个中断信号,通知CPU:“我的数据传完了,你自己从缓冲里面去取吧”。如果是输出也是一样的道理。

通道: 那么通道用来做什么呢?其实从他的名字就可以看出,它就是一条通道,您想传递出去的数据被放置在缓冲区中,然后缓冲区中怎么从哪里传输出去呢?或者外设怎么把数据传输到缓冲中呢?这里就要用到通道。它可以进一步的减少CPU的干预,同时更有效率的提高整个系统的资源利用率,例如当CPU要完成一组相关的读操作时,只需要向I/O通道发送一条指令,以给出其要执行的通道程序的首地址和要访问的设备,通道执行通道程序便可以完成CPU指定的I/O任务。

选择器: 另外一项创新是选择器,当我们使用通道的时候也许通道没有准备好,或者有了新的请求过来,或者线程遇到了阻塞,而选择器恰恰可以帮助CPU了解到这些信息,但前提是将这个通道注册到了这个选择器。

下面项目代码 展示的功能意义:
一个服务器,处理多个客户端的请求操作.
package com.edu.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
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.Set;

public class ServerDemo {

	public static void main(String[] args) throws IOException {
		//打开服务器端的通道
		ServerSocketChannel ssc=ServerSocketChannel.open();
		//绑定端口号
		ssc.bind(new InetSocketAddress(8091));
		// 将通道设置为非阻塞
		ssc.configureBlocking(false);
		
		// 创建一个selector
		Selector selc = Selector.open();
		//将创建的serverChannel注册到selector选择器上,指定这个channel只关心OP_ACCEPT事件

		ssc.register(selc,SelectionKey.OP_ACCEPT);
		
		while(true){
			/*
             * select()操作,默认是阻塞模式的,即,当没有read时间到来时,将一直阻塞不往下面继续执行。
             *
             */
			//进行选择
			selc.select();
			//将选择后事件获取出来
			Set<SelectionKey> keys = selc.selectedKeys();
			//获取到迭代器
			Iterator<SelectionKey> it = keys.iterator();
			while(it.hasNext()){
				//获取事件
				SelectionKey key = it.next();
				//可能是一个接受事件
				if(key.isAcceptable()){
					//从事件中获取到通道
					ServerSocketChannel sscx = (ServerSocketChannel)key.channel();
					//接收连接
					SocketChannel sc = sscx.accept();
					while(sc==null){
						sc=sscx.accept();
					}
					sc.configureBlocking(false);
					sc.register(selc, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
				}
				//可能是一个可读事件
				if(key.isReadable()){
					//获取通道
					SocketChannel sc =(SocketChannel)key.channel();
					// 准备缓冲区
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					// 读取数据
					sc.read(buffer);
					// 解析数据
					buffer.flip();
					System.out.println(new String(buffer.array(),0,buffer.limit()));
					sc.register(selc,key.interestOps() & ~SelectionKey.OP_READ);
			
				}
				//可能是一个可写事件
				if(key.isWritable()){
					// 从事件身上获取通道
					SocketChannel sc = (SocketChannel) key.channel();
					//写出数据
					sc.write(ByteBuffer.wrap("读取成功~~~".getBytes()));
					sc.register(selc, key.interestOps()& ~SelectionKey.OP_WRITE);
				}
				it.remove();
			}
		}
	}

}
package com.edu.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class ClientDemo {

	public static void main(String[] args) throws Exception{
		// 创建一个通道
		SocketChannel sc=SocketChannel.open();
		//设置为   非阻塞 的
		sc.configureBlocking(false);
		//将创建的channel连接到指定的服务地址
		sc.connect(new InetSocketAddress("localhost", 8091));
		
		//创建一个事件选择器  Selector
		Selector selc=Selector.open();
		// 将创建的SocketChannel注册到指定的Selector上,并指定关注的事件类型为OP_CONNECT
		sc.register(selc,SelectionKey.OP_CONNECT);
		
		//无线循环体
		while(true){
			//监听创建的Selector选择器
			selc.select();
			//获取筛之后有用的事件
			Set<SelectionKey> keys = selc.selectedKeys();
			//获取迭代器
			Iterator<SelectionKey> it = keys.iterator();
			while(it.hasNext()){
				//将遍历到的这个事件获取出来
				SelectionKey key = it.next();
				//向服务器发起连接
				if(key.isConnectable()){
					//从该事件获取对应的通道
					SocketChannel scx=(SocketChannel) key.channel();
					
					//判断连接是否成功
					while(!scx.finishConnect())
						;
					scx.register(selc, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
				}
				//向服务区写数据
				if(key.isWritable()){
					//从事件身上获取到通道
					SocketChannel scx = (SocketChannel) key.channel();
					
					//写数据
					scx.write(ByteBuffer.wrap("Hello".getBytes()));
					scx.register(selc, key.interestOps() & ~SelectionKey.OP_WRITE);
					
				}
				// 可能从服务器读取数据
				if(key.isReadable()){
					//从事件身上获取到通道
					SocketChannel scx = (SocketChannel) key.channel();
					//定义一个ByteBuffer的容器,容量为1k
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					scx.read(buffer);
					buffer.flip();
					System.out.println(new String(buffer.array(),0,buffer.limit()));
					scx.register(selc,key.interestOps() & ~SelectionKey.OP_READ);
				}
				it.remove();
			}
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值