NIO的理解

1、简介

       新的输入/输出库是JKD1.4中引入的。NIO弥补了原来同步阻塞IO的不足,它在标准Java代码中提供了高速的、面向块的I/O.通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO不用使用本机代码就可以利用低级优化,这是原来的IO包无法做到的。

下面是一些NIO的概念:

缓冲区Buffer

        Buffer是一个对象,它包含一些要写入或者要读出的数据。在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的;在写入数据时,写到缓冲区中。任何时候访问NIO中的数据,都是通过缓冲区进行操作。

       Buffer其实是一个数组,包括字节缓冲区、字符缓冲区等。

通道Channel

       Channel是一个通道,网络数据可以通过它读取和写入数据。这里要说一下它和传统IO中流的区别,流是单向的,它只是一个方向上的移动,它要么是InputStream的子类,要么是OutputStream的子类;而channel是双向的,可以同时进行读和写的操作,而不阻塞。

       channel大体上分为两类:用于网络读写的SelectableChannel和用于文件操作的FileChannel。

多路复用选择器Selector

      Selector是Java NIO编程的基础,它提供选择已经就绪的任务的能力。Selector会不断的轮询注册在其上面的Channel,只要channel上面有新的TCP连接接入,读和写事件,这个channel就处于就绪状态,它就会被Selector轮询出来,再通过SelectionKey获取就绪channel的集合,进行后续的IO操作。

       一个Selector可以同时轮询多个channel,它使用了epoll()代替传统的select实现,使得它没有最大连接句柄的限制,所以说只要开启一个线程来运行selector的轮询,就可以接入成千上万的客户端。

       下面我们结合代码看一下NIO的一个执行流程:

1、打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的父管道:

ServerSocketChannel   channelSvr=new ServerSocketChannel.open();
2、绑定监听端口,设置连接为非阻塞模式,代码如下:

channelSvr.socket().bind(new InetSocketAddress(InetAddress.getByName("IP"),port));

channelSvr.configureBlocking(false);

3、创建Reactor线程,创建多路复用器并启动线程,代码如下:

Selector selector=Selector.open();

New Thread(new ReactorTask()).start();

4、将ServerSocketChannel注册到Reactor线程的多路复用器Selector上,监听ACCEPT事件,代码如下:

SelectionKey key=accpetorSvr.register(selector,SelectionKey.OP_ACCEPT,ioHandler);

5、多路复用器在线程run方法的无限循环体内轮询准备就绪的Key,代码如下:

Set selectedKeys=selector.selectedKeys();

Iterator it=selectedKeys.iterator();

while(it.hashNext){

SelectionKey key=(SelectionKey)it.next();

}

6、多路复用器监听到有新的客户端接入,处理新的接入请求,完成TCP三次握手,建立物理连接:

Selector selector=Selector.open();
New Thread(new ReactorTask()).start();

7、多路复用器在线程run方法的无限循环体内轮询准备就绪的Key,代码如下:

int num=selector.select();
Set selectedKeys=selector.selectedKeys();
Iterator it=selectedKeys.iterator();
while(it.hashNext()){
SelectionKey key=(SelectionKey)it.next();
}

8、接收connect 事件进行处理,示例代码如下:

if(key.isConnectable())

9、判断连接结果,如果连接成功,注册读事件到多路复用器,示例代码如下:

if(channel.finishConnect());

10、注册读事件到多路复用器,示例代码如下:

clientChannel.register(selector,SelectionKey.OP_READ,ioHandler);

11、异步读客户端请求消息到缓冲区,示例代码如下:

int readNumer=channel.read(receivedBuffer);


12、对ByteBuffer 进行编码,如果有半包消息接收缓冲区Reset,继续读取后续的报文,将解码成功的消息封装成Task,投递到业务线程池中,进行业务逻辑编排示例代码如下:

Object message=null;
while(buffer.hashRemain()){
byteBuffer.mark();
Object message=decode(byteBuffer);
if(message==null){
byteBuffer.reset();
break;
}
}
if(!byteBuffer.hashRemain())
byteBuffer.clear();
else
byteBuffer.compact();
if(messageList!=null&!messageList.isEmpty()){
for(Object messageE:messageList)
handlerTask(messageE);
}

13、将POJO对象encode成ByteBuffer,调用SocketChannel的异步write接口,将消息异步发送给客户端,示例代码如下:

socketChannel.writer(buffer);



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值