BIO,NIO,AIO的理解。

都用程序来说明,当然远不止下面的程序的对比。还需要自己取更深的理解。以下的内容的大前提。
大前提:所有的IO操作都分为磁盘IO和网络IO。都是以磁盘IO为例。
应用场景copy网上一句话:
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

1, 同步阻塞的BIO
程序举例:

package com.auto.demo;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class NIOTest {

public static void main(String[] args) throws FileNotFoundException {
    
    // 输入输出文件流
    FileInputStream fis = new FileInputStream("C:\\Users\\zy962\\Desktop\\tip说明.txt");
    FileOutputStream fos = new FileOutputStream("C:\\Users\\zy962\\Desktop\\NIO测试.txt");
    //ByteArrayOutputStream baos = new ByteArrayOutputStream();
    
    // 创建缓冲区(注意这个缓冲区和NIO的区别)
    byte[] buffer = new byte[1024];
    
    int length = 0;
    try {
        while ((length = fis.read(buffer)) != -1) {
            fos.write(buffer, 0, length);
        }
        //byte[] data = baos.toByteArray();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fos.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
}

2, 同步非阻塞的NIO(JDK1.4)
程序举例:

package com.auto.demo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOTest {

public static void main(String[] args) throws FileNotFoundException {
    
    // 输入输出文件流
    FileInputStream fis = new FileInputStream("C:\\Users\\zy962\\Desktop\\tip说明.txt");
    FileOutputStream fos = new FileOutputStream("C:\\Users\\zy962\\Desktop\\NIO测试.txt");
    
    // 输入输出管道
    FileChannel fci = fis.getChannel();
    FileChannel fco = fos.getChannel();
    
    // 创建缓冲
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    
    // 写入
    int flag = 0;
    try {
        while ((flag = fci.read(buffer)) != -1) {
            buffer.flip();
            fco.write(buffer);
            buffer.clear();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fco.close();
            fci.close();
            fos.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
}

读完程序我们可以知道,最大的差距:操作过程间加入了管道和缓冲区
操作过程:输入输出流获取-> 创建输入输出管道->创建缓冲区->通过输入管道写入缓冲区->通过输出管道缓冲区内容写入。注意在输出写入钱需要filp重置,写入完成后需要clear重置。

3, 异步非阻塞的AIO( 即是NIO 2.0)
能力有限,以后理解了再来写。
借用别人的话。
AIO与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。 在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道:
AsynchronousSocketChannel
AsynchronousServerSocketChannel
AsynchronousFileChannel
AsynchronousDatagramChannel
其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。

NIO(含AIO)家族截图:
在这里插入图片描述
网络IO的理解:(这个还需要更加加深理解)

首先、socket的目的是创建连接,发送消息,socket是对tcp/ip或udp/ip协议的封装,socket本身只是一个调用接口,他是一个中间工具存在于操作系统的内核中,你可以理解为api,有两端,客户端需要ip+port去连接发送消息,服务端serversocket需要指定端口接受处理消息以及回复消息。

对BIO理解:
BioServer

package zhouyi.bio;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class BioServer {

public static void main(String[] args) {
	
	// 指定服务端口号
	int port = 8002;
	ServerSocket server = null;
	
	try {
		server = new ServerSocket(port);
		Socket socket = null;
		// 是否有数据传输
		while (true) {
			// 只能接受一个服务
			socket = server.accept();
			// 新的请求就需要新线程去处理
			new Thread(new BioServerHandler(socket)).start();
		}
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		if (server != null) {
			try {
				server.close();
				server = null;
				System.out.println("服务关闭结束");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
}

}

处理线程:

package zhouyi.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class BioServerHandler implements Runnable {

private Socket socket;

public BioServerHandler(Socket socket) {
	this.socket = socket;
}

@Override
public void run() {
	// 客服端信息读入
	BufferedReader reader = null;
	// 返回客服端信息
	PrintWriter writer = null;
	
	try {
		reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
		writer = new PrintWriter(this.socket.getOutputStream(), true);
		StringBuffer buffer = new StringBuffer();
		String bodyString = null;
		while (true) {
			bodyString = reader.readLine();
			if (bodyString == null) {
				break;
			}
			buffer.append(";" + bodyString + ";");
			System.out.println("请求消息主题:" + bodyString);
			writer.println(buffer);
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		if (writer != null) {
			writer.close();
			writer = null;
		}
		if (reader != null) {
			try {
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if (this.socket != null) {
			try {
				this.socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			this.socket = null;
		}
	}
}

}

客服端:

package zhouyi.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class BioClientServer {

public static void main(String[] args) {
	int port = 8002;
	Socket socket = null;
	BufferedReader reader = null;
	PrintWriter writer = null;
	
	try {
		// IP和端口号绑定
		socket = new Socket("127.0.0.1", port);
	} catch (UnknownHostException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	try {
		writer = new PrintWriter(socket.getOutputStream(), true);
		reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
	} catch (IOException e) {
		e.printStackTrace();
	}
	writer.println("客服端请求request");
	System.out.println("客服端请求已经发出");
	
	try {
		// 返回结果如下
		String readString = reader.readLine();
		System.out.println("返回结果如下:" + readString);
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		if (writer != null) {
			writer.close();
			writer = null;
		}
		if (reader != null) {
			try {
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			reader = null;
		}
		if (socket != null) {
			try {
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			socket = null;
		}
	}
}
}
运行结果:(我们可以看到这是一对一的,来一个请求就需要新建一个线程,当请求很多时候,显然是低效的,而且服务器要占用很多)

在这里插入图片描述
NIO的理解:
服务端

package zhouyi.nio;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NioServer {

// 管道管理器
private Selector selector;

/**
 * 绑定端口
 * @param port
 */
public void initServer(int port) {
	
	// 获得一个socket通道
	try {
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		// 设置为非阻塞通道
		serverSocketChannel.configureBlocking(false);
		// 将通道绑定到对应的serversocket的port上
		serverSocketChannel.socket().bind(new InetSocketAddress(port));
		// 上面做完进程中就有socke去监听设置的端口,这里只是进入了监听状态,并不是和客户端的连接状态,因此后面还需要再处理。
		// 获得一个通道管理,多路复用
		this.selector = selector.open();
		// 将通道管理器和该通道绑定,并为该通道注册SelectKey.OP_ACCEPT事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
	} catch (IOException e) {
		e.printStackTrace();
	}
}

/**
 * 采用轮询的方式监听selector上是否需要处理的事件,如果有,则进行处理
 */
public void listen() {
	System.out.println("服务端启动成功!");
	// 轮询访问selector
	while (true) {
		try {
			selector.select();
			// 处理selector中的注册事件
			Iterator<?> it = this.selector.selectedKeys().iterator();
			while (it.hasNext()) {
				SelectionKey key = (SelectionKey) it.next();
				// 删除已选key,防止重复
				it.remove();
				serverHandler(key);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

/**
 * 请求处理
 * @param key
 */
private void serverHandler(SelectionKey key) {
	// 这就是客户端连接后的处理。
	// 客服端请求连接事件(轮询)
	if (key.isAcceptable()) {
		serverHandlerAccept(key);
		// 获得了可读事件
	} else if (key.isReadable()) {
		serverHandlerRead(key);
	}
}

/**
 * 处理读事件
 * @param key
 */
private void serverHandlerRead(SelectionKey key) {
	// 得到读通道
	SocketChannel channel = (SocketChannel) key.channel();
	ByteBuffer buffer = ByteBuffer.allocate(1024);
	try {
		int read = channel.read(buffer);
		if (read > 0) {
			byte[] data = buffer.array();
			String msg = new String(data).trim();
			System.out.println(msg);
			// 写数据
			ByteBuffer outBuffer = ByteBuffer.wrap(("服务器收到的消息" + msg).getBytes());
			channel.write(outBuffer);
		} else {
			System.out.println("服务器关闭");
			key.cancel();
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

/**
 * 处理连接请求
 * @param key
 */
private void serverHandlerAccept(SelectionKey key) {
	ServerSocketChannel server = (ServerSocketChannel) key.channel();
	//获取和客服端连接的通道
	try {
		SocketChannel socketChannel = server.accept();
		socketChannel.configureBlocking(false);
		System.out.println("新的客服端连接");
		socketChannel.register(this.selector, SelectionKey.OP_READ);
	} catch (IOException e) {
		e.printStackTrace();
	}
}

/**
 * 启动服务端测试
 * @param args
 */
public static void main(String[] args) {
	NioServer server = new NioServer();
	server.initServer(8082);
	server.listen();
}
}

客服端:

package zhouyi.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;


public class NioClient {

public static Selector selector;

public static SocketChannel socketClientChannel;

private static void init() {
	try {
		selector = Selector.open();
		socketClientChannel = SocketChannel.open();
		// 设置为阻塞模式
		socketClientChannel.configureBlocking(false);
		socketClientChannel.register(selector, SelectionKey.OP_READ);
		/*
		 * while (!socketClientChannel.finishConnect()) {
		 * 
		 * }
		 */
		System.out.println("已经连接服务器!");
	} catch (IOException e) {
		e.printStackTrace();
	}
}

public static void main(String[] args) throws IOException {
	init();
	SocketChannel socketChannel = NioClient.socketClientChannel;
	ByteBuffer buffer = ByteBuffer.allocate(1024);
	new ExecutorThread(selector, socketChannel).start();
	while (true) {
		// 输入
		Scanner scanner = new Scanner(System.in);
		String word = scanner.nextLine();
		buffer.put(word.getBytes());
		buffer.flip();
		socketChannel.write(buffer);
		buffer.clear();
	}
}

static class ExecutorThread extends Thread {

	private Selector selector;
	
	public ExecutorThread(Selector selector, SocketChannel socketChannel) {
		this.selector = selector;
	}
	
	@Override
	public void run() {
		// 轮询等待服务器给返回值
		while (true) {
			try {
				selector.select();
				Set<SelectionKey> keys = selector.selectedKeys();
				Iterator<SelectionKey> keyIterator = keys.iterator();
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				while (keyIterator.hasNext()) {
					SelectionKey selectionKey = keyIterator.next();
					if (selectionKey.isValid()) {
						// 可读的那么就来读一波
						if (selectionKey.isReadable()) {
							SocketChannel socketChannel = (SocketChannel) selectionKey
									.channel();
							// 读取服务器的返回值
							socketChannel.read(buffer);
							buffer.flip();
							byte[] bytes = new byte[buffer.remaining()];
							buffer.get(bytes);
							System.out.println(new String(bytes));
							buffer.clear();
						}
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
}
}

总感觉网络IO理解起来很费劲!思维有点有点绕,或许自己都是本地模拟的场景,显得总有点不合实际的感觉。这种思维网络IO
的理解我还需要更加更加深刻的理解。
真的要去浅显的理解的话,也还好。
其实它和磁盘IO差距只有一个,那就是需要建立连接,其他的操作很是类似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值