Java NIO 浅析

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();
				}
			}
		}
	}
}

 

 

       

 

 

      

 

 

 

你可能感兴趣的:(java NIO)