NIO_2
导语
缓冲器的设计的是新IO模型中最基础的一部分。因为新IO模型中要求所有的IO操作都需要进行缓冲。在新的IO模型中,不再向输出流写入数据和从数据流中读取数据了,而是要从缓冲区中读写数据。缓冲区可是是数组,也可以是与硬件或内存直接连接。
从编程的角度来看,流和通道之间的关键区别子在于流是基于字节的,而通道是基于块的。流设计为按顺序一字节接一字节地传输数据。出于性能的考虑,可以传送字节数组。不过,基本的概念都是一次传输一个字节的数据。与之不同的是,通道会传输缓冲区中的数据块。
基本概念
缓冲区可以看作是固定大小的元素列表,这些元素为特定类型,一般是基本数据类型。除boolean外,java中所有基本数据类型都有特定的Buffer子类:ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer和DoubleBuffer。每个子类中的方法都有相应类型的返回值和参数列表。例如:DoubleBuffer类有设置和获取double的方法。不管缓冲区具体是什么类型,他们都有四个关键部分。
position
缓冲区中见读取或写入的位置,读取和写入时都从这个位置开始,其初始值为0,最大等于缓冲区的大小。可以用下面两个方法来获取和设置该值:
public final int position()
public final Buffer position(int newPosition)
capacity
缓冲区可以保存的元素最大数目。容量值在创建缓冲区时设置,此后不能改变,可以用下面的方式读取:
public final int capacity()
limit
缓冲区中可访问的末尾位置,读或写时只能到limit的前一个位置,其初始值等于capacity。通过下面两个方法来控制:
public final int limit()
public final Buffer limit(int newLimit)
mark
在对缓冲区进行读写的时候用户可以通过mark进行标记。调用mark()时,mark的值等于position的值。调用reset()可以将position的值设置为mark值。
public final Buffer mark()
public final Buffer reset()
对缓冲区的操作还有其他几个方法。clear()方法将位置设置为0,将限度设置为容量,从而将缓冲区"清空"。这样就可以重新填充缓冲区了。rewind()的方法将位置设置为0,这样就可以重新读取缓冲区了。flip()方法将限度设置为当前位置,位置设置为0。
创建缓冲区
分配
通过allocate()方法创建一个固定大小的缓冲区。该方法创建的缓冲区是基于Java数组实现的。
ByteBuffer buffer = ByteBuffer.allocate(100);
直接分配
ByteBuffer类(其他类没有)有另外一个方法是 allocateDirect()方法,这个方法不为缓冲区创建Java数组。JVM会对以太网卡,核心内存或其他位置上的缓冲区使用直接内存访问,这样可以提升IO操作的性能。不过在API角度来说,两者在使用上是没有区别的。创建直接缓冲区的代价比间接缓冲区要高,除非必须要使用直接缓冲区则去创建它。
包装
如果有了要输出的数据数组,则可以用缓冲区进行包装。例如:
byte[] data = "Some data".getBytes("UTF-8");
ByteBuffer buffer = ByteBuffer.wrap(data);
在这里,缓冲区直接将该数组作为后备数组,即该数组已经变为缓冲区了。所以对该数组的操作将直接反应到缓冲区,反之亦然。
填充和排空
缓冲区是为顺序访问而设计的。没有缓冲区都有一个当前位置,由position来标识。从缓冲区读取或写入一个元素时,position都将增1。例如:假设你想分配一个容量为12的CharBuffer,并在其中放置5个字符:
CharBuffer buffer = CharBuffer.allocate(12);
buffer.put('H');
buffer.put('e');
buffer.put('l');
buffer.put('l');
buffer.put('o');
缓冲区现在的位置为5。这称为填充缓冲区。如果现在试图使用get()从缓冲区获取数据,则会得到null字符。要想读取之前写入的数据需要调用flip()将缓冲区回绕。当然还有两个扩展方法可以指定位置来读或写。
public abstract byte get(int index)
public abstract ByteBuffer put(int index, byte b)
批量方法
即使使用缓冲区,操作数据块通常比一次填写一个元素要快。不同的缓冲区都有一些批量方法来填充或排空相应的数组。例如:
public ByteBuffer get(byte[] dst, int offset, int length);
public ByteBuffer get(byte[] dst)
public ByteBuffer put(byte[] array, int offset, int length)
public ByteBuffer put(byte[] array)
复制缓冲区
经常需要建立缓冲区的副本,从而将相同的信息分发到两个或多个通道。6中特定类型缓冲区类提供了duplicate()方法来完成这项工作:
public abstract ByteBuffer duplicate()
public abstract IntBuffer duplicate()
public abstract ShortBuffer duplicate()
public abstract FloatBuffer duplicate()
public abstract CharBuffer duplicate()
public abstract DoubleBuffre duplicate()
返回值是对原来缓冲区的复制,但不复制简介缓冲区(建立缓冲区时创建的用于存储数据的数组)。修改通过复制得到的缓冲区中的数据会直接反应到另一个缓冲区。这个方法主要用于只是读取缓冲区中的数据。虽然多个缓冲区共享数据,但是每个缓冲区都有独立的标记,限度和位置。希望多个通道大致并行地传输相同的数据时,这个方法特别有用。下面是改版后的单文件传输服务器:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URLConnection;
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.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.Set;
public class NonblockingSingleFileHTTPServer {
private ByteBuffer contentBuffer;
private final byte[] header;
private final byte[] discard;
private int port;
public NonblockingSingleFileHTTPServer(int _port, byte[] data,
String encoding, String contentType) {
port = _port;
String respHeader = "HTTP/1.1 200 OK\r\n"
+ "Server: OneFile\r\n"
+ "Content-length: "+ data.length +"\r\n"
+ "Content-Type: " + contentType + ";charset=" + encoding + "\r\n\r\n";
header = respHeader.getBytes();
contentBuffer = ByteBuffer.allocate(header.length + data.length);
contentBuffer.put(header);
contentBuffer.put(data);
contentBuffer.position(0);
discard = new byte[4096];
}
private void start() {
Selector selector;
try {
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.bind(new InetSocketAddress(port));
selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
return;
}
while (true) {
try {
selector.select();
} catch (IOException e) {
e.printStackTrace();
break;
}
Set<SelectionKey> readykeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readykeys.iterator();
ByteBuffer buffer = ByteBuffer.wrap(discard);
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
SelectionKey key2 = client.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
key2.attach(contentBuffer.duplicate());
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
buffer.clear();
client.read(buffer);
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer duplication = (ByteBuffer) key.attachment();
if (duplication.hasRemaining())
client.write(duplication);
else {
client.close();
key.cancel();
}
}
} catch (IOException e) {
if (key.isAcceptable())
e.printStackTrace();
else {
key.cancel();
try {
key.channel().close();
} catch (IOException e1) {}
}
}
}
}
}
public static void main(String[] args) {
int port;
String encoding = "utf-8";
if (args.length == 0) {
System.out.println("Usage: java NonblockingSingleFileHTTPServer file port encoding");
return;
}
if (args.length > 2)
encoding = args[2];
try {
port = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
port = 80;
}
Path path = Paths.get(args[0]).toAbsolutePath();
String contentType = URLConnection.getFileNameMap().getContentTypeFor(path.toString());
try {
byte[] data = Files.readAllBytes(path);
NonblockingSingleFileHTTPServer server = new NonblockingSingleFileHTTPServer(port, data, encoding, contentType);
server.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
NIO_2的更多相关文章
随机推荐
- NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配
在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...
- 无线路由器无线AP模式的配置
环境介绍>>>>>>>>>>>>>>>>>>>交换机类型:三层交换机无线路由器品牌:T ...
- 防EasyUI中登录按钮
之前系统中是用easyui做的,可能在提示"请输入密码"这几个字时,就变成了点了,让我很... 于时还是找了很多,总算还是让我找到了,不会表达就来源码吧 <style typ ...
- OpenCV学习笔记之课后习题练习3-3
3.3 创建一个100*100的拥有三个通道的二维字节类型矩阵,将其元素全部置0.通过cvPtr2D函数将指针指向中间通道(绿色),以(20,5)和(40,20)为顶点间画一个绿色的长方形. cvPt ...
- ubuntu16.04下安装opencv3.1.0
1.安装依赖项 sudo apt--dev pkg-config libavcodec-dev libavformat-dev libswscale-dev 可选的 sudo apt--dev lib ...
- Mergeable Stack 直接list内置函数。(152 - The 18th Zhejiang University Programming Contest Sponsored by TuSimple)
题意:模拟栈,正常pop,push,多一个merge A B 形象地说就是就是将栈B堆到栈A上. 题解:直接用list 的pop_back,push_back,splice 模拟, 坑:用splice ...
- centos 7 yum configuration; yum localinstall
Linux下对于软件包的管理使用rpm管理方式.直接使用rpm包管理工具来进行rpm包的安装,升级,卸载时,对于最让人头疼的莫过与包之间的依赖关系.yum作为一个rpm包前端管理工具,可以自动处理依赖 ...
- Heavy Transportation---poj1797
求(Dijkstra算法,求每条路径上的最小值 的最大值)和青蛙的那题类似: #include<iostream> #include<stdio.h> #include&l ...
- (1.1)mysql 选择合适的数据类型
(1.1)mysql 选择合适的数据类型 1.char与varchar [1.1]char 在内容未满定义长度时,做空格填充,且字符串末尾空格会被截断:超出定义长度也会被截断. 如:char(4) ...
- Hadoop自学笔记(三)MapReduce简单介绍
1. MapReduce Architecture MapReduce是一套可编程的框架,大部分MapReduce的工作都能够用Pig或者Hive完毕.可是还是要了解MapReduce本身是怎样工作的 ...