标签:映射 连接数 锁定 read block 返回值 内存 连接 完全
传统IO:
Stream
的对象一次移动一个字节。Channel
对象。Buffer
是一个容器对象。发送给一个通道的所有对象都放在缓冲区中(从通道读取数据也一样)。Buffer
是一个对象,包含要读出或写入的数据。ByteBuffer
。ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
Channel
是一个对象,用来读取和写入数据。InputStream
,OutputStream
)步骤:
FileInputStream
获取Channel
。Buffer
。Channel
将数据读到Buffer
中。示例:
// 获取通道
FileInputStream fin = new FileInputStream("test.md");
FileChannel fc = fin.getChannel();
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 利用通道将数据读到缓冲区
fc.read(buffer);
步骤:
FileOutputStream
获取Channel
。Buffer
。Buffer
中。Channel
将数据从缓冲区写出。示例:
// 创建输出流对象
FileOutputStream fout = new FileOutputStream();
// 由输出流对象获得通道
FileChannel fc = fout.getChannel();
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 将数据放到缓冲区中
for(int i = 0; i < message.length;++i){
buffer.put(message[i]);
}
// 设置指针指向缓冲区开始
buffer.flip();
// 利用通道将缓冲区数据写出
fc.write(buffer);
将一个文件的所有内容拷贝到另外一个文件中。
步骤:
示例:
FileInputStream fin = new FileInputStream(infile); // 输入流对象
FileOutputStream fou = new FileOutputStream(outfile); // 输出流对象
FileChannel fcin = fin.getChannel(); // 获取输入通道
FileChannel fcout = fou.getChannel(); // 获取输出通道
ByteBuffer buffer = ByteBuffer.allocate(1024); // 创建缓冲区
// 拷贝文件
while (true){
buffer.clear(); // 清空缓冲区
int r = fcin.read(buffer); // 通过输入通道将数据读入
if (r == -1) // 若到文件末尾,则退出
break;
buffer.flip(); // 将指针至到缓冲区开始,从开始位置输出数据
fcout.write(buffer); // 通过输出通道将数据从缓冲区写出到文件
}
缓冲区有两个重要的组件:
缓冲区有三个状态变量指示当前的状态:
position
limit
capacity
Position
position
用来指示下一次操作所指向的数组索引位置.position
指示下一个读入的数据应该放在数组的位置.position
指示下一个写出的数据在数组的位置.Limit
limit
指示position
读入数据的位置不能超过限制.limit
指示position
写出的数据不能超过的位置.position
不能超过limit
指示的位置.Capacity
limit
不能超过capacity
.示例
capacity
为n,limit
为n,position
为0.position
指向位置a,其他保持不变.position
指向位置a+b,其他保持不变.flip()
.其将limit
设置为position
指向的位置a+b,position
设置为0.position
指向位置a.position
指向位置a+b.clear()
方法,将清空缓冲区,并将position
设置为0,limit
设置为capacity
.get()
方法分类:
byte get()
==> 获取单个字符,操作影响positon
ByteBuffer get(byte dst[]);
==> 将一组字符读入数组中,操作影响position
ByteBuffer get(byte dst[], int offset, int length);
==> 将一组字符读入数组中,操作影响position
byte get(int index);
==> 从特定位置获取字符,与position
无关put()
方法分类:
ByteBuffer put(byte b);
==> 写入单个字节,影响position
ByteBuffer put(byte src[]);
==> 写入一组字节,影响position
ByteBuffer put(byte src[], int offset, int length);
==> 写入一组字节,影响position
ByteBuffer put(ByteBuffer src);
==> 从ByteBuffer写入到当前ByteBuffer,影响position
ByteBuffer put(int index, byte b);
==> 将字节写入到指定的位置,与position
无关get()
和put()
方法对于不同的类型有不同的方法.
allocate(缓冲区大小)
分配缓冲区.wrap()
方法将已有的数组转换成缓冲区.wrap()
方法获得缓冲区,则通过原数组也可访问底层数据.slice()
由已有的缓冲区创建一个子缓冲区.asReadOnlyBuffer()
方法将普通缓冲区转换为只读缓冲区.直接缓冲区:
加快I/O速度,以特殊的方式分配其内存的缓冲区.
给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接对它执行本机 I/O 操作。也就是说,它会在每一次调用底层操作系统的本机 I/O 操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中(或者从一个中间缓冲区中拷贝数据)。
通过使文件中的数据以内存数组的内容来完成.
一般只有实际读取或写入的部分才会送入内存中.
其提供底层操作系统的机制调用.
示例
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE,0,1024);
// 将FileChannel的前1024个字节映射到内存中
// 返回值为MappedByteBuffer,为ByteBuffer子类
两个接口:
ScatteringByteChannel
GatheringByteChannel
ScatteringByteChannel的两个读方法
long read(ByteBuffer[] dsts);
long read(ByteBuffer[] dsts, int offset, int length);
特点:
聚集写入的两个写方法
long write(ByteBuffer[] srcs);
long write(ByteBuffer[] srcs, int offset, int length);
有一个网络应用,每个消息被划分成固定长度的头部和固定长度的正文.
创建一个容纳头部的缓冲区和一个容纳正文的缓冲区.
文件锁定:不阻止任何形式的数据访问,而是通过锁的共享和获得运行不同的部分相互协调.
共享锁:其他人可以获得共享锁,但不能获得排它锁.
排它锁:其他人不能获得同一文件的锁.
示例:
RandomAccessFile raf = new RandomAccessFile("test.md","rw");
FileChannel fc = raf.getChannel();
FileLock lock = fc.lock(start,end,false);
原则:
private int ports[];
private ByteBuffer echoBuffer = ByteBuffer.allocate( 1024 );
private void go() throws IOException {
// 创建一个selector
// 是注册对I/O事件感兴趣的地方,当事件发送时,selector发出通知
Selector selector = Selector.open();
// 对每个端口打开一个监听器,并注册到selector中
for (int i=0; i<ports.length; ++i) {
// 为监听每个端口,每个端口需要一个ServerSocketChannel
ServerSocketChannel ssc = ServerSocketChannel.open();
// 设置为非阻塞式
ssc.configureBlocking( false );
// 新建一个socket
ServerSocket ss = ssc.socket();
// 新建socket地址
InetSocketAddress address = new InetSocketAddress( ports[i] );
// socket绑定端口地址
ss.bind( address );
// 将ServerSocketChannels注册到selector上
// 第一个参数是selector,第二个参数是指定监听的事件
// 返回值表示通道在此selector上的注册,当通知发生事件时,是提供该事件的selectionKey进行的
SelectionKey key = ssc.register( selector, SelectionKey.OP_ACCEPT );
System.out.println( "Going to listen on "+ports[i] );
}
while (true) {
// 阻塞,直到一个或多个事件发生
// 返回发生的事件数
int num = selector.select();
// 返回所有事件对应的selectedKey的集合
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
//对于每个事件的处理
while (it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
// 获取selectKey对应事件的类型,若有新连接,则接收
if ((key.readyOps() & SelectionKey.OP_ACCEPT)
== SelectionKey.OP_ACCEPT) {
// 创建ServerSocketChannel
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept(); // 接受连接
sc.configureBlocking( false ); // 设置为非阻塞式
// 接收完后,更新新的selectionKey,用来接收新连接
SelectionKey newKey = sc.register( selector, SelectionKey.OP_READ ); // 注册为读取
it.remove(); // 将处理完的selectionKey从集合中删除,否则会被再次处理
System.out.println( "Got connection from "+sc );
}
// 若套接字的数据到达,则接收数据
else if ((key.readyOps() & SelectionKey.OP_READ)
== SelectionKey.OP_READ) {
// 获取处理的通道
SocketChannel sc = (SocketChannel)key.channel();
int bytesEchoed = 0;
while (true) {
echoBuffer.clear();
// 获取读取结果
int r = sc.read( echoBuffer );
// 小于等于0则传输结束
if (r<=0) {
break;
}
echoBuffer.flip();
// 写出缓冲区数据
sc.write( echoBuffer );
bytesEchoed += r;
}
System.out.println( "Echoed "+bytesEchoed+" from "+sc );
// 移除selectionKey,避免重复处理
it.remove();
}
Charset
:十六位Unicode字符序列与字节序列之间的一个命名映射.
读文本:CharsetDecoder
.(逐位将字符转换为char值)
写文本:CharsetEncoder
.(将字符转换为位)
Java支持的字符编码:
示例:
// 创建字符集实例
Charset latin1 = Charset.forName( "ISO-8859-1" );
CharsetDecoder decoder = latin1.newDecoder(); // 字符集对应的解码器
CharsetEncoder encoder = latin1.newEncoder(); // 字符集对应的编码器
CharBuffer cb = decoder.decode( inputData ); // 将字符数据解码,生成缓冲区
ByteBuffer outputData = encoder.encode( cb ); // 将缓冲区数据编码
outc.write( outputData ); // 输出编码后的数据
inf.close();
outf.close();
NIO(Non-blocking I/O):同步非阻塞的I/O模型,I/O多路复用的基础.
传统服务器端同步阻塞I/O处理:
ExecutorService executor = Excutors.newFixedThreadPollExecutor(100); // 线程池
ServerSocket serverSocket = new ServerSocket(); // 创建新socket
serverSocket.bind(8088); // socket绑定端口
while(!Thread.currentThread.isInturrupted){ // 线程循环等待新连接
Socket socket = serverSocket.accept(); // 接收新连接
executor.submit(new ConnectIOnHandler(socket)); // 为新连接创建一个新线程
}
class ConnectIOnHandler extends Thread{
private Socket socket;
public ConnectIOnHandler(Socket socket){
this.socket = socket;
}
public void run(){
// 循环处理读写事件
while(!Thread.currentThread.isTnturrupted()&&!socket.isClosed()){
String someThing = socket.read(); // 读取数据
if(someThing != null){
// 处理数据
socket.write(); // 写数据
}
}
}
}
特点:
accpet()
,read()
和write()
是**同步阻塞的,即:每个连接处理I/O时是阻塞的.I/O的两个阶段:
read()
方法若没有收到数据,则一直阻塞,直到收到数据后返回数据.参考:
标签:映射 连接数 锁定 read block 返回值 内存 连接 完全
原文地址:https://www.cnblogs.com/truestoriesavici01/p/13235951.html