NIO : 异步非阻塞IO
介绍一下我了解Dao的NIO的基础知识:
非阻塞IO
(1) Buffer (缓冲区)
(2) Channel 通道 负责数据传输
(3) selector 选择器
举一个生活中的例子:就是高铁 channel就是我们的铁路连接北京和上海 然后Buffer就是和谐号列车 selector就是我们的铁路调度系统 我们人就是数据 我们需要进入buffer 然后通过channel在selector 的调度下进行传输.
首先buffer 顾名思义 缓冲 他的作用可以想象成数据传输的媒介 我们可以将一个buffer 写到一个channel 也可以从channel读取一个buffer 一般实例化的时候都需要设定大小
获取的方法有两种: 1.通过seletionKey获取
2.通过ByteBuffer.allocate(size)
ByteBuffer:
就是一个缓冲区类型的的东西:
有两种模式一种是 写模式 一种是 读模式!
之间用.filp()方法进行切换 这个方法的作用就是操作position, limit指针的内容
.compact() 方法的作用是 将position 和 limit之间的东西 移到最开始 然后position指向
插入之后的位置
clear() limit = 最后 position = 0 这个没有什么好说的
实例化 ByteBuffer.allocate();
然后是channel 经常使用的channel 是 FileChannel , SocketChannel , ServeSocketChannel
这三个 他可以执行写操作和读操作 当然他的媒介是buffer
最后selector 中有两个属性很重要:
keys selectionKeys
key: 注册到selector 中的channel 的数目
selelctionKey; 需要执行的Channel
来个例子:
public class
Client {
public static void
main(String[] args)
throws
IOException {
ByteBuffer bb = ByteBuffer.
allocate
(
1024
);
SocketChannel socketChannel =
null
;
try
{
socketChannel = SocketChannel.
open
();
socketChannel.configureBlocking(
false
);
socketChannel.connect(
new
InetSocketAddress(
"127.0.0.1"
,
8882
));
if
(socketChannel.finishConnect()) {
String msg =
"hello"
;
bb.put(msg.getBytes());
bb.flip();
socketChannel.write(bb);
sleep
(
4000
);
//这个是必须的 如果不加 他就会直接执行完成 当然也可以通过 死循环来实现
bb.clear();
int
n = socketChannel.read(bb);
while
(n >
0
) {
bb.flip();
while
(bb.hasRemaining()) {
System.
out
.print((
char
) bb.get());
}
n = socketChannel.read(bb);
}
sleep
(
4000
);
//再测试一波写入
bb.clear();
bb.put(msg.getBytes());
bb.flip();
socketChannel.write(bb);
sleep
(
4000
);
//这个是必须的 如果不加 他就会直接执行完成 当然也可以通过 死循环来实现
bb.clear();
n = socketChannel.read(bb);
while
(n >
0
) {
bb.flip();
while
(bb.hasRemaining()) {
System.
out
.print((
char
) bb.get());
}
n = socketChannel.read(bb);
}
}
}
catch
(IOException e) {
e.printStackTrace();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
finally
{
if
(socketChannel !=
null
) {
socketChannel.close();
}
}
}
}
public class
Server {
public static void
main(String[] args) {
try
{
Selector selector = Selector.
open
();
ServerSocketChannel ssc = ServerSocketChannel.
open
();
ssc.configureBlocking(
false
);
ssc.socket().bind(
new
InetSocketAddress(
8882
));
ssc.register(selector, SelectionKey.
OP_ACCEPT
);
while
(
true
) {
sleep
(
1000
);
if
(selector.selectNow() ==
0
) {
System.
out
.println(
"=="
);
continue
;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while
(it.hasNext()) {
SelectionKey sk = it.next();
if
(sk.isAcceptable()) {
handleAccept
(sk);
System.
out
.println(
"accept"
);
}
else if
(sk.isReadable()) {
System.
out
.println(
"read"
);
handleReader
(sk);
}
else if
(sk.isWritable()) {
handleWrite
(sk);
System.
out
.println(
"write"
);
}
else if
(sk.isConnectable()) {
System.
out
.println(
"is connected!"
);
}
it.remove();
}
}
}
catch
(IOException e) {
e.printStackTrace();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
public static void
handleReader(SelectionKey sk) {
SocketChannel sc = (SocketChannel) sk.channel();
ByteBuffer bf = (ByteBuffer) sk.attachment();
try
{
long
n = sc.read(bf);
while
(n >
0
) {
bf.flip();
while
(bf.hasRemaining()) {
System.
out
.print((
char
) bf.get());
}
System.
out
.println();
bf.compact();
n = sc.read(bf);
}
sc.register(sk.selector(), SelectionKey.
OP_WRITE
, ByteBuffer.
allocate
(
1024
));
}
catch
(IOException e) {
e.printStackTrace();
}
}
public static void
handleAccept(SelectionKey sk) {
ServerSocketChannel ssc = (ServerSocketChannel) sk.channel();
try
{
SocketChannel sc = ssc.accept();
sc.configureBlocking(
false
);
sc.register(sk.selector(), SelectionKey.
OP_READ
, ByteBuffer.
allocate
(
1024
));
}
catch
(IOException e) {
e.printStackTrace();
}
}
public static void
handleWrite(SelectionKey sk) {
String info =
"response:"
;
SocketChannel sc = (SocketChannel) sk.channel();
ByteBuffer bb = ByteBuffer.
allocate
(
1024
);
bb.put(info.getBytes());
bb.flip();
try
{
while
(bb.hasRemaining()) {
System.
out
.print(
"response:"
);
sc.write(bb);
}
System.
out
.println();
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
try
{
sc.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
}
BIO: 阻塞IO 面向流 一个连接对应一个线程
伪异步IO: 这个是为了解决上头BIO(也就是传统的CS模型的弊端, 一个连接一个线程的弊端就是系统的开销大) 而伪异步IO 采用线程池的方式代替为每一个连接创建线程, 而是从线程池中取空闲的线程. 这样的方式优化了传统的C/S模型 但是还是不能解决高并发条件下的线程大量等待.
NIO: 也就是上面讲的 非阻塞IO, 可以解决高并发条件下线程等待的问题
AIO: 异步IO 这个是NIO的进化版本 这个相比较与NIO 他最大的特征就是事件驱动, 有两个函数completed 和 failed 通过监控事件的结果,成功/失败 来调用返回的结果, 以下加粗部分就是AIO的核心部分., 其实总结一下 就是套接字的每一个操作都伴随着事件. 如
accept(null,
new CompletionHandler<AsynchronousSocketChannel,Object>(){}), connect(), read(), write()等 都可以添加一个事件, 具体的可以看一下源码.
public class AIOEchoServer {
public final static int PORT = 8001;
public final static String IP = "127.0.0.1";
private AsynchronousServerSocketChannel server = null;
public AIOEchoServer(){
try {
//同样是利用工厂方法产生一个通道,异步通道 AsynchronousServerSocketChannel
server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(IP,PORT));
} catch (IOException e) {
e.printStackTrace();
}
}
//使用这个通道(server)来进行客户端的接收和处理
public void start(){
System.out.println("Server listen on "+PORT);
//注册事件和事件完成后的处理器,这个CompletionHandler就是事件完成后的处理器
server.accept(null,new CompletionHandler<AsynchronousSocketChannel,Object>(){
final ByteBuffer buffer = ByteBuffer.allocate(1024);
@Override
public void completed(AsynchronousSocketChannel result,Object attachment) {
System.out.println(Thread.currentThread().getName());
Future<Integer> writeResult = null;
try{
buffer.clear();
result.read(buffer).get(100,TimeUnit.SECONDS);
System.out.println("In server: "+ new String(buffer.array()));
//将数据写回客户端
buffer.flip();
writeResult = result.write(buffer);
}catch(InterruptedException | ExecutionException | TimeoutException e){
e.printStackTrace();
}finally{
server.accept(null,this);
try {
writeResult.get();
result.close();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("failed:"+exc);
}
});
}
public static void main(String[] args) {
new AIOEchoServer().start();
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}