NIO核心知识(区别、Channel、Buffer、Selector、SelectionKey、完整代码案例)

来源:互联网 发布:人和卫星电视直播软件 编辑:程序博客网 时间:2024/05/17 19:20
NIO核心知识

注:图片转载于并发编程网,链接:http://ifeve.com/。

0、IO和NIO的区别
①Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。
②Java IO的各种流是阻塞的。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。
③Java NIO还提供Selector选择器,使得单线程更容易管理多个连接(多个通道的多个监听事件)。



1、Channel
Channel:翻译为通道,是一个接口,提供通道的实现规范。
作用:一个用于输入/输出操作的连接。通道表示一个实体的开放连接,例如硬件设备、文件、网络套接字或可以使用的程序组件执行一个或多个不同的输入/输出操作,例如读取或写操作。一般来说,通道是安全的,用于多线程访问。
实现类:FileChannel、SocketChannel、ServerSocketChannel、DatagramChannelChannel。

核心方法:
服务端
//打开通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
客户端
//打开通道
SocketChannel socketChannel = SocketChannel.open();



2、Buffer
Buffer:缓存区,是一个抽象类,提供实现不同数据类型缓存区的实现规范。
作用:Buffer用于与NIO通道进行交互,数据是从通道读入到缓冲区,从缓冲区写入通道的。
实现类:ByteBuffer、MappedByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer。

核心方法:
ByteBuffer byteBuffer = ByteBuffer.allocate(buffer_size);
byteBuffer.put(byteArray);

核心属性:
position:当前游标
limit:游标限制
capacity:容量


查看源码
//将position设回0,可以重读Buffer中的所有数据。
public final Buffer rewind() {    position = 0;    mark = -1;    return this;}
//clear方法将缓冲区清空,在重新写缓冲区时调用。
public final Buffer clear() {    position = 0;    limit = capacity;    mark = -1;    return this;}
//反转缓冲区,在准备从缓冲区中读取数据时调用flip方法。
public final Buffer flip() {    limit = position;    position = 0;    mark = -1;    return this;}





3、Selector
Selector是通道监测器,能监测多个通道(Channel)的多个监听事件。

核心方法:
//获取通道监测器
Selector selector = Selector.open();
//监听
select();//阻塞到至少有一个通道在你注册的事件上就绪了。
select(long timeout);//和select()一样,除了最长会阻塞timeout毫秒(参数)。
selectNow();不会阻塞,不管什么通道就绪都立刻返回。




4、SelectionKey
监听事件集合。

核心属性:
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;

核心方法:
//通道注册监听事件到通道监测器
channel.register(selector, SelectionKey.OP_READ);
//如何注册多个监听事件呢?
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//轮询就绪事件
while(iterator.hasNext()) {SelectionKey selectionKey = iterator.next();//略...iterator.remove();}



5、完整代码案例

具体步骤:
1、服务端进入监听状态
2、客户端连接服务端,连接成功后向服务端发送消息
3、服务端接受到数据后,向客户端发送消息

服务端:
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.util.Iterator;/*** @author 周杰* @time 2017年10月25日 下午2:53:37*/public class Server {private static final int port = 12345;private static final int buffer_size = 1024;private static final String charsetName = "UTF-8";private static final String hello_message = "hello client !";public static void main(String[] args) throws Exception {//获取通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.bind(new InetSocketAddress(port));//获取通道管理器Selector selector = Selector.open();//将通道注册通道管理器的OP_ACCEPT事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while(true) {//当有注册事件到达时,方法返回,否则阻塞selector.select();Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()) {SelectionKey selectionKey = iterator.next();if(!selectionKey.isValid()) {continue;}if(selectionKey.isAcceptable()) {System.out.println("accept a connection...");ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();SocketChannel sc = ssc.accept();sc.configureBlocking(false);sc.register(selector, SelectionKey.OP_READ);}else if(selectionKey.isReadable()) {System.out.println("accept a message...");SocketChannel sc = (SocketChannel) selectionKey.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(buffer_size);int length = sc.read(byteBuffer);if(length > 0) {String receive_message = new String(byteBuffer.array(), charsetName);System.out.println("server accept : "+receive_message);sc.write(ByteBuffer.wrap(hello_message.getBytes(charsetName)));}}iterator.remove();}}}}




客户端:
import java.net.InetAddress;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;/*** @author 周杰* @time 2017年10月25日 下午3:10:49*/public class Client {private static final int port = 12345;private static final int buffer_size = 1024;private static final String charsetName = "UTF-8";private static final String hello_message = "hello server !";public static void main(String[] args) throws Exception {//获取通道SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);//获取通道管理器Selector selector = Selector.open();//连接socketChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));//将通道注册通道管理器的OP_CONNECT事件socketChannel.register(selector, SelectionKey.OP_CONNECT);while(true) {selector.select();for (SelectionKey key : selector.selectedKeys()) {if(!key.isValid()) {continue;}if(key.isConnectable()) {System.out.println("connect successfully...");SocketChannel sc = (SocketChannel) key.channel();if(sc.isConnectionPending()) {sc.finishConnect();}sc.register(selector, SelectionKey.OP_READ);sc.write(ByteBuffer.wrap(hello_message.getBytes(charsetName)));}else if(key.isReadable()) {System.out.println("accept a message...");SocketChannel sc = (SocketChannel) key.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(buffer_size);int length = sc.read(byteBuffer);if(length > 0) {String receive_message = new String(byteBuffer.array(), charsetName);System.out.println("client accept : "+receive_message);}}}}}}


阅读全文
0 0
原创粉丝点击