NIO之ByteBuffer_NIO之网络IO_与ChannelNetty初窥门径

NIO之ByteBuffer与Channel

传统IO:byte[] <= inputStream <= 文件 => outputStream => byte[]
NIO:文件 => inputChannel <=> buffer <=> outputChannel => 文件文件 <= inputChannel <=> outputChannel => 文件
  • 文件复制, 并测试ByteBuffer常用API
    position: 当前指针位置; limit: 当前内容的最大位置(例如: buffer内容为"hello", 容量为20, limit就是5); capacity: 最大容量
/*** 测试Buffer的position, limit, capacity, clear, flip* @author regotto*/
public class NioTest1 {/*** 下面的代码中, clear与flip效果一样.* 没有clear,flip,position的位置将会一直等于limit, 根据各属性之间的大小关系, position一定不会大于limit, * 所以在下面的read中, 读取到的值将一直都是0(代表当前读取到的位置), read一直不会等于-1, 代码出现死循环* @param args* @throws Exception*/public static void main(String[] args) throws Exception {FileChannel inputChannel = new FileInputStream("input.txt").getChannel();FileChannel outputChannel = new FileOutputStream("output.txt").getChannel();ByteBuffer buffer = ByteBuffer.allocate(4);while(true){//此处不进行clear, 此时position还是处于limit的位置, read将一直保持当前位置出现//死循环buffer.clear();int read = inputChannel.read(buffer);System.out.println("read: " + read);if (-1 == read){break;}//重置bufferbuffer.flip();outputChannel.write(buffer);}inputChannel.close();outputChannel.close();}
}

clear, flip源码如下:

   public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;}public final Buffer flip() {limit = position;position = 0;mark = -1;return this;}
  • DirectByteBuffer
    ByteBuffer.allocate(1024) => HeapByteBuffer, 内部使用的就是byte[], 底层源码如下
    public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity);}//HeapByteBuffer extends ByteBuffer//ByteBuffer构造函数如下:ByteBuffer(int mark, int pos, int lim, int cap,   // package-privatebyte[] hb, int offset){super(mark, pos, lim, cap);this.hb = hb;this.offset = offset;}

HeapByteBuffer位于JVM堆空间, 当使用HeapByteBuffer进行内容复制时, 存在2个复制过程: 应用程序 => 应用程序缓冲区 => 内核缓冲区 => 文件; 这种情况下, 2个复制过程存在一定的性能问题;

ByteBuffer.allocateDirect(1024) => DirectByteBuffer, DirectByteBuffer使用native方法创建数组, 数组不再位于JVM的Heap中, 而是位于内核内存中, 这样就避免了一次数据拷贝(拷贝的原因在于JVM中数据的地址会改变, 在GC下): 应用程序缓冲区 -> 内核缓冲区; 所谓的零拷贝, 加快速度, C/C++开辟的数组空间都是位于内核缓冲区
DirectByteBuffer底层源码:

    public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);}//unsafe中底层内存分配base = unsafe.allocateMemory(size);public native long allocateMemory(long var1);

使用DirectByteBuffer进行文件复制:

