NIO的核心:缓冲区,通道,选择器
Table of Contents
一、缓冲区 Buffer :存储数据
1 基本概念:
2 直接缓冲区和非直接缓冲区
3 种类:基本数据类型处理boolean外都有
4 常用方法
二、管道channel : 传输数据
1 概念
2 通道的主要实现类
3 获取的三种方式
例子:通过非直接缓冲区完成文件复制
通过直接缓冲区完成文件复制(内存映射文件)
4 通道之间传输 transferTo和 transferFrom
5 分散(scatter)和聚集(Gather)
6 字符集charset
三、阻塞io和非阻塞io
1 阻塞io,就是服务端一直等待客户端的请求,不能去做其他的事情
2 示例
实例一
示例二
2 非阻塞io,服务端可以在选择器上监控已经准备就绪的事件处理,不用一直等待
示例一
示例二
四、管道:
1 position :当前操作数据的位置,指针位置
2 limit:缓冲区中可以操作数据的大小,即limit后面的数据不能操作
3 capacity:缓冲区的存储大小
非直接缓冲区(allocate())方法分配的缓冲区,将缓冲区直接建立在jvm上面
直接缓冲区:allocateDirect()方法分配的缓冲区,将缓冲区直接建立在磁盘内存中,可以提高效率,弊端是消耗大,不安全
ByteBuffer.allocateDirect(1024); ByteBuffer allocate = ByteBuffer.allocate(1024);
ByteBuffer
CharBufferShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
常用方法: put 存储数据 get 获取数据 flip:转换为读取模式,将指针位置置于开始部位可以读或者写操作 clear 清空位子,数据还在 mark 标记此刻数据的位子 reset 回到mark标记的数据位子
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
ByteBuffer allocate = ByteBuffer.allocate(1024);
System.out.println(allocate.position());
System.out.println(allocate.limit());
System.out.println(allocate.capacity());
//存数据
String str = "abcde";
allocate.put(str.getBytes());
System.out.println("*************put***********");
System.out.println(allocate.position());
System.out.println(allocate.limit());
System.out.println(allocate.capacity());
System.out.println("******************flip************");
allocate.flip();//切换为读数据模式
System.out.println("*************put***********");
System.out.println(allocate.position());
System.out.println(allocate.limit());
System.out.println(allocate.capacity());
System.out.println("**********get***************");
byte[] dest = new byte[allocate.limit()];
allocate.get(dest);
System.out.println(new String(dest,0,dest.length));
System.out.println(allocate.position());
System.out.println(allocate.limit());
System.out.println(allocate.capacity());
System.out.println("*************clear******");
allocate.clear();
System.out.println(allocate.position());
System.out.println(allocate.limit());
System.out.println(allocate.capacity());
System.out.println("***********mark**********");
allocate.mark();
System.out.println(allocate.position());
System.out.println(allocate.limit());
System.out.println(allocate.capacity());
allocate.reset();
表示IO源与目标打开的连接,类似于传统的流,只不过channel本身不存储数据,它只能与缓冲区交互。
用于源节点和目标节点的连接,在java NIO中负责缓冲区中数据的传输。
java.nio.channels.Channel接口 -- FileChannel -- SocketChannel -- ServerSocketChannel -- DatagramChannel -- Pipe.SourceChannel --Pipe.SinkChannel
1 java针对支持通道的类提供了getChannel()方法 本地IO FileInputStream/FileOutputStream RandomAccessFile 网络IO socket ServerSocket DatagramSocket 比如: RandomAccessFile raf2 = new RandomAccessFile("2.jpg", "rw"); FileChannel outChannel = raf2.getChannel(); 2 在jdk1.7 中NIO2针对各个通道提供了静态方法open():
比如:
FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ); 3 在jdk1.7 中NIO2的Files 工具类的newByteChannel()
/**
* 利用通道完成数据复制
*/
@Test
public void test01(){
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
inputStream = new FileInputStream("src/1.jpg");
outputStream = new FileOutputStream("2.jpg");
//1 获取通道
inChannel = inputStream.getChannel();
outChannel = outputStream.getChannel();
//2 分配指定大小的缓冲区
ByteBuffer buff = ByteBuffer.allocate(1024);
//3 将通道的数据读入缓冲区
while(inChannel.read(buff) != -1){
buff.flip();
//4 将缓冲区的数据写入通道
outChannel.write(buff);
buff.clear();
}
//5 关闭流和通道
} catch (IOException e) {
e.printStackTrace();
} finally {
if(inChannel != null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(outChannel!=null){
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 使用直接缓冲区完成文件复制(内存映射文件)
*/
@Test
public void test02() throws Exception {
FileChannel inChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\00.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//内存映射文件
MappedByteBuffer inBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
//直接对缓冲区进行数据读写操作
byte[] dst = new byte[inBuffer.limit()];
inBuffer.get(dst);
outBuffer.put(dst);
outChannel.close();
inChannel.close();
}
/*
* 通道之间传输数据
* transferTo
* transferFrom
*/
@Test
public void test03() throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\00.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//transferTo
// inChannel.transferTo(0,inChannel.size(),outChannel);
//transferFrom
outChannel.transferFrom(inChannel,0,inChannel.size());
inChannel.close();
outChannel.close();
}
/**
* 分散和聚集
*/
@Test
public void test04() throws Exception {
//1 获取通道
RandomAccessFile raf1 = new RandomAccessFile("1.jpg", "rw");
FileChannel inChannel = raf1.getChannel();
//2 分配指定缓冲区
ByteBuffer buffer1 = ByteBuffer.allocate(100);
ByteBuffer buffer2 = ByteBuffer.allocate(102400);
ByteBuffer buffer3 = ByteBuffer.allocate(102400);
ByteBuffer buffer4 = ByteBuffer.allocate(102400);
//3 分散du读1取
ByteBuffer[] dst = {
buffer1,buffer2,buffer3,buffer4
};
inChannel.read(dst);
for (ByteBuffer b : dst){
b.flip();
}
// 4 聚集写出
RandomAccessFile raf2 = new RandomAccessFile("2.jpg", "rw");
FileChannel outChannel = raf2.getChannel();
outChannel.write(dst);
outChannel.close();
inChannel.close();
}
@Test
public void test07() throws CharacterCodingException {
Charset charset = Charset.forName("GBK");
//获取编码器和解解码器
CharsetEncoder encoder = charset.newEncoder();
CharsetDecoder decoder = charset.newDecoder();
CharBuffer buffer = CharBuffer.allocate(1024);
buffer.put("研发费分为非") ;
buffer.flip();
//编码
ByteBuffer byteBuffer = encoder.encode(buffer);
//解码
CharBuffer charBuffer = decoder.decode(byteBuffer);
//读取
System.out.println(charBuffer.toString());
}
@Test
public void client() throws IOException {
//1 获取通道
SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
FileChannel fileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
//2 获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3 通道传输出去数据
while(fileChannel.read(buffer) != -1){
buffer.flip();
skChannel.write(buffer);
buffer.clear();
}
//4 关闭流
fileChannel.close();
skChannel.close();
}
@Test
public void server() throws IOException {
// 获取通道
ServerSocketChannel sskChannel = ServerSocketChannel.open();
// 绑定连接
sskChannel.bind(new InetSocketAddress(9898));
//获取客户端连接通道
SocketChannel skChannel = sskChannel.accept();
//获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//接收客户端数据并存入本地
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
while(skChannel.read(buffer) != -1){
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
outChannel.close();
skChannel.close();
sskChannel.close();
}
@Test
public void client() throws IOException {
//1 获取通道
SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
FileChannel fileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
//2 获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3 通道传输出去数据
while(fileChannel.read(buffer) != -1){
buffer.flip();
skChannel.write(buffer);
buffer.clear();
}
System.out.println("发送图片完成");
//阻塞式必须告诉服务端发送完成服务端才可以接收到数据
skChannel.shutdownOutput();
//接收服务端反馈
int len = 0;
while((len = skChannel.read(buffer)) != -1){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
buffer.clear();
}
//4 关闭流
fileChannel.close();
skChannel.close();
}
@Test
public void server() throws IOException {
// 获取通道
ServerSocketChannel sskChannel = ServerSocketChannel.open();
// 绑定连接
sskChannel.bind(new InetSocketAddress(9898));
//获取客户端连接通道
SocketChannel skChannel = sskChannel.accept();
//获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//接收客户端数据并存入本地
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
while(skChannel.read(buffer) != -1){
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
System.out.println("接收图片完成");
byte[] src = "获取客户端图片成功".getBytes();
buffer.put(src);
buffer.flip();
skChannel.write(buffer);
outChannel.close();
skChannel.close();
sskChannel.close();
}
public static void main(String[] args) throws IOException {
//1 获取通道
SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
//切换 为非阻塞式
skChannel.configureBlocking(false);
// 2 获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
Scanner scan = new Scanner(System.in);
while(scan.hasNext()) {
String str = scan.next();
System.out.println(str);
//3 缓冲区存储数据
buffer.put((new Date()+"\n"+str).getBytes());
//4 通道传输数据
buffer.flip();
skChannel.write(buffer);
buffer.clear();
}
// 关闭通道
skChannel.close();
}
@Test
public void client() throws IOException {
//1 获取通道
SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
//切换 为非阻塞式
skChannel.configureBlocking(false);
// 2 获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3 缓冲区存储数据
buffer.put(new Date().toString().getBytes());
//4 通道传输数据
buffer.flip();
skChannel.write(buffer);
buffer.clear();
// 关闭通道
skChannel.close();
}
@Test
public void server() throws IOException {
//获取通道
ServerSocketChannel sskChannel = ServerSocketChannel.open();
// 切换为非阻塞式
sskChannel.configureBlocking(false);
//绑定客户端连接
sskChannel.bind(new InetSocketAddress(9898));
//获取选择器
Selector selector = Selector.open();
// 将通道注册到选择器
SelectionKey selectionKey = sskChannel.register(selector, SelectionKey.OP_ACCEPT);
// System.out.println("tongdao 注册选择器");
//轮询式的获取选择器上已经准备就绪的事件
while(selector.select()>0){
// 获取当前选择器中所有的选择键(已经准备就绪的事件)
Iterator iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
//获取准备就绪的事件
SelectionKey next = iterator.next();
// System.out.println("获取选择器"+next.isReadable()+"||"+next.isAcceptable());
if (next.isAcceptable()) {
//接收事件
//获取客户端连接的通道,并注册选择器
SocketChannel sChannel = sskChannel.accept();
//切换为非阻塞
sChannel.configureBlocking(false);
//注册到选择器
sChannel.register(selector,SelectionKey.OP_READ);
}else if(next.isReadable()){
//读事件
//获取选择器上(读状态就绪)状态的通道
SocketChannel socketChannel = (SocketChannel) next.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = 0;
while((len=socketChannel.read(buffer)) !=-1){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
buffer.clear();
}
}
}
//需要将这个从选择器中取消掉,不然下次循环还会获取准备就绪的这个事件,重复获取了 Iterator iterator = selector.selectedKeys().iterator();
iterator.remove();
}
}
@Test
public void client() throws IOException {
DatagramChannel dc = DatagramChannel.open();
dc.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("ceshi DatagramChannel".getBytes());
buffer.flip();
dc.send(buffer,new InetSocketAddress("127.0.0.1",9898));
buffer.clear();
dc.close();
}
@Test
public void server() throws IOException {
DatagramChannel dc = DatagramChannel.open();
dc.configureBlocking(false);
dc.bind(new InetSocketAddress(9898));
Selector selector = Selector.open();
dc.register(selector, SelectionKey.OP_READ);
while (selector.select() > 0) {
//遍历获取准备好的事件
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey next = iterator.next();
//只需要监控读事件
if (next.isReadable()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
dc.receive(buffer);
buffer.flip();
System.out.println(new String(buffer.array(), 0, buffer.limit()));
buffer.clear();
}
iterator.remove();
}
}
}
javanio两个线程之间单向的数据传输:管道Pipe有个Source和Sink通道,数据会被写入Sink通道,从Source读取
@Test
public void test() throws IOException {
//获取管道
Pipe open = Pipe.open();
//将缓冲区的数据写入管道
Pipe.SinkChannel sink = open.sink();
Pipe.SourceChannel source = open.source();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("测试sink通道".getBytes());
System.out.println(buffer.position());
System.out.println(buffer.limit());
buffer.flip();
System.out.println(buffer.position());
System.out.println(buffer.limit());
sink.write(buffer);
System.out.println(buffer.position());
System.out.println(buffer.limit());
// 读取缓冲区的数据
//切换操作模式,将位置置为初始位置,才可以读取
buffer.flip();
System.out.println(buffer.position());
System.out.println(buffer.limit());
source.read(buffer);
System.out.println(new String(buffer.array(),0,buffer.limit()));
//关闭通道
sink.close();
source.close();
}