`

Java NIO原理文分析及代码实现

NIO 
阅读更多
Java NIO原理图文分析及代码实现

前言:

最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。可以参考:http://baike.baidu.com/view/32726.htm )机制时,发现hadoop的RPC机制的实现主要用到了两个技术:动态代理(动态代理可以参考博客:http://weixiaolu.iteye.com/blog/1477774 )和java NIO。为了能够正确地分析hadoop的RPC源码,我觉得很有必要先研究一下java NIO的原理和具体实现。

这篇博客我主要从两个方向来分析java NIO

目录:
一.java NIO 和阻塞I/O的区别
     1. 阻塞I/O通信模型
     2. java NIO原理及通信模型
二.java NIO服务端和客户端代码实现

具体分析:

一.java NIO 和阻塞I/O的区别

1. 阻塞I/O通信模型

    假如现在你对阻塞I/O已有了一定了解,我们知道阻塞I/O在调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超时)才会返回;同样,在调用ServerSocket.accept()方法时,也会一直阻塞到有客户端连接才会返回,每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。阻塞I/O的通信模型示意图如下:



    如果你细细分析,一定会发现阻塞I/O存在一些缺点。根据阻塞I/O通信模型,我总结了它的两点缺点:
1. 当客户端多时,会创建大量的处理线程。且每个线程都要占用栈空间和一些CPU时间

2. 阻塞可能带来频繁的上下文切换,且大部分上下文切换可能是无意义的。

在这种情况下非阻塞式I/O就有了它的应用前景。

2. java NIO原理及通信模型

    Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。下面是java NIO的工作原理:

1. 由一个专门的线程来处理所有的 IO 事件,并负责分发。
2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
3. 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。

阅读过一些资料之后,下面贴出我理解的java NIO的工作原理图:



(注:每个线程的处理流程大概都是读取数据、解码、计算处理、编码、发送响应。)

    Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢?呵呵,我们一起来探究它的奥秘吧。java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:

事件名 对应值
服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)
客户端连接服务端事件 SelectionKey.OP_CONNECT(8)
读事件 SelectionKey.OP_READ(1)
写事件 SelectionKey.OP_WRITE(4)






   服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。下面是我理解的java NIO的通信模型示意图:



二.java NIO服务端和客户端代码实现

为了更好地理解java NIO,下面贴出服务端和客户端的简单代码实现。

服务端:

Java代码  收藏代码
package cn.nio; 
 
import java.io.IOException; 
import java.net.InetSocketAddress; 
import java.nio.ByteBuffer; 
import java.nio.channels.SelectionKey; 
import java.nio.channels.Selector; 
import java.nio.channels.ServerSocketChannel; 
import java.nio.channels.SocketChannel; 
import java.util.Iterator; 
 
/**
* NIO服务端
* @author 小路
*/ 
public class NIOServer { 
    //通道管理器 
    private Selector selector; 
 
    /**
     * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
     * @param port  绑定的端口号
     * @throws IOException
     */ 
    public void initServer(int port) throws IOException { 
        // 获得一个ServerSocket通道 
        ServerSocketChannel serverChannel = ServerSocketChannel.open(); 
        // 设置通道为非阻塞 
        serverChannel.configureBlocking(false); 
        // 将该通道对应的ServerSocket绑定到port端口 
        serverChannel.socket().bind(new InetSocketAddress(port)); 
        // 获得一个通道管理器 
        this.selector = Selector.open(); 
        //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, 
        //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 
        serverChannel.register(selector, SelectionKey.OP_ACCEPT); 
    } 
 
    /**
     * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
     * @throws IOException
     */ 
    @SuppressWarnings("unchecked") 
    public void listen() throws IOException { 
        System.out.println("服务端启动成功!"); 
        // 轮询访问selector 
        while (true) { 
            //当注册的事件到达时,方法返回;否则,该方法会一直阻塞 
            selector.select(); 
            // 获得selector中选中的项的迭代器,选中的项为注册的事件 
            Iterator ite = this.selector.selectedKeys().iterator(); 
            while (ite.hasNext()) { 
                SelectionKey key = (SelectionKey) ite.next(); 
                // 删除已选的key,以防重复处理 
                ite.remove(); 
                // 客户端请求连接事件 
                if (key.isAcceptable()) { 
                    ServerSocketChannel server = (ServerSocketChannel) key 
                            .channel(); 
                    // 获得和客户端连接的通道 
                    SocketChannel channel = server.accept(); 
                    // 设置成非阻塞 
                    channel.configureBlocking(false); 
 
                    //在这里可以给客户端发送信息哦 
                    channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes())); 
                    //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 
                    channel.register(this.selector, SelectionKey.OP_READ); 
                     
                    // 获得了可读的事件 
                } else if (key.isReadable()) { 
                        read(key); 
                } 
 
            } 
 
        } 
    } 
    /**
     * 处理读取客户端发来的信息 的事件
     * @param key
     * @throws IOException 
     */ 
    public void read(SelectionKey key) throws IOException{ 
        // 服务器可读取消息:得到事件发生的Socket通道 
        SocketChannel channel = (SocketChannel) key.channel(); 
        // 创建读取的缓冲区 
        ByteBuffer buffer = ByteBuffer.allocate(10); 
        channel.read(buffer); 
        byte[] data = buffer.array(); 
        String msg = new String(data).trim(); 
        System.out.println("服务端收到信息:"+msg); 
        ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); 
        channel.write(outBuffer);// 将消息回送给客户端 
    } 
     
    /**
     * 启动服务端测试
     * @throws IOException 
     */ 
    public static void main(String[] args) throws IOException { 
        NIOServer server = new NIOServer(); 
        server.initServer(8000); 
        server.listen(); 
    } 
 



客户端:


Java代码  收藏代码
package cn.nio; 
 
import java.io.IOException; 
import java.net.InetSocketAddress; 
import java.nio.ByteBuffer; 
import java.nio.channels.SelectionKey; 
import java.nio.channels.Selector; 
import java.nio.channels.SocketChannel; 
import java.util.Iterator; 
 
/**
* NIO客户端
* @author 小路
*/ 
public class NIOClient { 
    //通道管理器 
    private Selector selector; 
 
    /**
     * 获得一个Socket通道,并对该通道做一些初始化的工作
     * @param ip 连接的服务器的ip
     * @param port  连接的服务器的端口号         
     * @throws IOException
     */ 
    public void initClient(String ip,int port) throws IOException { 
        // 获得一个Socket通道 
        SocketChannel channel = SocketChannel.open(); 
        // 设置通道为非阻塞 
        channel.configureBlocking(false); 
        // 获得一个通道管理器 
        this.selector = Selector.open(); 
         
        // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调 
        //用channel.finishConnect();才能完成连接 
        channel.connect(new InetSocketAddress(ip,port)); 
        //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。 
        channel.register(selector, SelectionKey.OP_CONNECT); 
    } 
 
    /**
     * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
     * @throws IOException
     */ 
    @SuppressWarnings("unchecked") 
    public void listen() throws IOException { 
        // 轮询访问selector 
        while (true) { 
            selector.select(); 
            // 获得selector中选中的项的迭代器 
            Iterator ite = this.selector.selectedKeys().iterator(); 
            while (ite.hasNext()) { 
                SelectionKey key = (SelectionKey) ite.next(); 
                // 删除已选的key,以防重复处理 
                ite.remove(); 
                // 连接事件发生 
                if (key.isConnectable()) { 
                    SocketChannel channel = (SocketChannel) key 
                            .channel(); 
                    // 如果正在连接,则完成连接 
                    if(channel.isConnectionPending()){ 
                        channel.finishConnect(); 
                         
                    } 
                    // 设置成非阻塞 
                    channel.configureBlocking(false); 
 
                    //在这里可以给服务端发送信息哦 
                    channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes())); 
                    //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。 
                    channel.register(this.selector, SelectionKey.OP_READ); 
                     
                    // 获得了可读的事件 
                } else if (key.isReadable()) { 
                        read(key); 
                } 
 
            } 
 
        } 
    } 
    /**
     * 处理读取服务端发来的信息 的事件
     * @param key
     * @throws IOException 
     */ 
    public void read(SelectionKey key) throws IOException{ 
        //和服务端的read方法一样 
    } 
     
     
    /**
     * 启动客户端测试
     * @throws IOException 
     */ 
    public static void main(String[] args) throws IOException { 
        NIOClient client = new NIOClient(); 
        client.initClient("localhost",8000); 
        client.listen(); 
    } 
 
分享到:
评论

相关推荐

    Java NIO原理 图文分析及代码实现

    ### Java NIO原理 图文分析及代码实现 #### 前言 在深入探讨Java NIO之前,我们先简要回顾一下NIO的概念及其引入的原因。随着互联网的发展,越来越多的应用程序需要处理高并发的网络连接请求。传统的阻塞I/O模型在...

    Java NIO原理分析及代码实例

    Java NIO(New IO)是Java 1.4版本引入的一个新API,全称为Non-blocking Input/Output,它提供了一种不同于传统IO的编程模型,传统IO...在实际编码时,参考博文链接中的代码实例,可以帮助你更好地理解和实践Java NIO。

    Java NIO原理图文分析及代码实现

    - **Java NIO原理及通信模型**:NIO采用了非阻塞的方式,通过一个单独的线程来处理所有IO事件,使用事件驱动机制,当事件发生时才进行处理。它依赖于选择器(Selector)来监控多个通道(Channel)上的事件,如连接...

    基于java的BIO、NIO、AIO通讯模型代码实现

    压缩包中的文件可能包含了实现这三种通信模型的Java代码示例,通过学习和理解这些代码,你可以更好地掌握Java I/O的运用,并根据需求选择合适的模型。建议从简单的BIO开始,逐步过渡到更复杂的NIO和AIO,理解其工作...

    java nio示例代码

    4. **文件系统操作**:Java NIO提供了FileChannel用于文件的读写,可以实现大文件的高效传输。例如,使用transferTo和transferFrom方法可以直接将文件数据从一个通道传输到另一个通道,无需额外的缓冲区。 5. **...

    java NIO实例

    下面将详细介绍Java NIO的主要组件和工作原理,并结合这两个文件名推测它们可能包含的内容。 1. **Selector(选择器)**:选择器是NIO的核心组件,它能够监控多个通道(Channel)的状态变化,当某个通道准备进行...

    java NIO原理和使用

    ### Java NIO 原理与使用详解 #### 一、Java NIO 概述 在深入了解 Java NIO 的工作原理及其使用之前,我们首先来了解一下什么是 Java NIO(New I/O)。Java NIO 是 Java SE 1.4 版本引入的一个全新的 I/O API,...

    Java NIO原理解析

    Java NIO,即Non-Blocking I/O,是Java在JDK 1.4引入的一套新的I/O API,旨在提供一种更加高效的方式来处理I/O操作,尤其是对于网络编程和高并发场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器...

    Java NIO学习资料+代码.zip

    Java NIO提供了FileChannel,可以实现高效的大文件读写。例如,可以使用FileChannel的transferTo和transferFrom方法进行文件间的零拷贝操作,减少系统调用,提高效率。 **6. 多路复用** Java NIO的选择器使得单...

    Java NIO原理和使用

    Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。 Java NIO...

    Java.NIO资源下载资源下载

    - **Selector 基础**:介绍了 Selector 的基本概念及工作原理。 - **使用 SelectionKey**:解释了如何使用 SelectionKey 来管理 Channel 的选择状态。 - **使用 Selectors**:详细讨论了如何使用 Selector 来监听多...

    java nio聊天室源码

    Java NIO(New IO)是Java 1.4版本引入的一种新的I/O API,它提供了非阻塞I/O操作的能力,极大地提升了Java在...通过分析和学习这个源码,开发者可以深入理解Java NIO的工作原理,并将其应用于实际的网络编程项目中。

    Java中的NIO:深入理解与IO的区别、原理及代码实现

    本文将深入探讨Java中的NIO,包括它与IO的区别、工作原理以及如何通过代码实现NIO操作。 NIO是Java中一个强大的IO处理机制,它通过通道、缓冲区和选择器提供了一种高效、灵活的数据处理方式。虽然它的编程模型比传统...

    java nio 聊天室源码

    Java NIO(New IO)是Java 1.4版本引入的一个新模块,全称为New Input/Output,是对传统IO API的扩展...通过分析和理解这个项目,开发者不仅可以深入理解NIO的工作原理,还能掌握如何将NIO应用到实际的并发网络编程中。

    nio.zip_NIO_NewIO_NIO.c_java NIO chm_java nio

    `nio.c`可能是指C语言实现的NIO相关代码,这在Java NIO中不太常见,但可能是为了演示底层I/O操作的原理或与Java NIO进行性能比较。在Java中,NIO API通常由Java标准库提供,无需使用C代码。 `java_nio_chm`指的是...

    Java nio源码

    Java NIO,全称为New Input/Output,是Java在1.4版本引入的一个新特性,旨在提供一种更高效、更具选择性的I/O模型。...通过分析NIO源码,我们可以深入了解其内部工作原理,进一步优化和调试相关代码。

    JavaNIO的原理.pdf

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java从JDK 1.4版本开始引入的一种I/O模型,它为Java提供了更高效的数据传输能力,尤其适用于高并发的I/O操作场景。与传统的 Blocking I/O 相比,...

Global site tag (gtag.js) - Google Analytics