NIO学习笔记(1)--非阻塞I/O

一. 什么是阻塞?

阻塞(Block)这个概念。当进程调用一个阻塞的系统函数时,该进程被置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比如网络上接收到数据包,或者调用sleep指定的睡眠时间到了)它才有可能继续运行。

我自己是这样理解(同步/异步、阻塞/非阻塞)的:

所谓同步就是当一个进程发起一个函数(任务)调用的时候,一直会到函数(任务)完成。进程继续往下执行。而异步这不会这样,异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行当,函数返回的时候通过状态、通知、事件。等方式通知进程任务完成。

而阻塞和非阻塞的概念相对明了多了。阻塞是当请求不能满足的时候就试进程挂起,非阻塞则是直接返回


二.NIO包

java.nio包是Java在1.4之后增加的,用来提高I/O操作的效率。在nio包中主要包括以下几个类或接口:

* Buffer:缓冲区,用来临时存放输入或输出数据。

* Charset:用来把Unicode字符编码和其它字符编码互转。

* Channel:数据传输通道,用来把Buffer中的数据写入到数据源,或者把数据源中的数据读入到Buffer。

* Selector:用来支持异步I/O操作,也叫非阻塞I/O操作。


nio包中主要通过下面两个方面来提高I/O操作效率:

* 通过Buffer和Channel来提高I/O操作的速度。

* 通过Selector来支持非阻塞I/O操作。


三. 代码示例(文件读写 & socket通讯)

以下是 BIO,和NIO的 文件读写、socket通讯,大家可以对比下有什么不一样
BIO:

