Java NIO网络编程原理(9)--待整理

本篇主要介绍以下内容:

一 服务端使用NIO IO复用与传统一客户一线程方式的比较

二 NIO 主要API简介

三 服务端模型

四 mina、netty框架的分析

 

一 服务端使用NIO非阻塞网络编程与传统一客户一线程方式的比较

 

1 IO复用与并发编程

(1)连接上的消息处理,可以分为两个阶段:等待消息准备好(IO就绪)、消息处理(实际的IO操作);多路复用就是同时处理多个连接的等待IO就绪事件.

(2)传统并发编程,当使用默认的阻塞套接字时(例如1个线程捆绑处理1个连接),往往是把这两个阶段合而为一,这样操作套接字的代码所在的线程就得睡眠来等待消息准备好,这导致了高并发下线程会频繁的睡眠、唤醒,系统进行大量不必要的线程上下文切换,从而影响了CPU的使用效率。

(3)高并发编程方法当然就是把两个阶段分开处理。即,等待消息准备好的代码段,与处理消息的代码段是分离的。当然,这也要求套接字必须是非阻塞的,否则,处理消息的代码段很容易导致条件不满足时,所在线程又进入了睡眠等待阶段。那么问题来了,等待消息准备好这个阶段怎么实现?它毕竟还是等待,这意味着线程还是要睡眠的!解决办法就是,线程主动查询,或者★让1个线程为所有连接而等待,这样,当我们的线程被唤醒执行时,就一定是有一些连接准备好被我们的代码执行了.

 

2 IO复用与一客户一线程方式的比较

(1)一客户一线程的服务方式需要创建大量的线程,而使用非阻塞IO,只需要一个或少许(用线程池)来处理实际的IO操作就可以了;

(2)在NIO中使用多线程,主要目的已不是为了应对每个客户端请求而分配独立的服务线程,而是通过多线程充分使用用多个CPU的处理能力和处理中的等待时间,达到提高服务能力的目的。

 

 

二 NIO 主要API简介

 

1 Buffer

(1)索引: mark,position:下一个可读/写的位置,limit:第一个不可读/写的位置,capacity,"准备"则是改变position,limit的值为读写作准备

(2)get/put方法: 在p位置获取或加入一个byte,然后p++,如果==limit的值,则判断为下溢或上溢

 

2 Channel

        一个channel实例代表了一个可轮询的IO目标,如套接字(或一个文件,设备等)。channel的read方法隐式调用Buffer.put方法,write方法隐式调用get方法.把Channel设计为使用Buffer实例来传递数据,使用Buffer有两个主要好处:第一,与读写缓冲区相关联的系统开锁暴露给了程序员;第二,一些对java对象的特殊Buffer映射操作能够直接操作底层平台的资源,如操作系统的缓冲区,这些操作节省了在不同地址空间中复制数据的开锁.

        NIO的Channel抽象的一个重要特征是可以通过配置它的阻塞行为,以实现非阻塞式的信道

 

channel非阻塞操作中读写一次的byte量

(1)channel读时,取决于底层socket的buffer的字节数量和byteBuffer的剩余容量

(2)channel写时,取决于底层socket的buffer的容量及byteBuffer的剩余字节量

 

3 Selector

(1)Selector类可用于避免使用非阻塞式客户端中很浪费资源的"忙等"方法.因为它的select方法会阻塞等待,直到至少有一个信道可以进行IO操作,并指出是哪个信道.可以为select方法指定超时时间,如果经过一段时间后仍然没有信道准备好,那么select()方法就返回0,并允许程序继续执行其他任务.

 

(2)一个信道可以注册多个Selector实例因此可以有多个关联的SelectionKey实例

 

4 SelectionKey

        SelectionKey对应一个channel,并保存了感兴趣的操作,调用Selector.select()方法会返回有注册这个selector的并且已有就绪操作(接受连接或者读写等)的channel对应的SelectionKey的集合,以此可以就绪的IO源进行处理

 

(1)兴趣操作集有四种: 接收,连接,读,写

(2)通过调用其cancel()方法可以显式地将键设置为无效,调用其isValid()方法可以检测一个键的有效性,无效的键将沥青到选择器的注销键集中,并在一次调用任一种形式的select()方法或close()方法时从键集中移除,意味着与它关联的信道也不再受监听.

 

 

三 服务端模型

 

st1: 打开一个Selector

st2: 为每个需要监听的端口打开一个ServerSocketChannel,绑定对应的端口,配置为非阻塞,并使用每个ServerSocketChannel的register方法把SelectionKey.OP_ACCEPT事件注册到那个selector里

st3: 使用一个while(true)循环,做以理的事情:

(1) 调用Selector的select()方法,此方法会阻塞,当其返回并且返回值大于0时(如果设置了阻塞超时,可能返回0),那么做第(2)步事情

(2) 调用Selector的selectedKeys().iterator()方法,并迭代每一个SelectionKey,判断其是否可接收,可读,可写,并根据可用事件的类型做相应的事情.

(3)要将处理过的SelectionKey从iterator移除.因为select()操作只是向Selector所关联的键集合中添加元素,如果不移除处理过的键,会保留下来,造成错误

 

st4: 根据可用事件的类型做相应的事情

(1)对于可接收的SelectionKey,对应的Channel是ServerSocketChannel,可以调用一次accept()接收一个SocketChannel,然后将它设置为非阻塞,并向SelectionKey.selector()注册相应的感兴趣事件,同时将缓存作为附件加入.

(2)对于可读事件,对应的Channel是SocketChannel,可以从附件中获取到缓存数组,并对通道进行读取数据,如果读到-1,那么表示对应终端已经关闭,就调用Channel的close方法,如果读取正常,那么向这个SelectionKey声明感兴趣的事件(读或写,或者两者),即SelectionKey.interestOps(read | write);

(3)对于可写事件,可以类比读.

 

注: DatagramChannel不能注册连接操作,实际上也不需要,因为DatagramChannel的connect()方法只起到限制发送和接收终端的作用,它是立即返回的,并不涉及到网络传输

 

 

四 mina、netty框架的分析

 

1 mina

在工作中都是用原生的java socket/nio 的api,有看过mina的文档,发现:优点很多,简化服务端的开发步骤,提供额外的通用业务功能,如安全通信,日志记录,图片传送,对象传送 

 

(1) NioSocketAcceptor: 这是创建非阻塞服务器端的类,类似与java中的ServerSocket,非阻塞I/O,是java5里提供的一组新的API,意思是我们的服务器不用像以前那样调用accept()方法,阻塞等待了

 

(2) NioSocketConnector:功能似于jdk中的Socket类,当然,也是非阻塞的读取数据

 

(3) IoFilterChainBuilder:对接收到的数据进行过滤的创建器,用以设定通信时的协议

IoFilter的作用:

(1)记录事件的日志(这个在本文中关于LoggingFilter的讲述中会提到)

(2)测量系统性能

(3)信息验证

(4)过载控制

(5)信息的转换(例如:编码和解码,如ProtocolCodecFilter)

(6)和其他更多的信息

 

(4) IoHandlerAdapter:这是一个抽像类,专门用来让我们重写以处理程序接收到的消息的,即业务逻辑的处理,并处理通信中的IoSession连结,断开,消息到达等事件。客户机和服务器端创建后,都有一个setHandler 方法,就是要传入我们重写了这个类的对象。 其中各个方法在通信中会根据情况自动调用,类似与Swing事件中的调用机制

 

IoSession: 代表一个客户端与服务器的连接

 

2 netty

(1) 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值