当前位置: 首页 > news >正文

缓冲器的学习

导语

缓冲器的设计的是新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();
        }
    }
}

转载于:https://www.cnblogs.com/xidongyu/p/6259438.html

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 理解 Linux shell 中的一个方言:21
  • HBase 数据读写流程
  • Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结
  • powershell: 生成随机字符串
  • 使用httpClient上传至远程服务器
  • Kafka Offset Storage
  • jvm理论-运行时数据区
  • #if和#ifdef区别
  • Linux 虚拟地址与物理地址的映射关系分析【转】
  • nginx建https站实验
  • Flex布局到底解决了什么问题
  • VB之SendKeys键盘模拟
  • win 下 apache 虚拟主机配置方式
  • php libevent 详解与使用
  • Weex技术峰会精华集锦:揭秘火爆Github排行版的跨平台移动开发工具背后技术
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 10个确保微服务与容器安全的最佳实践
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • es的写入过程
  • LeetCode算法系列_0891_子序列宽度之和
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • mongo索引构建
  • MYSQL 的 IF 函数
  • mysql_config not found
  • python学习笔记 - ThreadLocal
  • react 代码优化(一) ——事件处理
  • SpringCloud集成分布式事务LCN (一)
  • Vue2.0 实现互斥
  • 面试遇到的一些题
  • 七牛云假注销小指南
  • 前嗅ForeSpider中数据浏览界面介绍
  • 小程序button引导用户授权
  • FaaS 的简单实践
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • 通过调用文摘列表API获取文摘
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • ​必胜客礼品卡回收多少钱,回收平台哪家好
  • ​什么是bug?bug的源头在哪里?
  • #### golang中【堆】的使用及底层 ####
  • $ git push -u origin master 推送到远程库出错
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (10)STL算法之搜索(二) 二分查找
  • (13):Silverlight 2 数据与通信之WebRequest
  • (2024,Vision-LSTM,ViL,xLSTM,ViT,ViM,双向扫描)xLSTM 作为通用视觉骨干
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (十六)一篇文章学会Java的常用API
  • (四)图像的%2线性拉伸
  • (详细文档!)javaswing图书管理系统+mysql数据库
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全
  • .Mobi域名介绍
  • .NET 动态调用WebService + WSE + UsernameToken
  • .NET 服务 ServiceController