1、JAVA NIO介绍
Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式监察I/O端口,有内容进来会自动通知,这样就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,程序接着做别的事情,当有事件发生时,它会通知程序,传回一组SelectionKey,程序读取这些Key,就会获得刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,接着程序可以处理这些数据。Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,它就会站起来报告,交出一把钥匙,让程序通过这把钥匙来读取这个channel的内容。
2、NIO和Standard IO区别
Standard IO把IO抽象成对字节流的读写,在进行IO之前,首先都要创建一个流对象。流对象在进行读写操作时都是按字节来进行的,一个字节一个字节的来读或者写的。
NIO把IO抽象成对块的读写,类似于磁盘的读写,每次IO操作的单位都是一个块,块被读入内存之后的存在形式就是一个字节数组,也就是说NIO可以一次性读/写多个字节。
3、重要概念
Channel
Channel表示程序与某一个IO实体之间的连接,Channel是双向的,不区分输入流和输出流,类似于Unix File的fd。
Buffer
Buffer就是一个字节数组,一块内存。Buffer和Channel每次都是一起使用的,每次都是从一个Channel中读出一个Buffer或者是把一个Buffer写入到一个Channel中。
Selector
Selector是用来实现事件驱动的IO Multiplexing,支持多种实现,BSD用kqueue()的实现,Linux用epoll()实现。
4、代码示例
服务器端:SumServer.java类
- package nio;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.nio.ByteBuffer;
- import java.nio.IntBuffer;
- import java.nio.channels.SelectionKey;
- import java.nio.channels.Selector;
- import java.nio.channels.ServerSocketChannel;
- import java.nio.channels.SocketChannel;
- import java.nio.channels.spi.SelectorProvider;
- import java.util.Iterator;
- import java.util.Set;
- /**
- * SumServer.java
- *
- *
- * Created
- *
- * @author
- * @version 1.0
- */
- publicclass SumServer {
- private ByteBuffer _buffer = ByteBuffer.allocate(8);
- private IntBuffer _intBuffer = _buffer.asIntBuffer();
- private SocketChannel _clientChannel =null;
- private ServerSocketChannel _serverChannel =null;
- publicvoid start() {
- try {
- openChannel();
- waitForConnection();
- } catch (IOException e) {
- System.err.println(e.toString());
- }
- }
- privatevoid openChannel()throws IOException {
- _serverChannel = ServerSocketChannel.open();
- _serverChannel.socket().bind(new InetSocketAddress(10001));
- _serverChannel.configureBlocking(false);// 设置为非阻塞形式
- System.out.println("服务器通道已经打开");
- }
- /*
- * private void waitForConnection() throws IOException { while (true) {
- * _clientChannel = _serverChannel.accept(); if (_clientChannel != null) {
- * System.out.println("新的连接加入"); processRequest(); _clientChannel.close(); } } }
- */
- privatevoid waitForConnection()throws IOException {
- Selector acceptSelector = SelectorProvider.provider().openSelector();
- /*
- * 在服务器套接字上注册selector并设置为接受accept方法的通知。
- * 这就告诉Selector,套接字想要在accept操作发生时被放在ready表 上,因此,允许多元非阻塞I/O发生。
- */
- SelectionKey acceptKey = _serverChannel.register(acceptSelector,
- SelectionKey.OP_ACCEPT);
- int keysAdded =0;
- /* select方法在任何上面注册了的操作发生时返回 */
- while ((keysAdded = acceptSelector.select()) >0) {
- // 某客户已经准备好可以进行I/O操作了,获取其ready键集合
- Set readyKeys = acceptSelector.selectedKeys();
- Iterator i = readyKeys.iterator();
- // 遍历ready键集合,并处理加法请求
- while (i.hasNext()) {
- SelectionKey sk = (SelectionKey) i.next();
- i.remove();
- if (sk.isAcceptable()) {
- ServerSocketChannel nextReady = (ServerSocketChannel) sk
- .channel();
- // 接受加法请求并处理它
- _clientChannel = nextReady.accept();
- // Socket _clientSocket = _clientChannel.socket();
- processRequest();
- // _clientSocket.close();
- }
- }
- }
- }
- privatevoid processRequest()throws IOException {
- _buffer.clear();
- _clientChannel.read(_buffer);
- int result = _intBuffer.get(0) + _intBuffer.get(1);
- _buffer.flip();
- _buffer.clear();
- _intBuffer.put(0, result);
- _clientChannel.write(_buffer);
- }
- publicstaticvoid main(String[] args) {
- new SumServer().start();
- }
- } // SumServer
客户端:SumClient.java类
- package nio;
- import java.nio.ByteBuffer;
- import java.nio.IntBuffer;
- import java.nio.channels.SocketChannel;
- import java.net.InetSocketAddress;
- import java.io.IOException;
- /**
- * SumClient.java
- *
- *
- * Created
- *
- * @author
- * @version 1.0
- */
- publicclass SumClient {
- private ByteBuffer _buffer = ByteBuffer.allocate(8);
- private IntBuffer _intBuffer;
- private SocketChannel _channel;
- public SumClient() {
- _intBuffer = _buffer.asIntBuffer();
- } // SumClient constructor
- publicint getSum(int first,int second) {
- int result =0;
- try {
- _channel = connect();
- sendSumRequest(first, second);
- result = receiveResponse();
- } catch (IOException e) {
- System.err.println(e.toString());
- } finally {
- if (_channel !=null) {
- try {
- _channel.close();
- } catch (IOException e) {
- }
- }
- }
- return result;
- }
- private SocketChannel connect()throws IOException {
- InetSocketAddress socketAddress = new InetSocketAddress("localhost",
- 10001);
- return SocketChannel.open(socketAddress);
- }
- privatevoid sendSumRequest(int first,int second)throws IOException {
- _buffer.clear();
- _intBuffer.put(0, first);
- _intBuffer.put(1, second);
- _channel.write(_buffer);
- System.out.println("发送加法请求 " + first +"+" + second);
- }
- privateint receiveResponse()throws IOException {
- _buffer.clear();
- _channel.read(_buffer);
- return _intBuffer.get(0);
- }
- publicstaticvoid main(String[] args) {
- SumClient sumClient = new SumClient();
- System.out.println("加法结果为 :" + sumClient.getSum(100,324));
- }
- } // SumClient