/*** 测试DirectByteBuffer* @author regotto*/
public class NioTest2 {public static void main(String[] args) throws Exception {FileChannel inputChannel = new FileInputStream("input.txt").getChannel();FileChannel outputChannel = new FileOutputStream("output.txt").getChannel();ByteBuffer buffer = ByteBuffer.allocateDirect(4);while(true){buffer.clear();int read = inputChannel.read(buffer);System.out.println("read: " + read);if (-1 == read){break;}buffer.flip();outputChannel.write(buffer);//    buffer.flip();}inputChannel.close();outputChannel.close();}/*** 进行文件复制*/public void test() throws Exception {FileChannel fisChannel = new FileInputStream("text1.txt").getChannel();FileChannel fosChannel = new FileOutputStream("text2.txt").getChannel();//transferTo与transferFrom效果一样fisChannel.transferTo(0, fisChannel.size(), fosChannel);fisChannel.close();fosChannel.close();}
}
  • 使用堆外内存进行文件内容复制(使用块内存提高性能)
/*** 测试MappedByteBuffer* 使用堆外内存对文件内容进行修改* @author regotto*/
public class NioTest3 {public static void main(String[] args) throws Exception{//下面的0, 4代表从0号位开始,将4个大小的空间映射到堆外内存MappedByteBuffer mappedByteBuffer = new RandomAccessFile("input.txt", "rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 4);mappedByteBuffer.put(0, (byte) 'a');mappedByteBuffer.put(0, (byte) 'a');mappedByteBuffer.put(0, (byte) 'a');}
}

NIO之网络IO

使用NIO进行网络非阻塞式编程, NIO编程模型图:
在这里插入图片描述

Selector: 检测Channel是否存在事件发生
ServerSocketChannel: 服务器
Channel: 管道
Client: 客户端

NIO网络编程结构图:
在这里插入图片描述

selectionKey的4种状态:OP_ACCEPT: 网络已连接 value = 16OP_CONNECT: 连接已建立 value = 8OP_READ OP_WRITE: 读/写操作, value = 1 或value = 4 

根据上面结构图编写简单案例代码:
NioServer

/*** server*/
public class NioServer {public static void main(String[] args) throws IOException, InterruptedException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.bind(new InetSocketAddress(9999));Selector selector = Selector.open();SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {//设置selector监控, 监控Channel的连接情况, 此处除去第一次的ServerSocketChannel注册, 监控时长2sif (selector.select(2000) == 0) {System.out.println("------当前没有要处理的Channel, 我去处理其他事------");TimeUnit.SECONDS.sleep(1);continue;}//此时有Channel进行连接, 获取selectedKeys, 处理每一个Channel的对象事件Set<SelectionKey> selectionKeys = selector.selectedKeys();selectionKeys.forEach(key -> {if (key.isAcceptable()) {System.out.println("OP_ACCEPT");//说明此处的key对应的是最开始前面register的ServerSocketChannelServerSocketChannel server = (ServerSocketChannel) key.channel();//此处对象的hash值相同System.out.println("(ServerSocketChannel) key.channel(): " + server.hashCode());System.out.println("ServerSocketChannel.open(): " + serverSocketChannel.hashCode());try {SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);//此处注册的时候就附加一个缓冲区, 可用于传输对象//当执行register的时候, 就会生成一个对应的SelectionKey事件, 当前的selectionKeys遍历完成, 就会将该selectionKey添加到set集合中SelectionKey socketChannelReadRegister = socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));} catch (IOException e) {e.printStackTrace();}}if (key.isReadable()) {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer byteBuffer = (ByteBuffer) key.attachment();try {socketChannel.read(byteBuffer);System.out.println("客户端内容: " + new String(byteBuffer.array(), StandardCharsets.UTF_8));} catch (IOException e) {e.printStackTrace();}}//这里使用remove与下面使用clear效果一样, 都是清除当前已经执行过的Channel, 避免重复执行//当前的Channel对应的事件被处理过, 就不再被处理, 使用这种写法较为恰当selectionKeys.remove(key);});
//            selectionKeys.clear();}}
}

NioClient

/*** client*/
public class NioClient {public static void main(String[] args) throws IOException, InterruptedException {SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);if (!socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999))) {//连接服务端失败, 使用finishConnect进行连接, 此处finishConnect非阻塞while (!socketChannel.finishConnect()) {System.out.println("连接同时, 干其他事情");TimeUnit.SECONDS.sleep(2);}}//return new HeapByteBuffer(capacity, capacity);
//        ByteBuffer.allocate(1024);//return new HeapByteBuffer(array, offset, length);ByteBuffer buffer = ByteBuffer.wrap("hello world".getBytes(StandardCharsets.UTF_8));socketChannel.write(buffer);//此处阻塞, 若关闭连接, server会抛出异常System.out.println("进入睡眠");Thread.currentThread().join();}
}

Netty初窥门径

  • Netty模型:
    在这里插入图片描述
BossGroup: 处理客户端连接请求
WorkGroup: 处理网络读写操作
二者使用NioEventLoopGroup不断循环处理任务线程, NioEventLoopGroup内部都有一个selector, 监听每个Channel连接情况NioEventLoopGroup内部使用串行化设计: 消息读取->解码->处理->编码->发送
  • NioEventLoopGroup模型:
    在这里插入图片描述
一个NioEventLoopGroup包含多个NioEventLoop
一个NioEventLoop包含一个Selector, 一个任务队列
每个NioChannel都会绑定一个自己的ChannelPipeline
  • ChannelPipeline模型:
    在这里插入图片描述
ChannelPipeline: Handler集合, 负责处理/拦截inbound, outbound操作
ChannelHandlerContext: 事件处理器上下文对象, 内部包含每个具体的ChannelHandler, 也绑定对应Channel, pipeline信息
  • 简单入门案例:
    NettyServer:
/*** 服务器端*/
public class NettyServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workGroup = new NioEventLoopGroup();//线程池中任务队列数: ChannelOption.SO_BACKLOG, 128//让连接保持活动状态: ChannelOption.SO_KEEPALIVEServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, workGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new NettyServerHandler());}});ChannelFuture channelFuture = serverBootstrap.bind(9999).sync();channelFuture.channel().closeFuture().sync();bossGroup.shutdownGracefully();workGroup.shutdownGracefully();}}

NettyServerHandler:

public class NettyServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf byteBuf = (ByteBuf) msg;System.out.println("客户端发送的内容: " + byteBuf.toString(CharsetUtil.UTF_8));}/*** 数据读取完成* @param ctx Channel上下文对象* @throws Exception*/@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.writeAndFlush(Unpooled.copiedBuffer("就是没钱", CharsetUtil.UTF_8));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {//出现异常, 直接关闭上下文对象ctx.close();}
}

NettyClient

/*** 客户端*/
public class NettyClient {public static void main(String[] args) throws InterruptedException {EventLoopGroup workGroup = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap().group(workGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new NettyClientHandler());}});ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9999).sync();channelFuture.channel().closeFuture().sync();}
}

