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();不会阻塞,不管什么通道就绪都立刻返回。
监听事件集合。
核心属性:
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 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);
}
}
}
}
}
}