JAVA NIO 小结

来源:互联网 发布:windows字体打包下载 编辑:程序博客网 时间:2024/05/18 02:04

根据网上的资料做些整理

Java NIO API详解

http://www.blogjava.net/19851985lili/articles/93524.html

这篇文章对nio的api讲解比较全,可以帮助在宏观上把握nio。

BIO 方式使得整个处理过程和连接是绑定的,只要连接建立,无论客户端是否有消息发送,都要进行等待处理,一定程度上浪费了服务器端的硬件资源,因此就有了NIO 方式。Java 对于 NIO 方式的支持是通过 Channel和 Selector 方式来实现,采用的方法为向 Channel注册感兴趣的事件,然后通过 Selector 来获取到发生了事件的 key,如发生了相应的事件,则进行相应的处理,否则则不做任何处理,是典型的Reactor 模式,按照这样的方式,就不用像 BIO 方式一样,即使在没有消息的情况下也需要占据一个线程来阻塞读取消息,从而提升服务器的使用效率, 为实现 TCP/IP+NIO 方式的系统间通讯, Java 提供了 SocketChannel和 ServerSocketChannel两个关键的类,网络 IO 的操作则改为通过ByteBuffer 来实现,具体的基于 java实现TCP/IP+NIO 方式的通讯的方法如下所示。

服务器端:

package com.flyoung;import java.io.IOException;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.util.Iterator;import java.util.Set;import java.nio.channels.SocketChannel;public class NIOServer {    /*标志数字*/    private static int flag = 0;    /*定义缓冲区大小*/    private static int block = 4096;    /*接收缓冲区*/    private static ByteBuffer receiveBuffer = ByteBuffer.allocate(block);    /*发送缓冲区*/    private static ByteBuffer sendBuffer = ByteBuffer.allocate(block);    /*定义Selector*/    private Selector selector;        public NIOServer(int port) throws IOException{        //打开服务器套接字通道        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();        //服务器配置为非阻塞        serverSocketChannel.configureBlocking(false);        //检索与此服务器套接字通道关联的套接字        ServerSocket serverSocket = serverSocketChannel.socket();        //进行服务的绑定        serverSocket.bind(new InetSocketAddress(port));        //通过open()方法找到Selector        selector = Selector.open();        //注册到selector        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        System.out.println("Server Start -----8888:");    }    //监听    public void listen() throws IOException{        while(true){            //监控所有注册的 channel ,当其中有注册的 IO 操作可以进行时,该函数返回,并将对应的 SelectionKey 加入 selected-key set            selector.select();            //Selected-key set 代表了所有通过 select() 方法监测到可以进行 IO 操作的 channel ,这个集合可以通过 selectedKeys() 拿到            Set<SelectionKey> selectionKeys = selector.selectedKeys();            Iterator<SelectionKey> iterator = selectionKeys.iterator();            while(iterator.hasNext()){                SelectionKey selectionKey = iterator.next();                handleKey(selectionKey);                iterator.remove();            }        }            }    //处理请求    public void handleKey(SelectionKey selectionKey) throws IOException{        //接受请求        ServerSocketChannel serverSocketChannel = null;        SocketChannel socketChannel = null;        String receiveText;        String sendText;        int count;        //测试此键的通道是否准备好接受新的套接字连接        if(selectionKey.isAcceptable()){            //返回创建此键的通道            serverSocketChannel = (ServerSocketChannel)selectionKey.channel();            //接受客户端建立连接的请求,并返回 SocketChannel 对象            socketChannel = serverSocketChannel.accept();            //配置为非阻塞            socketChannel.configureBlocking(false);            //注册到selector            socketChannel.register(selector, SelectionKey.OP_READ);        }else if(selectionKey.isReadable()){            //返回为之创建此键的通道            socketChannel = (SocketChannel)selectionKey.channel();            //将缓冲区清空,以备下次读取            receiveBuffer.clear();            //将发送来的数据读取到缓冲区                        count = socketChannel.read(receiveBuffer);                                if(count>0){                receiveText = new String(receiveBuffer.array(),0,count);                System.out.println("服务器端接受到的数据---"+receiveText);                socketChannel.register(selector, SelectionKey.OP_WRITE);            }        }else if (selectionKey.isWritable()) {              //将缓冲区清空以备下次写入              sendBuffer.clear();              // 返回为之创建此键的通道。              socketChannel = (SocketChannel) selectionKey.channel();              sendText="message from server--" + flag++;              //向缓冲区中输入数据              sendBuffer.put(sendText.getBytes());               //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位              sendBuffer.flip();              //输出到通道              socketChannel.write(sendBuffer);              System.out.println("服务器端向客户端发送数据--:"+sendText);              socketChannel.register(selector, SelectionKey.OP_READ);          }              }    public static void main(String[] args) throws IOException {        int port = 8888;         NIOServer server = new NIOServer(port);        server.listen();    }}
复制代码

客户端

package com.flyoung;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.Set;public class NIOClient {    /*标识数字*/      private static int flag = 0;      /*缓冲区大小*/      private static int BLOCK = 4096;      /*接受数据缓冲区*/      private static ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);      /*发送数据缓冲区*/      private static ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);      /*服务器端地址*/      private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(              "localhost", 8888);        public static void main(String[] args) throws IOException {          // 打开socket通道          SocketChannel clientChannel = SocketChannel.open();          // 设置为非阻塞方式          clientChannel.configureBlocking(false);          // 打开选择器          Selector selector = Selector.open();          // 注册连接服务端socket动作          clientChannel.register(selector, SelectionKey.OP_CONNECT);          // 连接          clientChannel.connect(SERVER_ADDRESS);              SocketChannel socketChannel;        Set<SelectionKey> selectionKeys;            String receiveText;          String sendText;          int count=0;            while (true) {              //选择一组键,其相应的通道已为 I/O 操作准备就绪。              //监控所有注册的 channel ,当其中有注册的 IO 操作可以进行时,该函数返回,并将对应的 SelectionKey 加入 selected-key set             selector.select();              //返回此选择器的已选择键集。              selectionKeys = selector.selectedKeys();              //System.out.println(selectionKeys.size());              for(SelectionKey selectionKey:selectionKeys){                 //判断是否为建立连接的事件                if (selectionKey.isConnectable()) {                      System.out.println("client connect");                      socketChannel = (SocketChannel) selectionKey.channel();  //                    // 判断此通道上是否正在进行连接操作。                      // 完成套接字通道的连接过程。                      if (socketChannel.isConnectionPending()) {                         //完成连接的建立(TCP三次握手)                        socketChannel.finishConnect();                          System.out.println("完成连接!");                          sendBuffer.clear();                          sendBuffer.put("Hello,Server".getBytes());                          sendBuffer.flip();                          socketChannel.write(sendBuffer);                      }                      socketChannel.register(selector, SelectionKey.OP_READ);                  } else if (selectionKey.isReadable()) {                      socketChannel = (SocketChannel) selectionKey.channel();                      //将缓冲区清空以备下次读取                      receiveBuffer.clear();                      //读取服务器发送来的数据到缓冲区中                      count=socketChannel.read(receiveBuffer);                      if(count>0){                          receiveText = new String( receiveBuffer.array(),0,count);                          System.out.println("客户端接受服务器端数据--:"+receiveText);                          socketChannel.register(selector, SelectionKey.OP_WRITE);                      }                    } else if (selectionKey.isWritable()) {                      sendBuffer.clear();                      socketChannel = (SocketChannel) selectionKey.channel();                      sendText = "message from client--" + (flag++);                      sendBuffer.put(sendText.getBytes());                       //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位                      sendBuffer.flip();                      socketChannel.write(sendBuffer);                      System.out.println("客户端向服务器端发送数据--:"+sendText);                      socketChannel.register(selector, SelectionKey.OP_READ);                  }              }              selectionKeys.clear();          }      }  }

原创粉丝点击