Bio、Nio、Aio的用法系列之NIO服务端(二)

NIO的由来

技术圈有很多人说NIO是new IO,是因为他是新增的接口,这也是官方说法,但是,我们知道,以前都是阻塞IO,详细见上文BIO详解,而NIO是非阻塞的,所以说,NIO更确切的说法  
是non-block IO,当然关于说法,大家可以根据自己的理解,不过多做解释。  
首先在讲解NIO之前我们先了解几个概念

1、缓冲区Buffer

Buffer是一个对象,在nio读取数据时,他是直接读到buffer的,写数据时,也是先写到缓冲区的,所以任何时候访问,都是通过缓冲区完成的,  
Buffer的实质是一个数据,一般来说是一个字节数据,byteBuffer,也可以是其他的数组,但是缓冲区不是一个简简单单的数组,缓冲区对数据的结构化访问以及维护读写位置信息

2、通道Channel

管道是一个管道,是全双工模式,对于里面的数据,可读可写,比流更好的映射到底层操作系统的API,支持同时读写操作,Channel一共可以分为两大类,一个是SelectableChannel,一个是FileChannel,不过本例中并没有用到FileChannel,因为SocketChannel跟ServerSocketChannel都是SelectableChannel的子类

NIO服务端的代码实现

1、服务端启动器


publicclassNioServer{
   publicstaticvoidmain(String [] args){
       //启动一个线程
       new Thread(new NioServerHandle()).start();
   }
}

2、NioServerHandle构造器中初始化


   //初始化注册
   publicNioServerHandle(){
       try {
           //获取ServerSocketChannel对象
           serverSocketChannel = ServerSocketChannel.open();
           //绑定ip
           serverSocketChannel.socket().bind(new InetSocketAddress(“127.0.0.1”,8989));
           //设置为非阻塞
           serverSocketChannel.configureBlocking(false);
           //获取Selector
           selector = Selector.open();
           //将管道注册到多路复用器selector上
           serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
       } catch (IOException e) {
           e.printStackTrace();
           System.exit(1);
       }

   }

3、run方法中进行轮询


@Override
   publicvoidrun(){
       //轮询key
       while (!stop){
           try {
               //设置超时时间
               selector.select(1000);
               //获取所有key
               Set<selectionkey> selectionKeys = selector.selectedKeys();
               //遍历
               Iterator</selectionkey><selectionkey> it = selectionKeys.iterator();
               SelectionKey selectionKey = null;
               while (it.hasNext()){
                   selectionKey = it.next();
                   //获取到就绪数组进行操作,并移除
                   it.remove();
                   try {
                       handle(selectionKey);
                   }catch (Exception e){
                       selectionKey.cancel();
                       selectionKey.channel().close();
                   }

               }
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }

4、处理客户端发送过来的请求


   /**
    * 处理
    * @param key
    * @throws Exception
    */
   publicvoidhandle(SelectionKey key)throws Exception{
       if (key.isValid()){
           if (key.isAcceptable()){
               //获取ServerSocketChannel
               ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
               //接受请求
               SocketChannel sc = ssc.accept();
               //设置为非阻塞
               sc.configureBlocking(false);
               //注册到多路复用器
               sc.register(selector,SelectionKey.OP_READ);
           }
           //read data
           if (key.isReadable()){
               //得到SocketChannel
               SocketChannel sc = (SocketChannel) key.channel();
               //设置字节缓冲区
               ByteBuffer readBuffer = ByteBuffer.allocate(1024);
               //将通道的数据读取码流
               int readByte = sc.read(readBuffer);
               //对于大于0的情况进行编解码
               if (readByte > 0){
                   //将当前的缓冲区的limit设置为0,让后面进行读取
                   readBuffer.flip();
                   //根据缓冲区的可读大小设置字节数组
                   byte [] bytes = newbyte[readBuffer.remaining()];
                   //get将读取的数据放入字节数组
                   readBuffer.get(bytes);
                   //将字节数组按照UTF-8的格式输出到body
                   String body = new String(bytes,“UTF-8”);
                   System.out.print(“The Time server recevive order:”+body);
                   String currentTime = “query”.equals(body)?new java.util.Date(System.currentTimeMillis()).toString():“No”;
                   //进行输出操作
                   doWrite(sc,currentTime);
               }elseif (readByte < 0 ){
                   key.cancel();
                   sc.close();
               }else {
               }
           }
       }

   }

</code>

5、将数据返回给客户端


   /**
    * 将数据返回给客户端
    * @param sc
    * @param currentTime
    * @throws Exception
    */
   publicvoiddoWrite(SocketChannel sc,String currentTime)throws Exception{
       if (currentTime != null && currentTime.trim().length() > 0){
           //转换为字节数组,放到缓冲区
           byte [] bytes = currentTime.getBytes();
           ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
           byteBuffer.put(bytes);
           byteBuffer.flip();
           sc.write(byteBuffer);
       }
   }

    本站所有文章均来自互联网,如有侵权,请联系站长删除。极客文库 » Bio、Nio、Aio的用法系列之NIO服务端(二)
    分享到:
    赞(0)

    评论抢沙发

    评论前必须登录!