java非阻塞式IO NIO 初步认识

来源:互联网 发布:lol末日人工智能投票 编辑:程序博客网 时间:2024/05/14 09:32

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编程, 使用步骤大致和这里一致.

本文仅用作个人学习笔记使用. 

原创粉丝点击