NIO 之 Channel实现原理

相关文章

IO、NIO、AIO 内部原理分析 NIO 之 Selector实现原理 NIO 之 ByteBuffer实现原理

NIO概述

Java NIO 由以下几个核心部分组成:

  • Channels
  • Buffers
  • Selectors

在传统IO中,流是基于字节的方式进行读写的。 在NIO中,使用通道(Channel)基于缓冲区数据块的读写。

Channel 和 IO 流的区别

Java NIO的通道类似IO中的流,但又有些不同:

  1. 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
  2. 通道可以异步地读写。
  3. 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

Channel 实现类

下面是JAVA NIO中的一些主要Channel的实现:

  1. FileChannel FileChannel 从文件中读写数据。
  2. DatagramChannel DatagramChannel 能通过UDP读写网络中的数据。
  3. SocketChannel SocketChannel 能通过TCP读写网络中的数据。
  4. ServerSocketChannel ServerSocketChannel可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

FileChannel 是基于文件的Channel。 DatagramChannel、SocketChannel、ServerSocketChannel 都是基于网络流的Channel。

下面我们主要介绍关于基于网络流的SocketChannel 和 ServerSocketChannel 。

register 方法

基于网络流的Channel中提供了register方法。 使用示例:

channel.register(selector,  Selectionkey.OP_READ, ByteBuffer.allocate(64));

代码的写法感觉是要将selector对象注册到channel中。其实正好相反,应该是将channel注册到selector中。 下面通过代码分析: ![将channel 注册到 selector 中]

channel 的 register 方法

1.首先判断 selector 中是否已经注册此 channel ,如果注册过就更新注册的事件和attach附件信息。

  1. 如果 selector 没有注册过 channel ,则将 channel 注册到 selector 中。

configureBlocking 方法

  public final SelectableChannel configureBlocking(boolean block)

Channel 默认使用阻塞模式,通过 configureBlocking 方法可以将该 Channel 设置成非阻塞模式。

//设置成非阻塞模式
channel.configureBlocking(false);

ServerSocketChannel

ServerSocketChannel只支持入站连接请求。不提供读取、写入数据功能。

accept 方法

public abstract SocketChannel accept()

accept()可以在阻塞或非阻塞模式下操作。

  • 阻塞模式 accept()方法等待入站连接。然后它接受一个连接,并返回到远程客户端的一个SocketChannel。在建立连接前你无法做任何操作。

示例:

public static void blockMode(){
    try {
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        //绑定要监控的端口
        serverChannel.bind(new InetSocketAddress(9000));
        //获取入站请求socketChannel
        SocketChannel clientChannel = serverChannel.accept();
        ...
    } catch (Exception e) {
    }
}
  • 非阻塞模式 如果没有入站连接,accept()返回null。非阻塞模式一般和Selector结合使用。

示例:

public static void nonBlockMode(){
    try {
        //创建一个selector
        Selector selector = Selector.open();
        //创建一个ServerSocketChannel
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        //设置成非阻塞模式
        serverChannel.configureBlocking(false);
        //绑定要监控的端口
        serverChannel.bind(new InetSocketAddress(9000));
        //接受入站请求
        serverChannel.accept();
        //将 serverChannel 注册到 selector 上
        serverChannel.register(selector, SelectionKey.OP_READ);
    } catch (Exception e) {
    }
}

SocketChannel

SocketChannel 类可以读写TCP Socket。数据必须编码到ByteBuffer对象中来完成读写。

open 方法

  1. public static SocketChannel open() 创建一个SocketChannel,但不连接(没有指定目标ip和port),如果想使用非阻塞模式则使用该方法创建SocketChannel对象。

示例:

public static void nonSocketChannel() throws Exception {
    SocketChannel client = SocketChannel.open();
    client.configureBlocking(false);
    client.connet(new InetSocketAddress("192.168.1.10", 9000));
}
  1. public static SocketChannel open(SocketAddress remote) 构造SocketChannel 对象,并使用阻塞模式连接目标地址。

connect 方法

public abstract boolean connect(SocketAddress remote)

非阻塞模式下,如果要连接远程服务器必须使用 connect 方法进行连接。

finishConnect 方法

public abstract boolean finishConnect()

阻塞模式:直接返回true,因为在构造SocketChannel的时候已经建立连接了。 非阻塞模式下:必须调用此方法来判断该方法是否已经建立网络连接,只有建立网络连接后才能进行读写操作。

判断网络连接状态

public abstract boolean isConnected() 判断是否已经连接。 public abstract boolean isConnectionPending() 判断连接是否正在连接状态

read 方法

  • public int read(ByteBuffer buf) 将 SocketChannel 中的数据读入填充到 buf 中
  • public final long read(ByteBuffer[] dsts) 将 SocketChannel 中的数据读入填充到 dsts[] 数组。按照数组顺序进行填充。
  • public long read(ByteBuffer[] dsts, int offset, int length) 将 SocketChannel 中的数据填充到 dsts[] 数组中,从第数组中的offset坐标开始填充,填充 length 个Bytebuffer。

write 方法

  • public int write(ByteBuffer src) 将 buf 中的数据写入到 SocketChannel 中
  • public final long write(ByteBuffer[] srcs) 将 srcs[] 数组中的数据写入到 SocketChannel 中,按照数组顺序一个一个写入。
  • public long write(ByteBuffer[] srcs, int offset, int length) 将 srcs[] 数组中的数据写入到 SocketChannel 中,从数组中 offset 坐标开始,写入length个ByteBuffer对象。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏YG小书屋

ES节点丢失导致实时数据导入速度特别慢

9532
来自专栏JavaEdge

Tomcat架构解析之3 Connector NIOAcceptorPollerWorkerNioSelectorPool

2904
来自专栏社区的朋友们

TAF 必修课(二):Reactor多线程模型

最近看了很多文章和分享,非常受益, 实习所做项目主要用到了TAF,有必要对之前的学习做个梳理和总结,网络线程模型及请求接收过程,必修亦为基础、通用,故取其名。

1.1K1
来自专栏清晨我上码

第八节 netty前传-NIO 几种channel介绍02

java bio中的serversocket和nio中的socket有些类似,两者使用可参考如下: BIO模式

932
来自专栏蓝天

基于zookeeper的主备切换方法

继承CZookeeperHelper即可快速实现主备切换: https://github.com/eyjian/mooon/blob/master/mooo...

1542
来自专栏大数据架构

Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式

2105
来自专栏chenssy

【死磕Netty】-----NIO基础详解

原文出处http://cmsblogs.com/ 『chenssy』 转载请注明原创出处,谢谢! Netty 是基于Java NIO 封装的网络通讯框架,只有充...

4776
来自专栏清晨我上码

第六节 netty前传-NIO Selector

可以使用单个线程来处理多个channel来节省资源。对于操作系统而言,线程之间切换是昂贵的,并且每个线程也占用操作系统中的一些资源(存储器)。 因此,使用的线程...

1712
来自专栏SAP最佳业务实践

SAP最佳业务实践:SD–外贸出口处理(118)-4发货

一、VL10C创建交货 1. 在 销售订单项目 屏幕上,进行以下输入: 字段名称用户操作和值注释装运点/接收点<装运点> 交货创建日期(从)<输入交货创建日期>...

41810
来自专栏Linux驱动

Linux-使用patch命令给uboot打补丁(3)

patch:修改文件,让用户对原文件打补丁 用法   patch -p[剥离层级]  <[补丁文件] 打补丁示例: u-boot-1.1.6_jz2440.p...

3059

扫码关注云+社区

领取腾讯云代金券