一.NIO产生的背景
BIO,输入输出(原始)NIO(Not-blocking IO) 非阻塞的IO模型
channel高速公路
(selector选择器)客户端 --------------------------> 服务端 多路复用技术
主要目的:提高程序的性能,借鉴了一些先进的思想
BIO和 NIO都是 同步IO
二.必须明白的几个概念
1).阻塞(Block)和非阻塞(NoN-block) 都是面向数据而言
阻塞:数据没有准备好,程序就一直等待,直到数据准备好之后才往下执行
非阻塞:不管我的数据有没有准备好,程序都往下执行
2).同步和异步 相对于我们的IO事件而言的
同步:当程序处理IO的时候,程序不能去干别的事情的,要等着IO事件完成之后才能去做别的事情
异步:不关心IO处理过程,只要在处理IO的时候,可以去做别的事情,然后等待IO事件处理完成的通知
三.java现有的IO模型
BIO JDK1.4以前的IO模型,阻塞IO
NIO JDK1.4以后新增的IO模型,非阻塞IO,借鉴Linux的多路复用技术,轮询机制
AIO JDK1.7以后才有的IO模型,真正的异步处理,把IO读写操作完全交给我们的操作系统,学习Linux epoll模式
四.java NIO原理的解读
1).多路复用(Channel通道) 相当于是高速公路
2).轮询机制(Seletor选择器)
Client(客户) (Selector)管家 Boss(老板)
客人跟管家说,我要找你们老板帮忙,管家就跟客人说,我们老板现在比较忙,说你先坐会儿,先喝杯茶. (注册行为),给客户一个牌号(SelectionK),001号,过来,现在老板有空了,可以处理了,处理完之后,牌号要回收,给下一个人用,客户就可以打发走了.
一定要等IO事件做完之后,客户才能走 (同步)
3). SelectionKey 相当于是时间标签
4).Buffer(数据缓冲区)
5).NIO常用APIChannel:
服务端: ServerSocketChannel BIO:ServerSocket
客户端: SocketChannel BIO:Socket
open(); //修路,把高速公路修起来
channel.regist(selector,注册的标识)
Selector 选择器
open(); //开始营业
selectedKeys(); //获取到当前已经注册了的所有牌号信息
Buffer
put(); //往缓冲区里面写数据
get(); //读数据
flip(); //切换读写模式
clear(); //清空缓冲区
SelectionKey 牌号
isAccptable() //是否可以接受客户端连接
isConnctionable() //是否已经连接
isReadable() //缓冲区是否可读
isWriteable() //是否可写
五.java AIO简介 异步非阻塞IO
现在还没有流行起来,了解即可
服务端: AsynchronousServerSocketChannel
客户端: AsynchronousSocketChannel
CompletionHandler:通知程序,IO操作是否成功,还是失败
六.编写代码并演示
BIO: 阻塞IO 同步
package com.yz.io.bio;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by yz on 2018/2/5.
*/
public class BioServer {
ServerSocket server ;
/**
* 构造一个服务端
* @param port
*/
public BioServer(int port){
try {
server = new ServerSocket(port);
System.out.println("Bio服务已启动,监听端口是: "+port);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 监听客户端过来的请求
*/
public void listener() throws IOException {
while (true){
Socket client = server.accept();
InputStream is = client.getInputStream();
byte[] buff = new byte[1024];//缓冲区
int len = is.read(buff);
if(len > 0 ){
String msg = new String(buff,0,len);
System.out.println("接收到客户端发来的消息:"+msg);
}
}
}
public static void main(String[] args) throws IOException {
new BioServer(8080).listener();
}
}
package com.yz.io.bio;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/**
* Created by yz on 2018/2/5.
*/
public class BioClient {
public static void main(String[] args) throws IOException {
Socket client = new Socket("localhost",8080);
/**
* 获取一个输出流
*/
OutputStream os = client.getOutputStream();
os.write("报个到".getBytes());
os.close();
client.close();
}
}
NIO: 非阻塞IO selector+非阻塞 同步 常用框架: Mina2.0 Netty5.0
package com.yz.io.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.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Created by yz on 2018/2/5.
*/
public class NioServer {
int port = 8080;
ServerSocketChannel server;
Selector selector;
//缓冲区
ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
Map<SelectionKey,String> sessionMsg = new HashMap<>();
public NioServer(int port) throws IOException {
/**
* 把高速公路修起来
*/
server = ServerSocketChannel.open();
/**
* 关卡也打开了,可以多路复用了
*/
server.socket().bind(new InetSocketAddress(this.port));
/**
* 默认是阻塞的,手动设置为非阻塞,它才是非阻塞
*/
server.configureBlocking(false);
/**
* 管家开始营业
*/
selector = Selector.open();
/**
* 告诉管家,Boss已经准备就绪,等会有客人来了,要通知我一下
*/
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("nio服务已经启动,监听端口是:"+this.port);
}
public void listener() throws IOException {
/**
* 轮询
*/
while (true){
//判断一下,当前有没有客户来注册,有没有排队的,有没有取号的
int i = selector.select();
if(i == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()){
//来一个处理一个
process(iterator.next());
//处理完之后打发走
iterator.remove();
}
}
}
/**
* 处理数据的方法
*/
private void process(SelectionKey key) throws IOException {
//首先要判断客户有没有跟我们boss建立好连接
if(key.isAcceptable()){
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector,SelectionKey.OP_READ);
}else if(key.isReadable()){
receiveBuffer.clear();
//判断是否可以读数据了
SocketChannel client = (SocketChannel) key.channel();
int len = client.read(receiveBuffer);
if(len > 0 ){
String msg = new String(receiveBuffer.array(),0,len);
sessionMsg.put(key,msg);
System.out.println("获取客户端发送来的消息:"+msg);
}
//读完之后告诉管家可以写了
client.register(selector,SelectionKey.OP_WRITE);
}else if(key.isWritable()){
//判断是否可以写数据了
if(!sessionMsg.containsKey(key)){
return ;
}
SocketChannel client = (SocketChannel) key.channel();
sendBuffer.clear();
sendBuffer.put(new String(sessionMsg.get(key)+",您好,您的请求已处理完成.").getBytes());
sendBuffer.flip();//切换读写模式
client.write(sendBuffer);
client.register(selector,SelectionKey.OP_READ);
}
}
public static void main(String[] args) throws IOException {
new NioServer(8080).listener();
}
}
package com.yz.io.nio;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
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;
/**
* Created by yz on 2018/2/5.
*/
public class NioClient {
SocketChannel client ;
InetSocketAddress serverAddress = new InetSocketAddress("localhost",8080);
Selector selector;
//缓冲区
ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
public NioClient() throws IOException {
//先开路
client = SocketChannel.open();
client.configureBlocking(false);
client.connect(serverAddress);
selector = Selector.open();
client.register(selector, SelectionKey.OP_CONNECT);
}
public void session() throws IOException {
//先要判断是否已经建立连接
if(client.isConnectionPending()){
client.finishConnect();
System.out.println("请在控制台登记姓名");
//告诉管家可以写东西了
client.register(selector,SelectionKey.OP_WRITE);
}
Scanner scan = new Scanner(System.in);
while (scan.hasNext()){
String name = scan.nextLine();
if("".equals(name)){
continue;
}
process(name);
}
}
public void process(String name) throws IOException {
boolean unFinish = true;
while (unFinish){
int i = selector.select();
if(i == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isWritable()){
sendBuffer.clear();
sendBuffer.put(name.getBytes());
sendBuffer.flip();
client.write(sendBuffer);
client.register(selector,SelectionKey.OP_READ);
}else if(key.isReadable()){
receiveBuffer.clear();
int len = client.read(receiveBuffer);
if(len > 0){
receiveBuffer.flip();
System.out.println("获取到服务端反馈的消息:"+new String(receiveBuffer.array(),0,len));
client.register(selector,SelectionKey.OP_WRITE);
unFinish = false;
}
}
}
}
}
public static void main(String[] args) throws IOException {
new NioClient().session();
}
}
AIO: 异步非阻塞IO
package com.yz.io.aio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
/**
* Created by yz on 2018/2/5.
*/
public class AioServer {
AsynchronousServerSocketChannel server;
ByteBuffer receviceBuff = ByteBuffer.allocate(1024);
int port = 8080;
public AioServer(int port) throws IOException {
this.port = port;
//要想富,先修路
server = AsynchronousServerSocketChannel.open();
//打开关卡
server.bind(new InetSocketAddress("localhost",this.port));
}
public void listener(){
new Thread(){
public void run(){
System.out.println("Aio服务已启动,监听端口:"+port);
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
//成功后的回调
@Override
public void completed(AsynchronousSocketChannel client, Void attachment) {
server.accept(null,this);
process(client);
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("异步IO失败");
}
private void process(AsynchronousSocketChannel client){
try {
receviceBuff.clear();
int len = client.read(receviceBuff).get();
receviceBuff.flip();
System.out.println("已接到客户端发来的消息:"+new String(receviceBuff.array(),0,len));
} catch (Exception e) {
e.printStackTrace();
}
}
});
while (true){}
}
}.run();
}
public static void main(String[] args) throws IOException, InterruptedException {
new AioServer(8080).listener();
Thread.sleep(20000);
}
}
package com.yz.io.aio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.Scanner;
import java.util.concurrent.Future;
/**
* Created by yz on 2018/2/5.
*/
public class AioClient {
AsynchronousSocketChannel client;
InetSocketAddress serverAddress = new InetSocketAddress("localhost",8080);
ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
public AioClient() throws IOException {
client = AsynchronousSocketChannel.open();
Future<?> f = client.connect(serverAddress);
System.out.println("客户端已启动");
}
public void send(String content){
sendBuffer.clear();
sendBuffer.put(content.getBytes());
sendBuffer.flip();
client.write(sendBuffer);
}
public static void main(String[] args) throws IOException {
AioClient client = new AioClient();
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String content = sc.nextLine();
client.send(content);
}
}
}
七.总结
BIO: 阻塞IO 同步
NIO: 非阻塞IO 同步 selector+非阻塞 常用框架: Mina2.0 Netty5.0
AIO: 异步非阻塞IO