BIO:(blocked input and output)堵塞式IO,主要是应用进程采用独占式的方式对一些资源(硬件,网络资源)进行独占式访问。
在阻塞式模式模式下若从网络读取不到数据是,IO进程就会阻塞在那边,直到数据发送过来,或者网络异常.比如:我们直到对端发来100字节的数据,但是我们只是收到90字节数据,那么当前的线程就会阻塞在这边,等待10个字节的数据过来,才能将资源(设备)放开。或者直到超时,异常。
NIO:在非阻塞模式下,若从网络流中读取不到指定大小的数据量,非阻塞IO就立即通行。比如,已知后面会有100个字节的数据发过来,但是我现在只收到90个字节,那么当前线程就读取这90个字节的数据,读完后就立即返回,等另外两个字节再来的时候再去读取
AIO:AIO是NIO的升级版,还是基于非阻塞式的NIO,不过在处理网络事件的时候,比NIO更简单,易用。
BIO处理网络:
服务端:bind到本地的某个网络端口与网卡上---->accept()---->接收网络过来的链接---》是否需要创建线程进行异步处理。
伪代码如下:
ServerSocket server = null;
try {
server = new ServerSocket();
server.bind(new InetSocketAddress(9999));
while(true){
Socket client = server.accept();
Thread t = new Thread(new SocketHandleThread(client));
t.setName("socketClientThread");
t.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public class SocketHandleThread implements Runnable{
private Socket clientSocket = null;
private InputStream input = null;
public SocketHandleThread(Socket clientSocket){
this.clientSocket = clientSocket;
try {
this.input = this.clientSocket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
//你自己的处理
}
}
NIO处理模型:
NIO通过通道技术,客户进程与内核进程之间建立通道,当内核接收到数据时生成一个事件,并将事件放入到事件队列中,用户将轮训事件队列得到事件类型并依据事件的类型获取对应的数据。用户进程不会堵塞和独占设备资源,设备资源完全通过内核进行管理和交互,用户进程与内核通过通道机制进行交互。类似于操作系统中的DMA管理。流程如下:
建立服务端通道——》服务端通道绑定到对应的设备和端口上——》创建一个selector 选择器——》并将通道注册到轮训器中。循环轮训对应的。伪代码如下:
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress("127.0.0.1", 10000));
serverChannel.configureBlocking(false);
// 创建一个选择器并将serverChannel注册到它上面
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set readySelectionKey = selector
.selectedKeys();
Iterator it = readySelectionKey.iterator();
while (it.hasNext()) {
SelectionKey selectionKey = it.next();
// 判断是哪个事件
if (selectionKey.isAcceptable()) {// 客户请求连接
+ " - 接受请求事件");
// 获取通道 接受连接,
// 设置非阻塞模式(必须),同时需要注册 读写数据的事件,这样有消息触发时才能捕获
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey
.channel();
SocketChannel client = serverSocketChannel
.accept();
client.configureBlocking(false);
client.register(
selector,
SelectionKey.OP_READ
SelectionKey.OP_WRITE);
System.out
.println(selectionKey.attachment() + " - 已连接");
}
if (selectionKey.isReadable()) {// 读数据
+ " - 读数据事件");
SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
ByteBuffer receiveBuf = ByteBuffer.allocate(1024);
clientChannel.read(receiveBuf);
}
if (selectionKey.isWritable()) {// 写数据
+ " - 写数据事件");
SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
ByteBuffer sendBuf = ByteBuffer.allocate(11);
String sendText = "hello word";
sendBuf.put(sendText.getBytes());
sendBuf.flip(); //写完数据后调用此方法
clientChannel.write(sendBuf);
}
if (selectionKey.isConnectable()) {
+ " - 连接事件");
}
// 必须removed 否则会继续存在,下一次循环还会进来,
// 注意removed 的位置,针对一个.next() remove一次
it.remove();
}
}
AIO:aio是nio的升级版本,jdk1.7以后支持,解决了nio需要程序自己轮训的疼点,将以前的pull的模式,变成了push模式,将尽可能多的操作交给内核去做。采用事件监听的模式实现内核到用户的数据直接交换。
AIO 流程如下:
获取同步nio的serverSocket(处理链接的线程池设置),设置tcp的一些参数,如接收buffer的大小等,并将其绑定到对应端口上——》设置处理事件的监听器,开始监听链接。
伪代码如下:
AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(4));
AsynchronousServerSocketChannel sc = AsynchronousServerSocketChannel.open(group);
// sc
// .setOption(
// StandardSocketOption.SO_REUSEADDR,true);
// sc
// .setOption(
// StandardSocketOption.SO_RCVBUF,16*1024);
InetSocketAddress addree = new InetSocketAddress("127.0.0.1",10000);
sc.bind(addree);
CompletionHandlerimp handled = new CompletionHandlerimp();
sc.accept(null,handled);
BIO 是通过用户态堵塞IO的方式实现网络的通讯和读写,一般是是BIO+线程池(业务处理线程池的方式)由于需要堵塞IO不能将主机的网络性能发挥到最大,所以效率比较低。但是编程简单适合与一些,业务处理很长,网络开销的时间远远低于业务处理的时间(网络读写耗时不是主要矛盾可以忽略不计的场景),并发量要求比较低的场景。
NIO:由于不堵塞IO的长连接,连接一次以后,轮训数据,适合与业务处理很短,网络开销是主要矛盾的场景,并发量要求比较高的情况,但是编程稍微比较复杂。一般都是通过第三方框架来实现,如netty,mina等
AIO:AIO和NIO原理一致,不过处理数据的方式不一样,一个是推的模式,一个是主动拉的模式,相比NIO编程简单。但是AIO是需要jdk1.7以后的版本才可以使用。
BIO,NIO和AIO只有在操作系统底层支持时才能发挥显著特性。建议在linux上使用。
领取专属 10元无门槛券
私享最新 技术干货