`

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原理图文分析及代码实现

    本文主要介绍Java NIO原理的知识,这里整理了详细资料及简单示例代码和原理图,有需要的小伙伴可以参考下

    Java NIO原理分析及代码实例

    NULL 博文链接:https://dengqsintyt.iteye.com/blog/2083316

    Java NIO原理和使用

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

    java NIO详细教程

    java NIO详细教程,包括使用背景,实现原理,代码实现

    Java NIO工作原理的全面分析

    JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的、面向块的 I/O。本实用教程从高级概念到底层的编程细节,非常详细地介绍了 NIO 库。您将学到诸如缓冲区和通道这样的关键 I/O 元素的知识,并...

    JAVA上百实例源码以及开源项目源代码

     基于JAVA的UDP服务器模型源代码,内含UDP服务器端模型和UDP客户端模型两个小程序,向JAVA初学者演示UDP C/S结构的原理。 简单聊天软件CS模式 2个目标文件 一个简单的CS模式的聊天软件,用socket实现,比较简单。 ...

    Java NIO异步文件通道原理及用法解析

    主要介绍了Java NIO异步文件通道原理及用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    JAVA上百实例源码以及开源项目

     基于JAVA的UDP服务器模型源代码,内含UDP服务器端模型和UDP客户端模型两个小程序,向JAVA初学者演示UDP C/S结构的原理。 简单聊天软件CS模式 2个目标文件 一个简单的CS模式的聊天软件,用socket实现,比较简单。 ...

    java面试笔试题库java软件设计java笔试题大集合及答案文档资料合集300MB.zip

    java面试笔试题库java软件设计java笔试题大集合及答案文档资料合集300MB“ 100家大公司java笔试题汇总.doc 125条常见的java 面试笔试题大汇总.pdf 2011最新整理java经典代码.doc 25个经典的Spring面试问答.docx 8张...

    java面试笔试题库java学习笔记开发教程互联网公司面试资料大全合集.zip

    Java NIO通信框架在电信领域的实践.docx java.png javaconcurrencyinpractice.pdf JavaEE学习笔记.pdf java_Java_学习笔记.pdf Java_Performance.pdf java代码效率优化.docx Java内存模型的历史变迁.docx Java在游戏...

    JAVA高并发高性能高可用高扩展架构视频教程

    JAVANIO原理详解 高并发数据库(Mysql数据库性能优化) 软件质量管控 企业常用框架springMVC基于注解+xml配置方式实现链接 WEB服务器优化之Tomcat7性能调优 JVM概述 Java开发技术之(项目工程的日志管理) 数据库连接池...

    精通并发与 netty 视频教程(2018)视频教程

    28_gRPC与Gradle流畅整合及问题解决的完整过程与思考 29_Gradle插件问题解决方案与Nodejs环境搭建 30_通过gRPC实现Java与Nodejs异构平台的RPC调用 31_gRPC在Nodejs领域中的静态代码生成及与Java之间的RPC通信 ...

    java核心面试笔记+核心知识源码降解

    通过带着读者从java基础到java编写spring全家桶框架面试以及技术讲解,例如:集合,数组,JVM,引用类型,IO/NIO,多线程,类加载机制,spring设计原理等内容实现。 适合人群:具备一定编程基础,工作3-5年的研发...

    精通并发与netty视频教程(2018)视频教程

    48_Netty与NIO系统总结及NIO与Netty之间的关联关系分析 49_零拷贝深入剖析及用户空间与内核空间切换方式 50_零拷贝实例深度剖析 51_NIO零拷贝彻底分析与Gather操作在零拷贝中的作用详解 52_NioEventLoopGroup源码...

    JAVA版基于netty的物联网高并发智能网关.rar

    java 1.4起,jdk支持了NIO(NEW IO),因NIO(os nonblocking)的非阻塞式工作方式,让应用服务器可以极大的优化线程模型,相比传统的阻塞式IO线程和链路一对一的模式,NIO只需少量的线程即可处理所有的链路,这对广大的...

    看透springMvc源代码分析与实践

    4.2 NioSocket的用法28 第5章 自己动手实现HTTP协议33 第6章 详解Servlet37 6.1 Servlet接口37 6.2 GenericServlet40 6.3 HttpServlet41 第7章 Tomcat分析44 7.1 Tomcat的顶层结构及启动过程44 7.1.1 ...

Global site tag (gtag.js) - Google Analytics