网络编程之NIO

概述:

网络编程IO主要分为:同步、异步、阻塞、非阻塞。

image.png
在Java1.4之前的I/O系统中,提供的都是面向流的I/O系统,系统一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消费一个字节的数据,面向流的I/O速度非常慢,而在Java 1.4中推出了NIO,这是一个面向块的I/O系统,系统以块的方式处理处理,每一个操作在一步中产生或者消费一个数据库,按块处理要比按字节处理数据快的多。
在NIO中有几个核心对象需要掌握:缓冲区(Buffer)、通道(Channel)、选择器(Selector)。
在这里插入图片描述

Channel

channel是个双向通道和BIO的Stream不同,Stream是单向通道。但他们都用于数据的读写。
Channel的实现

FileChannel (从文件中)

DatagramChannel (从UDP中)

SocketChannel (通过TCP读写)

ServerSocketChannel (监听新进来的TCP连接)

打开 SocketChannel:

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

从 SocketChannel 读取数据:

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

写入 SocketChannel:

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);
}

ServerSocketChannel

通过 ServerSocketChannel.accept() 方法监听新进来的连接
在非阻塞模式下,accept() 方法会立刻返回,如果还没有新进来的连接,返回的将是null。 因此,需要检查返回的SocketChannel是否是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...
    }
}

Buffer

有 3 个非常重要的属性:capacity、limit、position。

创建 Buffer:

三种实现:allocate(int capacity),wrap(array),allocateDirect(int capacity)
image.png
向 Buffer 写入数据:

提供了 #put(…) 方法,对Buffer 写入数据。

从Channel 的数据写入到 Buffer 中,是一个读操作,因为数据是从外部( 文件或者网络等 )读取到内存中。也就是从外部写入buffer中。

int num = channel.read(buffer);

从 Buffer 读取数据:

提供了 #get(…) 方法,从 Buffer 读取数据。

来向 Channel 的写入 Buffer 中的数据,是个写操作。因为数据是从内存中写入到外部( 文件或者网络等 )。也就是channel从buffer中读数据。

int num = channel.write(buffer);

flip(),从写模式切换到读模式。读取buffer中的数据,往外写数据。

rewind(),可以重置 position 的值为 0 。因此,我们可以重新读取和写入 Buffer 了。

clear(),可以“重置” Buffer 的数据。因此,我们可以重新读取和写入 Buffer 了。

Selector

首先,需要将 Channel 注册到 Selector 中,这样 Selector 才知道哪些 Channel 是它需要管理的。

之后,Selector 会不断地轮询注册在其上的 Channel 。如果某个 Channel 上面发生了读或者写事件,这个 Channel 就处于就绪状态,会被 Selector 轮询出来,然后通过 SelectionKey 可以获取就绪 Channel 的集合,进行后续的 I/O 操作。

创建 Selector:

Selector selector = Selector.open();

注册 Chanel 到 Selector 中:

channel.configureBlocking(false); // <1>
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

SelectionKey 类:

interest set :感兴趣的事件集合

ready set :就绪的事件集合

attachment :可选的附加对象

选择 Channel:

提供三种类型的选择( select )方法

获取可操作的 Channel:

Set selectedKeys = selector.selectedKeys();

唤醒 Selector 选择:

只要让其它线程在第一个线程调用 select() 方法的那个 Selector 对象上,调用该 Selector 的 #wakeup() 方法,进行唤醒该 Selector 即可。

那么,阻塞在 #select()方法上的线程,会立马返回。

关闭 Selector:

Selector 相关的所有 SelectionKey 都会失效。

Selector 相关的所有 Channel 并不会关闭。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值