Java 网络IO编程总结

一、BIO编程

    1.一、传统的BIO编程

 

    网络编程的基本模型是C/S模型,即两个进程间的通讯。java

    服务端提供IP和监听端口,客户端经过链接操做想服务端监听的地址发起链接请求,经过三次握手链接,若是链接成功创建,双方就能够经过套接字进行通讯。编程

    传统的同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起链接操做。链接成功后,双方经过输入和输出流进行同步阻塞式通讯。 数组

    采用BIO通讯模型的服务端,一般由一个独立的Acceptor线程负责监听客户端的链接,它接收到客户端链接请求以后为每一个客户端建立一个新的线程进行链路处理没处理完成后,经过输出流返回应答给客户端,线程销毁。即典型的一请求一应答通宵模型。缓存

    该模型最大的问题就是缺少弹性伸缩能力,当客户端并发访问量增长后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,Java中的线程也是比较宝贵的系统资源,线程数量快速膨胀后,系统的性能将急剧降低,随着访问量的继续增大,系统最终就死-掉-了。网络

 

 

 

 1.二、伪异步I/O编程

    为了改进这种一链接一线程的模型,咱们可使用线程池来管理这些线程,实现1个或多个线程处理N个客户端的模型(可是底层仍是使用的同步阻塞I/O),一般被称为“伪异步I/O模型“。数据结构

 

若是使用CachedThreadPool线程池(不限制线程数量,若是不清楚请参考文首提供的文章),其实除了能自动帮咱们管理线程(复用),看起来也就像是1:1的客户端:线程数模型,而使用FixedThreadPool咱们就有效的控制了线程的最大数量,保证了系统有限的资源的控制,实现了N:M的伪异步I/O模型。并发

    可是,正由于限制了线程数量,若是发生大量并发请求,超过最大数量的线程就只能等待,直到线程池中的有空闲的线程能够被复用。而对Socket的输入流就行读取时,会一直阻塞,直到发生:异步

  •     有数据可读
  •     可用数据以及读取完毕
  •     发生空指针或I/O异常

    因此在读取数据较慢时(好比数据量大、网络传输慢等),大量并发的状况下,其余接入的消息,只能一直等待,这就是最大的弊端。高并发

 

 

二、NIO 编程

    JDK 1.4中的java.nio.*包中引入新的Java I/O库,其目的是提升速度。性能

     实际上,“旧”的I/O包已经使用NIO从新实现过,即便咱们不显式的使用NIO编程,也能从中受益。速度的提升在文件I/O和网络I/O中均可能会发生。

   2.一、简介

    NIO咱们通常认为是New I/O(也是官方的叫法),由于它是相对于老的I/O类库新增的(其实在JDK 1.4中就已经被引入了,但这个名词还会继续用好久,即便它们在如今看来已是“旧”的了,因此也提示咱们在命名时,须要好好考虑),作了很大的改变。但民间跟多人称之为Non-block I/O,即非阻塞I/O,由于这样叫,更能体现它的特色。而下文中的NIO,不是指整个新的I/O库,而是非阻塞I/O。

    NIO提供了与传统BIO模型中的Socket和ServerSocket相对应的SocketChannel和ServerSocketChannel两种不一样的套接字通道实现。

    新增的着两种通道都支持阻塞和非阻塞两种模式。

    阻塞模式使用就像传统中的支持同样,比较简单,可是性能和可靠性都很差;非阻塞模式正好与之相反。

    对于低负载、低并发的应用程序,可使用同步阻塞I/O来提高开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用NIO的非阻塞模式来开发。

    下面会先对基础知识进行介绍。

    2.二、缓冲区 Buffer

    Buffer是一个对象,包含一些要写入或者读出的数据。

    在NIO库中,全部数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的;在写入数据时,也是写入到缓冲区中。任什么时候候访问NIO中的数据,都是经过缓冲区进行操做。

    缓冲区其实是一个数组,并提供了对数据结构化访问以及维护读写位置等信息。

    具体的缓存区有这些:ByteBuffe、CharBuffer、 ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。他们实现了相同的接口:Buffer。

    2.三、通道 Channel

    咱们对数据的读取和写入要经过Channel,它就像水管同样,是一个通道。通道不一样于流的地方就是通道是双向的,能够用于读、写和同时读写操做。

    底层的操做系统的通道通常都是全双工的,因此全双工的Channel比流能更好的映射底层操做系统的API。

    Channel主要分两大类:

  •     SelectableChannel:用户网络读写
  •     FileChannel:用于文件操做

    后面代码会涉及的ServerSocketChannel和SocketChannel都是SelectableChannel的子类。

    2.四、多路复用器 Selector

    Selector是Java  NIO 编程的基础。

    Selector提供选择已经就绪的任务的能力:Selector会不断轮询注册在其上的Channel,若是某个Channel上面发生读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,而后经过SelectionKey能够获取就绪Channel的集合,进行后续的I/O操做。

    一个Selector能够同时轮询多个Channel,由于JDK使用了epoll()代替传统的select实现,因此没有最大链接句柄1024/2048的限制。因此,只须要一个线程负责Selector的轮询,就能够接入成千上万的客户端。

    2.五、NIO服务端

    代码比传统的Socket编程看起来要复杂很多。

    能够看到,建立NIO服务端的主要步骤以下:

  1.     打开ServerSocketChannel,监听客户端链接
  2.     绑定监听端口,设置链接为非阻塞模式
  3.     建立Reactor线程,建立多路复用器并启动线程
  4.     将ServerSocketChannel注册到Reactor线程中的Selector上,监听ACCEPT事件
  5.     Selector轮询准备就绪的key
  6.     Selector监听到新的客户端接入,处理新的接入请求,完成TCP三次握手,简历物理链路
  7.     设置客户端链路为非阻塞模式
  8.     将新接入的客户端链接注册到Reactor线程的Selector上,监听读操做,读取客户端发送的网络消息
  9.     异步读取客户端消息到缓冲区
  10.     对Buffer编解码,处理半包消息,将解码成功的消息封装成Task
  11.     将应答消息编码为Buffer,调用SocketChannel的write将消息异步发送给客户端

 

 

 

三、AIO编程

    NIO 2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。

    异步的套接字通道时真正的异步非阻塞I/O,对应于UNIX网络编程中的事件驱动I/O(AIO)。他不须要过多的Selector对注册的通道进行轮询便可实现异步读写,从而简化了NIO的编程模型。

 

 

BIO.png

相关文章
相关标签/搜索