public class FileIODemo {
	public static void main(String[] args) {
		//获取文件
		File fileRead = new File("fileIORead.txt");
		File fileWrite = new File("fileIOWrite.txt");
		
		//字节流
		try {
			FileInputStream input=new FileInputStream(fileRead);
			FileOutputStream out=new FileOutputStream(fileWrite);
			
			//缓冲数组
			byte[] buffer=new byte[10];
			int i=0;
			while(input.read(buffer)!=-1){
				System.out.println("i:"+i++);
				out.write(buffer);
			}
			input.close();
			out.close();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

NIO:

public class FileNIODemo {
	public static void main(String[] args) {
		//获取文件
		File fileRead = new File("fileIORead.txt");
		File fileWrite = new File("fileIOWrite.txt");
		
		
		try {
			FileInputStream input = new FileInputStream(fileRead);
			FileOutputStream out=new FileOutputStream(fileWrite);
			FileChannel inputChannel= input.getChannel();
			FileChannel outputChannel=out.getChannel();
			
			ByteBuffer buffer=ByteBuffer.allocate(10);
			int i=0;
			while(true){
				buffer.clear();
				int res=inputChannel.read(buffer);
				if(res==-1){
					break;
				}
				buffer.flip();
				outputChannel.write(buffer);
				System.out.println("i:"+i++);
			}
			
			inputChannel.close();
			input.close();
			outputChannel.close();
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

====================================================================================================================================

咱们再来看看 socket;

BIO:

//客户端
public class ClientIODemo {
	public static void main(String[] args) {
		try {
			Socket socket=new Socket("127.0.0.1",12345);
			
			InputStream in=socket.getInputStream();
			OutputStream out=socket.getOutputStream();
			
			//客户端发送消息
			String message="[client]: send a message:发送消息!";
			byte[] context=message.getBytes();
			out.write(context);
			out.flush();
			System.out.println(message);
			
			//客户端接受消息
                       StringBuilder feedback = new StringBuilder();
			byte[] buffer=new byte[1000];
			in.read(buffer);
			feedback.append(new String(buffer,Charset.defaultCharset()));
			
			System.out.println("[client]: receive a message:"+feedback.toString());
			
			in.close();
			out.close();
			
			socket.close();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
}
//服务端
public class ServerIODemo {
	public static void main(String[] args) {
		try {
			ServerSocket server=new ServerSocket(12345);
			while(true){
				Socket socket = server.accept();//阻塞
				
				InputStream in=socket.getInputStream();
				OutputStream out=socket.getOutputStream();

				StringBuilder message=new StringBuilder();
				byte[] buffer=new byte[10];
				int i=0;
				while(true){
					i++;
					in.read(buffer); //当发现没有值read的时候,就阻塞住了
					message.append(new String(buffer,Charset.defaultCharset()));
					if(i>10){
						break;
					}
				}
				System.out.println("[server]:"+message);
				
				String messages="[server]: send a message:发送消息!";
				byte[] context=messages.getBytes();
				out.write(context);
				out.flush();

				in.close();
				out.close();
				socket.close();
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

NIO:

//客户端
public class ClientNIODemo {
	public static void main(String[] args) {
		try {
			//创建客户端通道
			SocketChannel client=SocketChannel.open();
			Selector selector= Selector.open();
			//设置成非阻塞
			client.configureBlocking(false);
			client.connect(new InetSocketAddress("127.0.0.1", 12345));
			//注册监听Connect
			client.register(selector, SelectionKey.OP_CONNECT);
			boolean complate=true;
			while(complate){
				try {
					selector.select();
				} catch (IOException e) {
					e.printStackTrace();
					break;
				}
				Set keys= selector.selectedKeys();
				Iterator iterator = keys.iterator();
				
				while(iterator.hasNext()){
					SelectionKey key=iterator.next();
					iterator.remove();

					if(key.isConnectable()&&client.finishConnect()){
						System.out.println("=============connectable");
						key.interestOps(SelectionKey.OP_WRITE);
					}
					if(key.isReadable()){
						System.out.println("=============readable");
						SocketChannel channel=(SocketChannel) key.channel();
						ByteBuffer buffer=ByteBuffer.allocate(10);
						StringBuilder feedback=new StringBuilder();
						
						while(true){
							int res=channel.read(buffer);
							if(res==-1||res==0){
								break;
							}else if(res>0){
								buffer.flip();
								feedback.append(new String(buffer.array(),Charset.defaultCharset()));
								buffer.clear();
							}
						}
						
						System.out.println("[client]:"+feedback);
						complate=false;
					}
					if(key.isWritable()){
						System.out.println("=============writeable");
						SocketChannel channel=(SocketChannel) key.channel();
						ByteBuffer buffer=ByteBuffer.allocate(100);
						String message="send a message:客户端发消息了!";
						byte[] context=message.getBytes();
						buffer.put(context);
						buffer.flip();
						channel.write(buffer);
						key.interestOps(SelectionKey.OP_READ);
					}
				}
			}
			client.close();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}
}
//服务端
public class ServerNIODemo {
	public static void main(String[] args) {
		ServerSocketChannel serverChannel;
		Selector selector;
		
		try{
			//服务端的准备
			serverChannel= ServerSocketChannel.open();
			ServerSocket serverSocket=serverChannel.socket();
			serverSocket.bind(new InetSocketAddress(12345));
			serverChannel.configureBlocking(false);
			selector=Selector.open();
			serverChannel.register(selector, SelectionKey.OP_ACCEPT);
		} catch (IOException e){
			e.printStackTrace();
			return;
		}
		
		while(true){
			try {
				selector.select();
			} catch (IOException e) {
				e.printStackTrace();
				break;
			}
			Set readKeys = selector.selectedKeys();
			Iterator iterator = readKeys.iterator();
			
			while(iterator.hasNext()){
				SelectionKey key=iterator.next();
				iterator.remove();
				
				try {
					if(key.isAcceptable()){
						System.out.println("=============accept");
						ServerSocketChannel server = (ServerSocketChannel) key.channel();
						//接受客户端的请求
						SocketChannel client=server.accept();
						//客户端 服务端 全都设置为 非阻塞
						client.configureBlocking(false);
						client.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocate(100));
					}
					if(key.isReadable()){
						System.out.println("=============read");
						SocketChannel client=(SocketChannel) key.channel();
						ByteBuffer buffer=ByteBuffer.allocate(10);
						WritableByteChannel out = Channels.newChannel(System.out);
						
						while(true){
							int res=client.read(buffer);
							if(res==-1||res==0){
								break;
							}else if(res>0){
								buffer.flip();
								out.write(buffer);
								buffer.clear();
							}
						}
						System.out.println();
						key.interestOps(SelectionKey.OP_WRITE);
					}
					if(key.isWritable()){
						System.out.println("=============write");
						SocketChannel client=(SocketChannel) key.channel();
						ByteBuffer buffer=(ByteBuffer) key.attachment();
						
						String message="send a message:服务端发消息了!";
						byte[] context=message.getBytes();

						buffer.clear();
						buffer.put(context);
						buffer.flip();
						client.write(buffer);
						key.cancel();
						System.out.println("=============send");
						try {
							//取消键后仍可以得到键的通道
							key.channel().close();
						} catch (IOException e1) {
							e1.printStackTrace();
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
					
				}
			}
		}
	}
}

从以上2个列子可以可以看出:

对于BIO如果服务端读取客户端发来的信息,如果用while(true)进行循环读取,读到-1时截止,那么必须要将客服端进行关闭,否则客户端和服务端就都等待对方进行发送消息,或者关闭。也就卡死在那了。

对于NIO来说,不管服务端or客户端,只要没有数据读取直接进行返回,返回为0,如果关闭则返回-1。从而可以进行其他的逻辑。 这也就是非阻塞的概念。

对比发现,NIO多了 Channel 和 Buffer 这2个重要的类!后续我们将着重进行介绍 “通道”“缓存区”

你可能感兴趣的:(java)