NIO 初认识
参加工作今年也已经是第三个年头了. 这是我的第一次(第一次决定写博客). 走出学校后我一直从事的是后台web开发. 废话不多说, 直接进入正题吧.
NIO 到底是什么一个什么概念呢, 个人作为一个程序感觉还是蛮惭愧的, 在昨天之前, 我对这个名词居然完全不理解.
NIO(New IO) 即一套新的IO操作API , 可以替代标准的JAVA IO. 那么NIO 与标准的API相比较, 有那些区别的. 经过一天的学习, 我理解最深刻的一点区别就是NIO支持非阻塞式编程, 这个一个功能在网络编程中的应用, 可以很大提高服务器的工序效率., jdk 1.7之后引入了NIO相关的包(java.nio.channels)
1. NIO 中最主要的几个概念
1.1 通道 Channel
|-- FileChannel
|-- SocketChannel
|-- ServerSocketChannel
|-- DategramChannel
1.2 缓冲区 Buffer
|-- ByteBuffer
|-- CharBuffer
|-- IntBuffer
|-- LongBuffer
|-- FloatBuffer
|-- ShortBuffer
|-- DoubleBuffer
1.3 选择器 Selector
|-- Selector
2. 概念的介绍和使用:
2.1 通道 和 缓冲区
这里在使用的过程中, 通道和缓冲区是需要一起工作的, 所以在这里一起介绍了.
通道的作用, 打开目标起点和目标终点的链接. 缓冲区的作用, 用于存储数据, 在通过通道将缓冲区的数据运输到指定的接收端.
缓冲区, 可分为直接缓冲区, 和非直接缓冲区, 非直接缓存区可以有效的提高效率, 但是很多时候有不可控性.
直接缓冲区: 直接分配机器的物理内存 通过静态方法, allocateDirect(int size)
非直接缓冲区: 分配的是JVM中的内存 通过静态方法, allocate(int size)
1) FileChannel 文件通道, 这个通道的主要作用就是用于文件的操作.
常用API:
open: 用于打开文件通道
path: 用于描述文件地址
options: 指定打开文件的一些参数, 入只读模式, 读写模式等等. 是一个可变参数集
transferTo: 用于文件复制
position: 开始复制位置
count: 结束位置
target: 目标文件通道
transferFrom: 用于文件复制
src: 原文件通道
position: 开始复制位置
count: 结束位置
实现文件复制功能:
FileChannel inChannel = null;
FileChannel outChannel = null;
try
{
// 以读模式打开文件通道
inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);
// 以读写模式打开文件通道,
// StandardOpenOption.CREATE 文件存在替换, 不存在创建// StandardOpenOption.CREATE_NEW 文件存在报错, 不存在穿件
outChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);// 复制文件
inChannel.transferTo(0, inChannel.size(), outChannel);
// 或者
// outChannel.transferFrom(inChannel, 0, inChannel.size());
}
catch(IOException e)
{
e.printStackTrace();
} finally
{
if(null != inChannel)
{
try{inChannel.close();}catch(IOException e){e.printStackTrace();}
}
if(null != outChannel)
{try{outChannel.close();}catch(IOException e){e.printStackTrace();}
}
}
2. SocketChannel 和 ServerSocketChannel 用于TCP网络编程, 因为是非阻塞式的, 所以中间多了一选择器(Selector), 非阻塞实现的关键在这
实现简单的客户端服务端通信.
客户端实现:
SocketChannel socketChannel = null;
try{
// 打开通道
socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
// 配置通道未非阻塞方式
socketChannel.configureBlocking(false);
// 分配缓冲区
ByteBuffer buff = ByteBuffer.allocate(1024);
// 向缓冲区中放数据
buff.put("这是客户端发送的消息".getBytes());
// 切换未模式
nuff. flip();
// 通过通道发送
socketChannel.write(buff);
// 清空缓冲区
buff.clear();
} catch(Exception e){
e.printStackTrce();
} finally{
if(socketChannel != null){
try{socketChannel.close();}catch(Exception e){e.pringStackTrace();}
}
}
服务端代码实现:
ServerSocketChannel serverSocketChannel = null;
try{
// 打开通道
serverSocketChannel = ServerSocketChannel.open();
// 绑定端口
serverSocketChannel.bind(new InetSocketAddress(8080));
// 配置非阻塞模式
serverScoketChannel.configureBlocking(false);
// 获取选择器
Selector selector = Selector.open();
// 将通道注册到选择器上, 注册接收事件准备就绪
serverSocketChannel.register(selector, SelectionKey.OP_ACCACCEPT);
// 轮询选择器
while(selector.select() > 0){
// 获取选择器中已注册并准备就绪的选择键
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = it.next();
if(key.isAccpetable()){
// 获取客户端链接通道
SocketChannel socketChannel = serverSocketChannel.accept();
// 配置非阻塞
socketChannel.configureBlocking(false);
// 注册带选择器, 监听选择键为读
socketChannel.register(selector, SelectionKey.OP_READ);
}else if(key.isReadabel()){
SocketChannel channel = (SocketChannel) key.channel();
// 读取信息
ByteBuffer buf = ByteBuffer.allocate(1024);
int len = 0;
while((len = channel.read(buf)) != -1){
// 切换读模式(,非常需要注意这个, 不然无法读取到数据)
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
}
}
}
}
}catch(Exception e){
e.printStackTrace();
} finally{
if(null != serverSocketChannel){
try{serverSocketChannel,close();}catch(Exception e){e.printStackTrack();}
}
}
到这里非阻塞式的Tcp网络编程结束. 还有个DatagramChannel 用于UDP编程, 使用步骤大致和这里一致.
本文仅用作个人学习笔记使用.