资讯专栏INFORMATION COLUMN

Java NIO 的前生今世 之二 NIO Channel 小结

JasonZhang / 2711人阅读

摘要:通常来说所有的的操作都是从开始的一个类似于一个和对比我们可以在同一个中执行读和写操作然而同一个仅仅支持读或写可以异步地读写而是阻塞的同步读写总是从中读取数据或将数据写入到中类型有文件操作操作操作操作使用在服务器端这些通道涵盖了和网络以及文件

Java NIO Channel

通常来说, 所有的 NIO 的 I/O 操作都是从 Channel 开始的. 一个 channel 类似于一个 stream.
java Stream 和 NIO Channel 对比

我们可以在同一个 Channel 中执行读和写操作, 然而同一个 Stream 仅仅支持读或写.

Channel 可以异步地读写, 而 Stream 是阻塞的同步读写.

Channel 总是从 Buffer 中读取数据, 或将数据写入到 Buffer 中.

Channel 类型有:

FileChannel, 文件操作

DatagramChannel, UDP 操作

SocketChannel, TCP 操作

ServerSocketChannel, TCP 操作, 使用在服务器端.
这些通道涵盖了 UDP 和 TCP网络 IO以及文件 IO.

基本的 Channel 使用例子:

public static void main( String[] args ) throws Exception
{
    RandomAccessFile aFile = new RandomAccessFile("/Users/xiongyongshun/settings.xml", "rw");
    FileChannel inChannel = aFile.getChannel();

    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = inChannel.read(buf);
    while (bytesRead != -1) {
        buf.flip();

        while(buf.hasRemaining()){
            System.out.print((char) buf.get());
        }

        buf.clear();
        bytesRead = inChannel.read(buf);
    }
    aFile.close();
}
FileChannel

FileChannel 是操作文件的Channel, 我们可以通过 FileChannel 从一个文件中读取数据, 也可以将数据写入到文件中.
注意, FileChannel 不能设置为非阻塞模式.

打开 FileChannel
RandomAccessFile aFile     = new RandomAccessFile("test.txt", "rw");
FileChannel      inChannel = aFile.getChannel();
从 FileChannel 中读取数据
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);
}
关闭

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

channel.close(); 
设置 position
long pos channel.position();
channel.position(pos +123);
文件大小

我们可以通过 channel.size()获取关联到这个 Channel 中的文件的大小. 注意, 这里返回的是文件的大小, 而不是 Channel 中剩余的元素个数.

截断文件
channel.truncate(1024);

将文件的大小截断为1024字节.

强制写入

我们可以强制将缓存的未写入的数据写入到文件中:

channel.force(true);
SocketChannel

SocketChannel 是一个客户端用来进行 TCP 连接的 Channel.
创建一个 SocketChannel 的方法有两种:

打开一个 SocketChannel, 然后将其连接到某个服务器中

当一个 ServerSocketChannel 接受到连接请求时, 会返回一个 SocketChannel 对象.

打开 SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://example.com", 80));
关闭
socketChannel.close(); 
读取数据
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 为异步模式, 这样我们的 connect, read, write 都是异步的了.

连接
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://example.com", 80));

while(! socketChannel.finishConnect() ){
    //wait, or do something else...    
}

在异步模式中, 或许连接还没有建立, connect 方法就返回了, 因此我们需要检查当前是否是连接到了主机, 因此通过一个 while 循环来判断.

读写

在异步模式下, 读写的方式是一样的.
在读取时, 因为是异步的, 因此我们必须检查 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();
监听连接

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

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    //do something with socketChannel...
}
非阻塞模式

在非阻塞模式下, 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...
        }
}
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 一样真正意义上的连接, 而是它会讲 DatagramChannel 锁住, 因此我们仅仅可以从指定的地址中读取或写入数据.

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

本文由 yongshun 发表于个人博客, 采用署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议.
非商业转载请注明作者及出处. 商业转载请联系作者本人
Email: yongshun1228@gmail.com
本文标题为: Java NIO 的前生今世 之二 NIO Channel 小结
本文链接为: segmentfault.com/a/1190000006824107

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65102.html

相关文章

  • Netty 源码分析之 一 揭开 Bootstrap 神秘红盖头 (客户端)

    摘要:目录源码分析之番外篇的前生今世的前生今世之一简介的前生今世之二小结的前生今世之三详解的前生今世之四详解源码分析之零磨刀不误砍柴工源码分析环境搭建源码分析之一揭开神秘的红盖头源码分析之一揭开神秘的红盖头客户端源码分析之一揭开神秘的红盖头服务器 目录 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 ...

    zhaot 评论0 收藏0
  • 源码之下无秘密 ── 做最好 Netty 源码分析教程

    摘要:背景在工作中虽然我经常使用到库但是很多时候对的一些概念还是处于知其然不知其所以然的状态因此就萌生了学习源码的想法刚开始看源码的时候自然是比较痛苦的主要原因有两个第一网上没有找到让我满意的详尽的源码分析的教程第二我也是第一次系统地学习这么大代 背景 在工作中, 虽然我经常使用到 Netty 库, 但是很多时候对 Netty 的一些概念还是处于知其然, 不知其所以然的状态, 因此就萌生了学...

    shenhualong 评论0 收藏0
  • Netty 源码分析之 一 揭开 Bootstrap 神秘红盖头 (服务器端)

    摘要:目录源码分析之番外篇的前生今世的前生今世之一简介的前生今世之二小结的前生今世之三详解的前生今世之四详解源码分析之零磨刀不误砍柴工源码分析环境搭建源码分析之一揭开神秘的红盖头源码分析之一揭开神秘的红盖头客户端源码分析之一揭开神秘的红盖头服务器 目录 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 ...

    张金宝 评论0 收藏0
  • Netty 源码分析之 三 我就是大名鼎鼎 EventLoop(一)

    摘要:目录源码之下无秘密做最好的源码分析教程源码分析之番外篇的前生今世的前生今世之一简介的前生今世之二小结的前生今世之三详解的前生今世之四详解源码分析之零磨刀不误砍柴工源码分析环境搭建源码分析之一揭开神秘的红盖头源码分析之一揭开神秘的红盖头客户端 目录 源码之下无秘密 ── 做最好的 Netty 源码分析教程 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NI...

    livem 评论0 收藏0
  • Java NIO 前生今世 之一 简介

    摘要:简介是由引进的异步由以下几个核心部分组成和的对比和的区别主要体现在三个方面基于流而基于操作是阻塞的而操作是非阻塞的没有概念而有概念基于与基于传统的是面向字节流或字符流的而在中我们抛弃了传统的流而是引入了和的概念在中我只能从中读取数据到中或将 简介 Java NIO 是由 Java 1.4 引进的异步 IO.Java NIO 由以下几个核心部分组成: Channel Buffer Se...

    李义 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<