先了解一下阻塞,非阻塞,同步,异步的概念。
BIO阻塞
都说传统的BIO网络编程是阻塞的,那么到底阻塞在哪里?
阻塞与非阻塞是相对于请求资源来说的,例如serverSocket的accpet,如果没有client进行连接,就无法获得资源,从而阻塞,类似的read也一样,client不写入数据你就没有资源read。
NIO三大核心
buffer
buffer实质上是个字节缓冲区,类似于字节数组。要传送的数据都要经过buffer
channel
channel是双向的并且是非阻塞的,不同于inputStream读不到资源就阻塞。
通过以上两个核心似乎可以解决非阻塞的网络编程了,思路无非就是:
轮询获取资源,获取不到就返回,获取到了就处理,这样子一个线程也可以服务于多个客户端,相较于BIO的一个线程服务一个连接效率已经得到提升,但这样子反复轮询也就造成了CPU浪费,低效,从而有了下面的selector
selector
selector通过监听channel的触发事件,从而线程来进行相应处理,类似于观察者模式。通过轮询selector而不再是轮询channel。
并且轮询的时候,下面这个方法是阻塞的,也就是说当你感兴趣的事件触发时,这个方法才返回,这样就不是一直无效的轮询了。
selector.select()
NIO对比BIO
正确的NIO网络编程开发姿势
单线程版
package 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;
/* *
* Created by Ay on 2018/11/26
*/
public class NIOServer {
public static void main(String[] args) throws IOException {
System.out.println("服务端已经启动...");
//1、创建通道
ServerSocketChannel sChannel = ServerSocketChannel.open();
//2、切换异步非阻塞
sChannel.configureBlocking(false);
//3、绑定连接
sChannel.bind(new InetSocketAddress(8080));
//4、获取选择器
Selector selector =Selector.open();
//5、将通道注册到选择器,并且"指定监听接受事件"。
sChannel.register(selector, SelectionKey.OP_ACCEPT);
//6、轮训式 获取选择"已经准备就绪"的事件
while (selector.select()>0){
// 7.获取当前选择器所有注册的"选择键(已经就绪的监听事件)"
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
//8.获取准备就绪的事件
SelectionKey sk = iterator.next();
//9.判断具体是什么事件准备就绪
if(sk.isAcceptable()){
// 10.若"接受就绪",获取客户端连接
SocketChannel socketChannel = sChannel.accept();
//11.设置阻塞模式
socketChannel.configureBlocking(false);
//12.将该通道注册到服务器上
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("收到新连接:"+socketChannel.getRemoteAddress());
}else if(sk.isReadable()){
// 13.获取当前选择器"就绪" 状态的通道
SocketChannel socketChannel=(SocketChannel)sk.channel();
// 14.读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len=0;
while ((len=socketChannel.read(buffer))>0){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
buffer.clear();
}
System.out.println("来自:"+socketChannel.getRemoteAddress());
//15. 响应客户端数据
String msg = "hello";
buffer.put(msg.getBytes());
socketChannel.write(buffer);
}
}
iterator.remove();
}
sChannel.close();
}
}
但要想提升效率。还是要结合多线程技术,NIO只是将单线程的效率提高了。