1. Java NIO初探
1.1 什么是IO
I/O输入/输出(Input/Output),分为IO设备和IO接口两个部分。 在POSIX兼容的系统上,例如Linux系统,I/O操作可以有多种方式,比如DIO(Direct I/O),AIO(Asynchronous I/O 异步I/O),Memory-Mapped I/O(内存映设I/O)等,不同的I/O方式有不同的实现 方式和性能,在不同的应用中可以按情况选择不同的I/O方式。
IO是主存和外部设备之间拷贝数据的过程。IO的实现是通过操作系统调用IO命令完成的。高级语言对这些IO命令加以封装形成IO api,应用程序通过调用这些api完成网络通信,文件存储,程序的输入输出等。
1.2 Java NIO
Java NIO 是 Java New IO 的简称,实在jdk1.4版本之后提供的新的IO api。该api提供了无阻塞的io访问的实现方式。
Sun 标榜的Java NIO特性如下:
– 为所有的原始类型提供 (Buffer) 缓存支持。
– 字符集编码解码解决方案。
– Channel :一个新的原始 I/O 抽象。
– 支持锁和内存映射文件的文件访问接口。
– 提供多路 (non-bloking) 非阻塞式的高伸缩性网络 I/O 。
1.3 Java NIO 实战
a)现在实用JAVA NIO实现一个文本文件拷贝的过程。
package org.cookie.nio; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; public class NIO { public static void main(String args[]) throws IOException { String filePathIn = "c:\\nio_test.sql"; String filePathOut = "d:\\nio_test_n.txt"; File fileOutN = new File("d:\\nio_test_nn.txt"); File fileOut = new File(filePathOut); if (!fileOut.exists()) { fileOut.createNewFile(); } if (!fileOutN.exists()) { fileOutN.createNewFile(); } FileInputStream fileInputStream = new FileInputStream(filePathIn); FileOutputStream fileOutputStream = new FileOutputStream(filePathOut); FileOutputStream fileOutputStreamN = new FileOutputStream(fileOutN); FileChannel fileInputChannel = fileInputStream.getChannel(); //从文件流中拿到文件通道 FileChannel fileOutputChannel = fileOutputStream.getChannel(); FileChannel fileOutputChannelN = fileOutputStreamN.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//建立一个缓存通道 while (true) { byteBuffer.clear();//清空通道,byteBuffer变得可写 int r = fileInputChannel.read(byteBuffer);//文件通道向byteBuffer读入数据 if (r == -1) { break; } byteBuffer.flip();//环绕byteBuffer这时候可以将byteBuffer写入到文件通道中 fileOutputChannel.write(byteBuffer); byteBuffer.flip();//byteBuffer通道可以实现重复读,在读之前需要调用byteBuffer的flip方法 fileOutputChannelN.write(byteBuffer); byteBuffer.flip(); System.out.println(byteBufferToString(byteBuffer)); } fileInputStream.close(); fileOutputStream.close(); fileOutputStreamN.close(); } public static String byteBufferToString(ByteBuffer buffer) { System.out.println("buffer=" + buffer); try { Charset charset = Charset.forName("UTF-8");//声明一个字符集 CharBuffer charBuffer = charset.decode(buffer);//通过字符集对buffer进行编码 System.out.println("charBuffer=" + charBuffer); System.out.println(charBuffer.toString()); return charBuffer.toString(); } catch (Exception ex) { ex.printStackTrace(); return ""; } } }
b) 通过 Java AIO实现一个简单的服务端,客户端通信
JAVA NIO Server 端
package com.cookie.study.java; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.Iterator; public class HeartServerNIO { public static String decode(ByteBuffer byteBuffer){ Charset charset = Charset.forName("UTF-8"); CharBuffer charBuffer = charset.decode(byteBuffer); return charBuffer.toString(); } public static void main(String args[]) { try { Selector selector = Selector.open();//创建一个selector对象,selector是在Java网络aio中最重要的对象 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//建立一个ServerSocketChannel serverSocketChannel.configureBlocking(false);//设置ServerSocketChannel为非阻塞的方式 InetSocketAddress address = new InetSocketAddress(9000); serverSocketChannel.socket().bind(address); //注册Selector,通过SelectionKey.OP_ACCEPT SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true){ int selectedNum = selector.select();//Selects a set of keys whose corresponding channels are ready for I/O operations. 翻译为:选取一下已经准备好IO操作的通道的SelectionKey System.out.println("Selected Number is :"+selectedNum); Iterator iter = selector.selectedKeys().iterator();//迭代SelectionKeys while(iter.hasNext()){ SelectionKey selectedKey = (SelectionKey)iter.next(); if ((selectedKey.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT){// 处理OP_ACCEPT事件 ServerSocketChannel serverChannel = (ServerSocketChannel)selectedKey.channel(); SocketChannel socketChannel = serverChannel.accept(); socketChannel.configureBlocking(false); SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ);//注册OP_READ事件 iter.remove(); }else if ( (selectedKey.readyOps()&SelectionKey.OP_READ) == SelectionKey.OP_READ ){//处理OP_READ事件 ByteBuffer buffer = ByteBuffer.allocate(1024); SocketChannel socketChannel = (SocketChannel)selectedKey.channel(); while (true){ buffer.clear(); int i=socketChannel.read(buffer);//将socket数据读入byteBuffer中 if (i == -1) break; if (i == 0) break; buffer.flip();//环绕byteBuffer,准备读取buffer数据 socketChannel.write(buffer); buffer.flip();//需要再次读取时,需要再次调用buffer的环绕方法 System.out.println(decode(buffer)); } iter.remove(); } } } } catch (ClosedChannelException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
Java NIO Client 端
package com.cookie.study.java; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class HeartClientNIO extends Frame { /* 标识数字 */ 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 TextField tfText; private TextArea taContent; private TFListener tfListener = new TFListener(); /** * 注意,入口... ^^ * * @param args */ public static void main(String[] args) { new HeartClientNIO().launchFrame(); } /** * Loading GU */ public void launchFrame() { tfText = new TextField(); taContent = new TextArea(); this.setSize(300, 300); this.setLocation(300, 300); this.tfText.addActionListener(this.tfListener); this.add(tfText, BorderLayout.SOUTH); this.add(taContent, BorderLayout.NORTH); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); this.pack(); this.connect(); this.setVisible(true); } /** * 我在努力地连接服务器中... */ public void connect() { try { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); Selector selector = Selector.open(); socketChannel.register(selector, SelectionKey.OP_CONNECT); InetSocketAddress inetSocketAddress = new InetSocketAddress( "localhost", 9000); socketChannel.connect(inetSocketAddress); this.tfListener.setSelector(selector); Set<SelectionKey> selectionKeys; Iterator<SelectionKey> iterator; SelectionKey selectionKey; SocketChannel client; String receiveText; String sendText; int count = 0; // 选择一组键,其相应的通道已为 I/O 操作准备就绪。 // 此方法执行处于阻塞模式的选择操作。 selector.select(); // 返回此选择器的已选择键集。 selectionKeys = selector.selectedKeys(); System.out.println(selectionKeys.size()); iterator = selectionKeys.iterator(); while (iterator.hasNext()) { selectionKey = iterator.next(); if (selectionKey.isConnectable()) { System.out.println("client connect"); client = (SocketChannel) selectionKey.channel(); // 判断此通道上是否正在进行连接操作。 // 完成套接字通道的连接过程。 if (client.isConnectionPending()) { client.finishConnect(); System.out.println("完成连接!"); sendbuffer.clear(); sendbuffer.put("Hello,Server".getBytes("UTF-8")); sendbuffer.flip(); client.write(sendbuffer); } client.register(selector, SelectionKey.OP_READ); } } selectionKeys.clear(); new Thread(new ReadProcess(selector)).start(); } catch (UnknownHostException e) { System.out.println("UnknownHostException"); e.printStackTrace(); } catch (IOException e) { System.out.println("IOException"); e.printStackTrace(); } finally { // 关闭啥尼... } } /** * 额不会傻等着tfText(TextField tfText的监听器类) */ class TFListener implements ActionListener { private String str; private Selector selector; @Override public void actionPerformed(ActionEvent e) { str = tfText.getText().trim(); tfText.setText(""); try { if (selector != null) { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); if (selectionKey.isWritable()) { sendbuffer.clear(); SocketChannel client = (SocketChannel) selectionKey.channel(); String sendText = "message from client--" + (flag++)+str; sendbuffer.put(sendText.getBytes("UTF-8")); // 将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位 sendbuffer.flip(); client.write(sendbuffer); System.out.println("客户端向服务器端发送数据--:" + sendText); client.register(selector, SelectionKey.OP_READ); } } selectionKeys.clear(); } } catch (ClosedChannelException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } } public void setSelector(Selector selector) { this.selector = selector; } } public class ReadProcess implements Runnable{ private Selector selector; public ReadProcess(Selector selector){ this.selector = selector; } public void run() { while(true){ try { selector.select(); Set<SelectionKey> selectionKeys = this.selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while(iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); if(selectionKey.isReadable()){ SocketChannel client = (SocketChannel) selectionKey.channel(); // 将缓冲区清空以备下次读取 receivebuffer.clear(); // 读取服务器发送来的数据到缓冲区中 int count = client.read(receivebuffer); if (count > 0) { String receiveText = new String(receivebuffer.array(), 0, count,"UTF-8"); System.out.println("客户端接受服务器端数据--:" + receiveText); taContent.setText(taContent.getText()+"\n"+receiveText); client.register(selector, SelectionKey.OP_WRITE); } } } selectionKeys.clear(); } catch (IOException e) { e.printStackTrace(); } } } } }