zoukankan      html  css  js  c++  java
  • Java基础知识强化之IO流笔记72:NIO之 NIO核心组件(NIO使用代码示例)

    1.Java NIO 由以下几个核心部分组成

    • Channels(通道)
    • Buffers(缓冲区)
    • Selectors(选择器)

    虽然Java NIO 中除此之外还有很多类和组件,ChannelBufferSelector 构成了核心的API。其它组件,如PipeFileLock,只不过是与三个核心组件共同使用的工具类。

    (1)Channel Buffer

    基本上,所有的 IO操作在NIO 中都从一个Channel 开始。Channel 有点像 流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:

    ChannelBuffer有好几种类型。

    下面是JAVA NIO中的一些主要Channel的实现

    • FileChannel
    • DatagramChannel
    • SocketChannel
    • ServerSocketChannel

    正如你所看到的,这些通道涵盖了UDPTCP 网络IO,以及文件IO

    与这些类一起的有一些有趣的接口,但为简单起见,我尽量在概述中不提到它们。本教程其它章节与它们相关的地方我会进行解释。

    以下是Java NIO主要的Buffer实现

    • ByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer

    这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, doublechar

    Java NIO 还有个 MappedByteBuffer,用于表示内存映射文件, 我也不打算在概述中说明。

    (2)Selector

      Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。

    这是在一个单线程中使用一个Selector处理3个Channel的图示:

    要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

    2. NIO使用代码示例:

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

    服务端:

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

    客户端:

      1 package com.himi.demo2;
      2 
      3 import java.io.IOException;
      4 import java.net.InetSocketAddress;
      5 import java.nio.ByteBuffer;
      6 import java.nio.channels.SelectionKey;
      7 import java.nio.channels.Selector;
      8 import java.nio.channels.SocketChannel;
      9 import java.util.Iterator;
     10 
     11 /**
     12  * NIO客户端
     13  * 
     14  * @author hebao
     15  */
     16 public class NIOClient {
     17     // 通道管理器
     18     private Selector selector;
     19 
     20     /**
     21      * 获得一个Socket通道,并对该通道做一些初始化的工作
     22      * 
     23      * @param ip
     24      *            连接的服务器的ip
     25      * @param port
     26      *            连接的服务器的端口号
     27      * @throws IOException
     28      */
     29     public void initClient(String ip, int port) throws IOException {
     30         // 获得一个Socket通道
     31         SocketChannel channel = SocketChannel.open();
     32         // 设置通道为非阻塞
     33         channel.configureBlocking(false);
     34         // 获得一个通道管理器
     35         this.selector = Selector.open();
     36 
     37         // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
     38         // 用channel.finishConnect();才能完成连接
     39         channel.connect(new InetSocketAddress(ip, port));
     40         // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
     41         channel.register(selector, SelectionKey.OP_CONNECT);
     42     }
     43 
     44     /**
     45      * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
     46      * 
     47      * @throws IOException
     48      */
     49     @SuppressWarnings("unchecked")
     50     public void listen() throws IOException {
     51         // 轮询访问selector
     52         while (true) {
     53             selector.select();
     54             // 获得selector中选中的项的迭代器
     55             Iterator ite = this.selector.selectedKeys().iterator();
     56             while (ite.hasNext()) {
     57                 SelectionKey key = (SelectionKey) ite.next();
     58                 // 删除已选的key,以防重复处理
     59                 ite.remove();
     60                 // 连接事件发生
     61                 if (key.isConnectable()) {
     62                     SocketChannel channel = (SocketChannel) key.channel();
     63                     // 如果正在连接,则完成连接
     64                     if (channel.isConnectionPending()) {
     65                         channel.finishConnect();
     66 
     67                     }
     68                     // 设置成非阻塞
     69                     channel.configureBlocking(false);
     70 
     71                     // 在这里可以给服务端发送信息哦
     72                     channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
     73                     // 在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
     74                     channel.register(this.selector, SelectionKey.OP_READ);
     75 
     76                     // 获得了可读的事件
     77                 } else if (key.isReadable()) {
     78                     read(key);
     79                 }
     80 
     81             }
     82 
     83         }
     84     }
     85 
     86     /**
     87      * 处理读取服务端发来的信息 的事件
     88      * 
     89      * @param key
     90      * @throws IOException
     91      */
     92     public void read(SelectionKey key) throws IOException {
     93         // 和服务端的read方法一样
     94     }
     95 
     96     /**
     97      * 启动客户端测试
     98      * 
     99      * @throws IOException
    100      */
    101     public static void main(String[] args) throws IOException {
    102         NIOClient client = new NIOClient();
    103         client.initClient("localhost", 8000);
    104         client.listen();
    105     }
    106 
    107 }
  • 相关阅读:
    struts2 action 之间的跳转
    json格式字符串用jquery.parseJSON()出现的问题 Uncaught SyntaxError: Unexpected token ' Uncaught SyntaxError: Unexpected number (index)
    转:Java生成带有二维码图片的word文档
    PowerDesigner 15 进行 数据库反转到 数据库模型
    模型方案参数更改 对比栏入选模型方案 图表效果对比 已不在项目中使用
    久违的博客园
    [转]不要if else的编程
    希望
    eclipse修改源码导出jar包
    compareTo 比较器
  • 原文地址:https://www.cnblogs.com/hebao0514/p/5332132.html
Copyright © 2011-2022 走看看