深入理解NIO系列 - Channel详解

Channel简介

在Java NIO中,主要有三大基本的组件:Buffer、Channel和Selector,前面两篇文章我们具体介绍了SelectorBuffer,老规矩,就让我们继续慢慢地揭开Channel的神秘面纱吧!

在Java NIO的世界中,Selector是中央控制器,Buffer是承载数据的容器,而Channel可以说是最基础的门面,它是本地I/O设备、网络I/O的通信桥梁,只有搭建了这座桥梁,数据才能被写入Buffer,连接才能被Selector控制,

Channel这座桥梁分别为本地I/O设备和网络I/O提供了以下实现,并且和Java IO体系的类是一一对应的:

  1. 网络I/O设备:
    • DatagramChannel:读写UDP通信的数据,对应DatagramSocket类
    • SocketChannel:读写TCP通信的数据,对应Socket类
    • ServerSocketChannel:监听新的TCP连接,并且会创建一个可读写的SocketChannel,对应ServerSocket类
  2. 本地I/O设备:
    • FileChannel:读写本地文件的数据,不支持Selector控制,对应File类

其类继承结构如下图:

Channel类继承结构图

  1. 从上图中我们可以看出前面讲述的四个类都是被定义为抽象的,这些类中只是声明了可操作的接口;主要是在不同的操作系统当中,其实际操作本地I/O和网络I/O在实现上会有根本性的差异,就拿Windows和Unix来说,两者的文件系统管理是不一致的(想了解三者I/O架构上的区别可参考Unix,Linux,Windows的IO架构
  2. Channel接口实现了Closeable接口,并且本身还定义isOpen()方法,标识所有的Channel都是可以被主动关闭
  3. InterruptibleChannel接口声明了Channel是可以被中断的
  4. SelectableChannel接口声明了Channel是可以被选择的(即支持Selector控制),而FileChannel是没有实现该接口的
  5. WritableByteChannelReadableByteChannel接口分别提供了写操作和读操作的API,且是基于Buffer的
  6. ScatteringByteChannelGatheringByteChannel接口允许您委托操作系统来完成辛苦活:将读取到的数据分开存放到多个存储桶(bucket)或者将不同的数据区块合并成一个整体。这是一个巨大的成就,因为操作系统已经被高度优化来完成此类工作了。它节省了您来回移动数据的工作,也就避免了缓冲区拷贝和减少了您需要编写、调试的代码数量。其分别定义了write(ByteBuffer[] srcs, int offset, int length)read(ByteBuffer[] dsts, int offset, int length)
  7. SeekableByteChannel接口用于控制本地文件的position
  8. NetworkChannel接口标识了该Channel是属于网络I/O

ServerSocketChannel

让我们从最简单的ServerSocketChannel来开始对socket通道类的讨论。以下是ServerSocketChannel的完整 API:

public abstract class ServerSocketChannel extends AbstractSelectableChannel {
   
    public static ServerSocketChannel open() throws IOException
    public abstract ServerSocket socket();
    public abstract SocketChannel accept() throws IOException;
    //支持的SelectionKey类型,返回OP_ACCEPT
    public final int validOps()
}

ServerSocketChannelServerSocket一样是socket监听器,其主要区别前者可以运行在非阻塞模式下运行;

    // 创建一个ServerSocketChannel,将会关联一个未绑定的ServerSocket
    public static ServerSocketChannel open() throws IOException {
   
        return SelectorProvider.provider().openServerSocketChannel();
    }

ServerSocketChannel的创建也是依赖底层操作系统实现,其实现类主要是ServerSocketChannelImpl,我们来看看其构造方法

class ServerSocketChannelImpl extends ServerSocketChannel implements SelChImpl {
   

    private final FileDescriptor fd;
    private int fdVal;
    // 这里忽略一些变量
    .....
    private int state = -1;
                                          ServerSocketChannelImpl(SelectorProvider var1) throws IOException {
   
        super(var1);
        // 创建一个文件操作符
        this.fd = Net.serverSocket(true);
        // 得到文件操作符是索引
        this.fdVal = IOUtil.fdVal(this.fd);
        this.state = 0;
    }

新建一个ServerSocketChannelImpl其本质是在底层操作系统创建了一个fd(即文件描述符),相当于建立了一个用于网络通信的通道,通过这个通道我们可以和外部网络进行通信;

当然上述操作只是抢占了一个通道,它是无法和外部通信的;我们知道,在实际网络交互中,必须通过端口才能通信,所以呢,下一步我们来看看如何绑定端口

ServerSocketChannel貌似没有bind()方法来绑定端口,上面我们提到它在创建时会新建一个fd,其本质对应了ServetSocket对象,我们看ServerSocketChannel的API能看到通过socket()对象能获取到ServetSocket,此时我们只要调用socket的bind()方法绑定即可

ServerSocketChannel#socket#bind(InetSocketAddress)

ServerSocketChannel最主要的作用就是用于监听TCP连接,其API中也有相应的accept()方法来获取TCP连接

public SocketChannel accept() throws IOException {
   
    // 忽略一些校验及无关代码
    ....
    
    SocketChannelImpl var2 = null;
    // var3的作用主要是说明当前的IO状态,主要有
    /**
    * EOF = -1;
    * UNAVAILABLE = -2;
    * INTERRUPTED = -3;
    * UNSUPPORTED = -4;
    * THROWN = -5;
    * UNSUPPORTED_CASE = -6;
    */
    int var3 = 0;
    // 这里本质也是用fd来获取连接
    FileDescriptor var4 = new FileDescriptor();
    // 用来存储TCP连接的地址信息
    InetSocketAddress[] var5 = new InetSocketAddress[1];
    
    try {
   
        // 这里设置了一个中断器,中断时会将连接关闭
        this.begin();
        // 这里当IO被中断时,会重新获取连接
        do {
   
            var3 = this.accept(this.fd, var4, var5);
        } while(var3 == -3 && this.isOpen());
    }finally {
   
        // 当连接被关闭且accept失败时或抛出AsynchronousCloseException
        this.end(var3 > 0);
        // 验证连接是可用的
        assert IOStatus.check(var3);
    }
    
    if (var3 < 1) {
   
        return null;
    } {
   
        // 默认连接是阻塞的
        IOUtil
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值