zoukankan      html  css  js  c++  java
  • Channel (Java NIO)

    【正文】netty源码死磕1.3: 

    Java NIO Channel

    1. Java NIO Channel

    1.1. Java NIO Channel的特点

    和老的OIO相比,通道和NIO流(非阻塞IO)主要有以下几点区别:

    (1)OIO流一般来说是单向的(只能读或者写),通道可以读也可以写。

    (2)OIO流值读写阻塞的,而通道可以异步读写。

    (3)通道总是基于缓冲区Buffer来读写。

    1.2. Channel类型

    下面列出Java NIO中最重要的集中Channel的实现:

    (1)FileChannel

    (2)DatagramChannel

    (3)SocketChannel

    (4)ServerSocketChannel

    四种通道的说明如下:

    FileChannel用于文件的数据读写。

    DatagramChannel用于UDP的数据读写。

    SocketChannel用于TCP的数据读写。

    ServerSocketChannel允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel。

    这个四种通道,涵盖了 UDP 和 TCP网络 IO以及文件 IO的操作。下面从通道的新建、读取、写入、关闭等四个操作,四种通道进行简单的介绍。

    1.3. FileChannel

    FileChannel 是操作文件的Channel,我们可以通过 FileChannel 从一个文件中读取数据,也可以将数据写入到文件中。

    注意,FileChannel 不能设置为非阻塞模式。

    操作一:打开 FileChannel通道

    RandomAccessFile aFile     = new RandomAccessFile("test.txt","rw");
    
    FileChannel      inChannel = aFile.getChannel();
    

    操作二:读取数据

    ByteBuffer buf = ByteBuffer.allocate(48);
    
    int bytesRead = inChannel.read(buf);
    

    操作三:写入数据

    String newData = "New String to write to file..." + System.currentTimeMillis();
    
    ByteBuffer buf = ByteBuffer.allocate(48);
    
    buf.clear();
    
    buf.put(newData.getBytes());
    
    buf.flip();
    
    while(buf.hasRemaining())
    
    {
    
        channel.write(buf);
    
    }
    

    操作四:关闭

    channel.close();

    当我们对 FileChannel 的操作完成后,必须将其关闭。

    操作五:强制刷新磁盘

    channel.force(true);

    FileChannel的force()方法将所有未写入的数据从通道刷新到磁盘中。在你调用该force()方法之前,出于性能原因,操作系统可能会将数据缓存在内存中,因此您不能保证写入通道的数据实际上写入磁盘。

    1.4. SocketChannel

    有两种Socket通道,一个是客户端的SocketChannel,一个是负责服务器端的Socket通道ServerSocketChannel。SocketChannel与OIO中的Socket类对应,ServerSocketChannel对应于OIO中的ServerSocket类相NIO。

    两种Socket通道新增的通道都支持阻塞和非阻塞两种模式。在阻塞模式下的通道的创建、关闭、读写操作如下:

    操作一:创建

    SocketChannel socketChannel = SocketChannel.open();

    socketChannel.connect(new InetSocketAddress("127.0.0.1",80));

    这个是客户端的创建。当一个服务器端的ServerSocketChannel 接受到连接请求时,也会返回一个 SocketChannel 对象。

    操作二:读取

    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = socketChannel.read(buf);

    如果 read()返回 -1,那么表示连接中断了.

    操作三:写入数据

    String newData = "New String to write to file..." + System.currentTimeMillis();
    
    ByteBuffer buf = ByteBuffer.allocate(48);
    
    buf.clear();
    
    buf.put(newData.getBytes());
    
    buf.flip();
    
    while(buf.hasRemaining()) {
    
        channel.write(buf);
    
    }
    

    操作四:关闭

    socketChannel.close();

    在非阻塞模式,我们可以设置 SocketChannel 为异步模式,这样我们的 connect,read,write 都是异步的了.

    操作一:连接

    socketChannel.configureBlocking(false);
    
    socketChannel.connect(new InetSocketAddress("127.0.0.1",80));
    
    while(! socketChannel.finishConnect() ){
    
        //wait,or do something else...
    
    }
    

    在异步模式中,或许连接还没有建立,socketChannel.connect 方法就返回了,因此我们不断的自旋,检查当前是否是连接到了主机。

    操作二:非阻塞读写

    在异步模式下,读写的方式是一样的.

    在读取时,因为是异步的,因此我们必须检查 read 的返回值,来判断当前是否读取到了数据.

    ServerSocketChannel

    ServerSocketChannel 顾名思义,是用在服务器为端的,可以监听客户端的 TCP 连接,例如:

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
    serverSocketChannel.socket().bind(new InetSocketAddress(9999));
    
    while(true){
    
        SocketChannel socketChannel =
    
                serverSocketChannel.accept();
    
        //do something with socketChannel...
    
    }
    

    操作四:关闭

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

    serverSocketChannel.close();

    1.4.1. 监听连接

    我们可以使用ServerSocketChannel.accept()方法来监听客户端的 TCP 连接请求,accept()方法会阻塞,直到有连接到来,当有连接时,这个方法会返回一个 SocketChannel 对象:

    while(true){
    
        SocketChannel socketChannel =
    
                serverSocketChannel.accept();
    
        //do something with socketChannel...
    
    }
    
    1.4.2. 非阻塞模式

    在非阻塞模式下,accept()是非阻塞的,因此如果此时没有连接到来,那么 accept()方法会返回null:

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
    serverSocketChannel.socket().bind(new InetSocketAddress(9999));
    
    serverSocketChannel.configureBlocking(false);
    
    while(true){
    
        SocketChannel socketChannel =
    
                serverSocketChannel.accept();
    
        if(socketChannel != null){
    
            //do something with socketChannel...
    
            }
    
    }
    

    1.5. DatagramChannel

    DatagramChannel 是用来处理 UDP 连接的.

    操作一:打开

    DatagramChannel channel = DatagramChannel.open();

    channel.socket().bind(new InetSocketAddress(9999));

    操作二:读取数据

    ByteBuffer buf = ByteBuffer.allocate(48);
    
    buf.clear();
    
    channel.receive(buf);
    

    操作三:发送数据

    String newData = "New String to write to file..."
    
                        + System.currentTimeMillis();
    
    ByteBuffer buf = ByteBuffer.allocate(48);
    
    buf.clear();
    
    buf.put(newData.getBytes());
    
    buf.flip();
    
    int bytesSent = channel.send(buf,new InetSocketAddress("example.com",80));
    

    连接到指定地址

    因为 UDP 是非连接的,因此这个的 connect 并不是向 TCP 一样真正意义上的连接,因此我们仅仅可以从指定的地址中读取或写入数据.

    channel.connect(new InetSocketAddress("example.com",80));


    源码:


    代码工程:  JavaNioDemo.zip

    下载地址:在疯狂创客圈QQ群文件共享。




    无编程不创客,无案例不学习。疯狂创客圈,一大波高手正在交流、学习中!

    疯狂创客圈 Netty 死磕系列 10多篇深度文章博客园 总入口】  QQ群:104131248

  • 相关阅读:
    学习Spring Boot:(八)Mybatis使用分页插件PageHelper
    学习Spring Boot:(七)集成Mybatis
    学习Spring Boot:(六) 集成Swagger2
    学习Spring Boot:(五)使用 devtools热部署
    学习Spring Boot:(四)应用日志
    学习Spring Boot:(三)配置文件
    学习Spring Boot:(二)启动原理
    学习Spring Boot:(一)入门
    Java8 新特性Stream 的学习和使用方法
    简易promise的实现(二)
  • 原文地址:https://www.cnblogs.com/crazymakercircle/p/9826883.html
Copyright © 2011-2022 走看看