zoukankan      html  css  js  c++  java
  • JAVA nio

    拿一个例子说我的理解

      

    public class NIOServer {
        private static final int TIMEOUT  = 300;
        private static final int PORT     = 12112;
    
        public static void main(String[] args) {        
            try {        
                //创建一个选择器  的作用是监听管理事件
                Selector selector = Selector.open();
                //创建一个ServerSocketChannel  对象 server
                ServerSocketChannel listenChannel = ServerSocketChannel.open();
                //配置为非阻塞方式
                listenChannel.configureBlocking(false);
                // 启动服务器并把服务器绑定到监听端口 12112
                listenChannel.socket().bind(new InetSocketAddress(PORT));
                //注册到选择器selector中   注意注册的事件类型为 OP_ACCEPT 既告诉
              // 注册器 Seclector 你需要监听 指定端口的On_ACCEPT事件 (注意现在注册器
                 只会监听这一种事件 而且在指定端口) 
                listenChannel.register(selector, SelectionKey.OP_ACCEPT);
                
                while(true) {
                    //监听事件
                    if(selector.select(TIMEOUT)==0) {
                        System.out.print(".");
                        continue;
                    }
                    //事件来源列表
                    Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                    while( iter.hasNext() ) {
                        //获取一个事件  开始处理这个事件
                        SelectionKey key = iter.next();
                        iter.remove(); //删除当前事件
                        
                        //对每一个事件来源key分别进行处理
                        if( key.isAcceptable() ) {
                             //前面说注册器Seclector要监听ON_ACCEPT事件 现在事件来了  需要服务端做相应处理了
                           //这里的处理方式是 启动一个 SocketChannel对象  来处理客户端的这个连接  这里的处理
                           也很有意思  是给当前的这个事件key绑定一个SocketChannel对象 ,告诉Seltor 如有ON_READ事件
                          进来 就用 此 SocketChannl来处理  
                            //启动服务器的一个channel对象对某个事件进行处理,这个channel对象代表客户端的连接
                            // 作用类似于socket
                            SocketChannel channel=listenChannel.accept();
                            channel.configureBlocking(false);
                            //把此channel 客户端对象作为一个事件注册到 选择器 selector中
                            SelectionKey connkey=channel.register(selector, SelectionKey.OP_READ );
                            NIOServerConnection conn=new NIOServerConnection(connkey);
                            connkey.attach(conn);
                        }
                        
                        if( key.isReadable() ) {
                            NIOServerConnection conn=(NIOServerConnection) key.attachment();
                            conn.handleRead();
                        }
                        
                        if( key.isValid() && key.isWritable() ) {
                            NIOServerConnection conn=(NIOServerConnection) key.attachment();
                            conn.handleWrite();
                        }
                    }                
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

       

    public class NIOServerConnection {
        private static final int BUFFSIZE = 1024;
        
        SelectionKey key;
        SocketChannel channel;
        ByteBuffer buffer;
        
        public NIOServerConnection(SelectionKey key) {
            this.key=key;
            this.channel=(SocketChannel) key.channel();
            buffer=ByteBuffer.allocate(BUFFSIZE);
        }
        
        public void handleRead() throws IOException {
            long bytesRead=channel.read(buffer);
            
            if(bytesRead==-1) {
                channel.close();
            } else {
                key.interestOps( SelectionKey.OP_READ | SelectionKey.OP_WRITE );
            }
        }
    
        public void handleWrite() throws IOException {
            buffer.flip();
            channel.write(buffer);
            
            if(!buffer.hasRemaining()) {
                key.interestOps( SelectionKey.OP_READ );
            } 
            
            buffer.compact();
        }
    }

    此外有两篇不错的文章

     http://www.cnblogs.com/rollenholt/archive/2011/09/29/2195730.html

    http://www.360doc.com/content/12/0409/15/87000_202205480.shtml

    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代码 收藏代码

    1. package cn.nio; 
    2. import java.io.IOException; 
    3. import java.net.InetSocketAddress; 
    4. import java.nio.ByteBuffer; 
    5. import java.nio.channels.SelectionKey; 
    6. import java.nio.channels.Selector; 
    7. import java.nio.channels.ServerSocketChannel; 
    8. import java.nio.channels.SocketChannel; 
    9. import java.util.Iterator; 
    10. /**
    11. * NIO服务端
    12. * @author 小路
    13. */
    14. public class NIOServer { 
    15. //通道管理器
    16. private Selector selector; 
    17. /**
    18.      * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
    19.      * @param port  绑定的端口号
    20.      * @throws IOException
    21.      */
    22. public void initServer(int port) throws IOException { 
    23. // 获得一个ServerSocket通道
    24.         ServerSocketChannel serverChannel = ServerSocketChannel.open(); 
    25. // 设置通道为非阻塞
    26.         serverChannel.configureBlocking(false); 
    27. // 将该通道对应的ServerSocket绑定到port端口
    28.         serverChannel.socket().bind(new InetSocketAddress(port)); 
    29. // 获得一个通道管理器
    30. this.selector = Selector.open(); 
    31. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
    32. //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
    33.         serverChannel.register(selector, SelectionKey.OP_ACCEPT); 
    34.     } 
    35. /**
    36.      * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
    37.      * @throws IOException
    38.      */
    39. @SuppressWarnings("unchecked") 
    40. public void listen() throws IOException { 
    41.         System.out.println("服务端启动成功!"); 
    42. // 轮询访问selector
    43. while (true) { 
    44. //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
    45.             selector.select(); 
    46. // 获得selector中选中的项的迭代器,选中的项为注册的事件
    47.             Iterator ite = this.selector.selectedKeys().iterator(); 
    48. while (ite.hasNext()) { 
    49.                 SelectionKey key = (SelectionKey) ite.next(); 
    50. // 删除已选的key,以防重复处理
    51.                 ite.remove(); 
    52. // 客户端请求连接事件
    53. if (key.isAcceptable()) { 
    54.                     ServerSocketChannel server = (ServerSocketChannel) key 
    55.                             .channel(); 
    56. // 获得和客户端连接的通道
    57.                     SocketChannel channel = server.accept(); 
    58. // 设置成非阻塞
    59.                     channel.configureBlocking(false); 
    60. //在这里可以给客户端发送信息哦
    61.                     channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes())); 
    62. //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
    63.                     channel.register(this.selector, SelectionKey.OP_READ); 
    64. // 获得了可读的事件
    65.                 } else if (key.isReadable()) { 
    66.                         read(key); 
    67.                 } 
    68.             } 
    69.         } 
    70.     } 
    71. /**
    72.      * 处理读取客户端发来的信息 的事件
    73.      * @param key
    74.      * @throws IOException
    75.      */
    76. public void read(SelectionKey key) throws IOException{ 
    77. // 服务器可读取消息:得到事件发生的Socket通道
    78.         SocketChannel channel = (SocketChannel) key.channel(); 
    79. // 创建读取的缓冲区
    80.         ByteBuffer buffer = ByteBuffer.allocate(10); 
    81.         channel.read(buffer); 
    82. byte[] data = buffer.array(); 
    83.         String msg = new String(data).trim(); 
    84.         System.out.println("服务端收到信息:"+msg); 
    85.         ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); 
    86.         channel.write(outBuffer);// 将消息回送给客户端
    87.     } 
    88. /**
    89.      * 启动服务端测试
    90.      * @throws IOException
    91.      */
    92. public static void main(String[] args) throws IOException { 
    93.         NIOServer server = new NIOServer(); 
    94.         server.initServer(8000); 
    95.         server.listen(); 
    96.     } 

    客户端:

    Java代码 收藏代码

    1. package cn.nio; 
    2. import java.io.IOException; 
    3. import java.net.InetSocketAddress; 
    4. import java.nio.ByteBuffer; 
    5. import java.nio.channels.SelectionKey; 
    6. import java.nio.channels.Selector; 
    7. import java.nio.channels.SocketChannel; 
    8. import java.util.Iterator; 
    9. /**
    10. * NIO客户端
    11. * @author 小路
    12. */
    13. public class NIOClient { 
    14. //通道管理器
    15. private Selector selector; 
    16. /**
    17.      * 获得一个Socket通道,并对该通道做一些初始化的工作
    18.      * @param ip 连接的服务器的ip
    19.      * @param port  连接的服务器的端口号        
    20.      * @throws IOException
    21.      */
    22. public void initClient(String ip,int port) throws IOException { 
    23. // 获得一个Socket通道
    24.         SocketChannel channel = SocketChannel.open(); 
    25. // 设置通道为非阻塞
    26.         channel.configureBlocking(false); 
    27. // 获得一个通道管理器
    28. this.selector = Selector.open(); 
    29. // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
    30. //用channel.finishConnect();才能完成连接
    31.         channel.connect(new InetSocketAddress(ip,port)); 
    32. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
    33.         channel.register(selector, SelectionKey.OP_CONNECT); 
    34.     } 
    35. /**
    36.      * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
    37.      * @throws IOException
    38.      */
    39. @SuppressWarnings("unchecked") 
    40. public void listen() throws IOException { 
    41. // 轮询访问selector
    42. while (true) { 
    43.             selector.select(); 
    44. // 获得selector中选中的项的迭代器
    45.             Iterator ite = this.selector.selectedKeys().iterator(); 
    46. while (ite.hasNext()) { 
    47.                 SelectionKey key = (SelectionKey) ite.next(); 
    48. // 删除已选的key,以防重复处理
    49.                 ite.remove(); 
    50. // 连接事件发生
    51. if (key.isConnectable()) { 
    52.                     SocketChannel channel = (SocketChannel) key 
    53.                             .channel(); 
    54. // 如果正在连接,则完成连接
    55. if(channel.isConnectionPending()){ 
    56.                         channel.finishConnect(); 
    57.                     } 
    58. // 设置成非阻塞
    59.                     channel.configureBlocking(false); 
    60. //在这里可以给服务端发送信息哦
    61.                     channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes())); 
    62. //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
    63.                     channel.register(this.selector, SelectionKey.OP_READ); 
    64. // 获得了可读的事件
    65.                 } else if (key.isReadable()) { 
    66.                         read(key); 
    67.                 } 
    68.             } 
    69.         } 
    70.     } 
    71. /**
    72.      * 处理读取服务端发来的信息 的事件
    73.      * @param key
    74.      * @throws IOException
    75.      */
    76. public void read(SelectionKey key) throws IOException{ 
    77. //和服务端的read方法一样
    78.     } 
    79. /**
    80.      * 启动客户端测试
    81.      * @throws IOException
    82.      */
    83. public static void main(String[] args) throws IOException { 
    84.         NIOClient client = new NIOClient(); 
    85.         client.initClient("localhost",8000); 
    86.         client.listen(); 
    87.     } 

    小结

  • 相关阅读:
    【JAVA笔记——道】JAVA对象销毁
    【JAVA笔记——道】并发编程CAS算法
    httpClientUtil的get请求
    python基础 day11 下 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业
    python基础 day11 上 数据库介绍 mysql 数据库安装使用 mysql管理 mysql 数据类型 常用mysql命令 事务 索引 python 操作mysql ORM sqlachemy学习
    Python基础 Day10 Gevent协程 SelectPollEpoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 RedisMemcached缓存 Paramiko SSH Twsited网络框架
    python基础 day9 进程、与线程区别 python GIL全局解释器锁 线程 进程
    python基础 day8 Socket语法及相关 SocketServer实现多并发
    python基础 day7 面向对象高级语法部分 异常处理 异常处理 Socket开发基础
    python基础 day6 面向对象的特性:封装、继承、多态 类、方法、
  • 原文地址:https://www.cnblogs.com/echomyecho/p/3266045.html
Copyright © 2011-2022 走看看