NettyClientHandler:

public class NettyClientHandler  extends ChannelInboundHandlerAdapter {/*** 通道准备就绪, 当前已经连接* @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ctx.writeAndFlush(Unpooled.copiedBuffer("老板, 还钱吧".getBytes(CharsetUtil.UTF_8)));}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf byteBuf = (ByteBuf) msg;System.out.println(byteBuf.toString(CharsetUtil.UTF_8));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468563.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mp4 拍摄时间如何看_时间不多了,如何备考期末最有效?这些复习技巧,看了你就会了...

再过不到一个月就要过年了&#xff0c;虽然天气越来越冷&#xff0c;但是有阻挡不了年度大戏“期末考”的前进步伐&#xff0c;尤其是对于紧张复习之中的高考备考生而言&#xff0c;高三第一学期的期末考就可以算是对自己第一轮复习的一个检验&#xff0c;如果成绩不理想&#…

vue实战学习第二天

1.怎么运行别人的项目 步骤一&#xff1a;搭建脚手架&#xff1a;npm i -g vue-cli 步骤二&#xff1a;vue init webpack 不要一直默认回车&#xff0c;去除一些不必要的依赖&#xff0c;减少代码的编写难度 步骤三&#xff1a;下载依赖的文件 npm i &#xff08;可能有些人会…

Netty之自定义RPC

需求分析 使用netty实现方法远程调用, 在client调用本地接口中方法时, 使用反射进行远程调用, server执行完结果后, 将执行结果进行封装, 发送到client RPC调用模型: 1. 服务消费方(client)以本地调用方式调用服务 2. client stub 接收到调用后负责将方法、参数等封装成能够…

samba 服务器搭建

为什么要搭建samba 服务器我在 windows 下安装了个虚拟机&#xff0c;然后想两边同步下资料&#xff0c;原来虚拟机是可以共享文件的&#xff0c;可是不知道什么见鬼了&#xff0c;就是不行&#xff0c;没办法了&#xff0c;我只好拿出我的杀手锏&#xff0c;安装 samba。这个在…

一直想说的,技术职业化

最近后台有人一直跟我说&#xff0c;为什么不好好写一篇技术比较强的文章&#xff0c;说实话&#xff0c;最近时间比较紧张&#xff0c;早上 8 点出门&#xff0c;晚上12点左右到家。刚好今天整理了一个不错文章的列表&#xff0c;明天发出来&#xff0c;希望给学习的同学们有点…

MyBatis初级入门及常见问题

入门案例 创建maven工程 项目目录结构: 首先在maven的pom.xml导入Mybatis和MySQL的依赖坐标: <dependencies><!--Junit测试依赖--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</…

读书笔记:Information Architecture for the World Wide Web, 3rd Edition 北极熊 第一部分 1-3...

Introducing Information Architecture 信息架构简介 Chapter 1 Defining Information Architecture 信息架构的意义&#xff08;我们盖房子&#xff0c;之后&#xff0c;房子影响我们&#xff09; A DefinitionTablets, Scrolls, Books, and Libraries 石板、卷轴、书籍&#…

Mybatis执行流程分析_自定义简易Mybatis框架

自定义简易Mybatis框架 Mybatis执行流程分析 Mybatis代码编写流程: Mybatis配置文件加载过程: 需求分析及技术概述 根据上述的功能结构图, 得出如下需求: 1. 需要具有配置文件加载模块. 2. 支持使用构建者进行SessionFactory构建. 3. 支持使用工厂模式构建Session对象. 4.…

给大家推荐一个优质Linux内核技术公众号-Linux阅码场

作为一个Linux 技术公众号的作者&#xff0c;我觉得有义务推荐优秀的公众号&#xff0c;推广内容&#xff0c;希望对大家的学习有所帮助~Linux阅码场是一个专注Linux内核和系统编程与调试调优技术的公众号&#xff0c;它的文章云集了国内众多知名企业一线工程师的心得。无论你工…

图数据库_ONgDB图数据库与Spark的集成

快速探索图数据与图计算图计算是研究客观世界当中的任何事物和事物之间的关系&#xff0c;对其进行完整的刻划、计算和分析的一门技术。图计算依赖底于底层图数据模型&#xff0c;在图数据模型基础上计算分析Spark是一个非常流行且成熟稳定的计算引擎。下面文章从ONgDB与Spark的…

2019 高考填报志愿建议

2019 高考填报志愿建议1、城市很关键&#xff0c;在大城市上学和小地方上学会有很大的不同&#xff0c;现在很多毕业生毕业后会往北上广深跑&#xff0c;很多原因是因为这里的就业机会多&#xff0c;薪资比内地好太多了&#xff0c;如果你大学就能在这样的地方上学&#xff0c;…

韦东山:闲鱼与盗版更配,起诉到底绝不和解!

之前很多人问&#xff0c;我和韦老师是什么关系&#xff0c;我们是本家人&#xff0c;至于更深的关系&#xff0c;我也不知道&#xff0c;这次事件受老师委托&#xff0c;帮忙发文支持&#xff0c;看到的朋友&#xff0c;也希望给予转发支持&#xff0c;感激不尽~大学的时候&am…

Windows下搭建FTP服务器

一、什么是ftp? FTP 是File Transfer Protocol&#xff08;文件传输协议&#xff09;的英文简称&#xff0c;而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时&#xff0c;它也是一个应用程序&#xff08;Application&#xff09;。基于不同的操作系统有不…

Redis常见数据类型_Redis通用指令

Redis常见数据类型 redis本身就是一个Map结构, 所有数据都采用key:value的形式, redis中的数据类型指的是value的类型, key部分永远是字符串 string(类似Java String) string基本操作: set key value //存储数据 get key value //获取数据 del key value //删除数据 mset …

我关注的学习公众号

在这个激烈竞争的社会&#xff0c;职场上还有“一招鲜”么&#xff1f;面对35岁的“魔咒”&#xff0c;提升自我的路很多&#xff0c;学习是其中最为捷径的一条。只有不断学习新知识&#xff0c;才能保持进步。今天为大家整理了8个公众号&#xff0c;分别为各个领域的佼佼者&am…

如何让开关打开_安卓手机如何打开USB调试模式

点击上方“手机互联” 关注我吧&#xff01;什么是USB调试模式&#xff1f;USB调试模式是 安卓手机提供的一个用于开发工作的功能。使用该功能可在设备和安卓手机之间复制数据、在移动设备上安装应用程序、读取日志数据等等。默认情况下&#xff0c;USB 调试模式是关闭的&#…

Redis持久化_Redis事务_Redis删除策略

Redis持久化 Redis包含3中持久化方案: RDB, AOF, RDB与AOF混合使用 RDB RDB: 将内存中数据生成快照形式, 将其保存到.rdb文件中, 关注点是数据 使用命令执行RDB过程 在保存.rdb文件之前还需要修改redis.conf配置文件, 修改项如下: dbfilename dump.rdb //配置.rdb文件名, …

第一次线下活动总结

公众号建立有了一段时间了&#xff0c;今天是我们筹划的第一次线下聚会活动&#xff0c;活动发起人是公众号的一个读者&#xff0c;是我们的前辈&#xff0c;也是这次活动的赞助商&#xff0c;非常感谢&#xff0c;支付了聚餐了费用&#xff0c;这次第一届活动&#xff0c;当然…

初学者选黑卡还是微单_零基础,一篇读懂单反和微单

许多小白在选购相机时&#xff0c;常常会纠结选微单还是选单反。那么这次就来一篇通俗讲解&#xff1a;单反和微单有什么区别&#xff1f;谁更好&#xff1f;应该怎么选择&#xff1f;一、单反和微单有什么区别&#xff1f;在了解单反和微单的区别之前&#xff0c;我们先要了解…

Redis核心配置_Redis高级数据类型使用

Redis核心配置 服务端配置 daemonize yes|no //服务器是否已守护进程方式运行 bind 127.0.0.1 //绑定主机 port 6379 //设置端口 databases 16 //设置数据库数量 loglevel debug|verbose|notice|warning //设置日志级别 logfile 端口号.log //设置日志文件名 maxclients 1 //…