多路复用
单线程可以配合 Selector 完成对多个 Channel 可读写事件的监控,这称之为多路复用
多路复用仅针对网络 IO,普通文件 IO 无法利用多路复用
如果不用 Selector 的非阻塞模式,线程大部分时间都在做无用功,而 Selector 能够保证
有可连接事件时才去连接
有可读事件才去读取
有可写事件才去写入
限于网络传输能力,Channel 未必时时可写,一旦 Channel 可写,会触发 Selector 的可写事件
事件的四种类型:
accept:有连接请求触发
connet:是客户端连接建立后触发
read:可读事件
write:可写事件
服务端代码:
package com.ityin.netty.c1;
import lombok.extern.slf4j.Slf4j;
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.ArrayList;
import java.util.Iterator;
import java.util.List;
import static com.ityin.netty.c1.ByteBufferUtil.debugRead;
@Slf4j
public class Server {
public static void main(String[] args) throws IOException {
//1.创建selector,管理多个channel
Selector selector=Selector.open();
//使用nio来理解阻塞模式,单线程
ByteBuffer buffer =ByteBuffer.allocate(16);
ServerSocketChannel ssc=ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channe之间的联系(注册)
//SelectionKey是事件发生后,通过它来得知哪个channel的事件
SelectionKey ssckey=ssc.register(selector,0,null);//0表示不关注任何事件
//key只关注accept事件
ssckey.interestOps(SelectionKey.OP_ACCEPT);
log.debug("register key:{}",ssckey);
ssc.bind(new InetSocketAddress(8080));
while(true){
//3.select方法,没有事件就阻塞,有事件线程恢复运行
selector.select();
//4.处理事件,selectedKeys内部包含了所有发生的事件
Iterator<SelectionKey> iter=selector.selectedKeys().iterator();
while(iter.hasNext()){
SelectionKey key=iter.next();
log.debug("register key:{}",key);
ServerSocketChannel channel=(ServerSocketChannel) key.channel();
channel.accept();
log.debug("{}",ssc);
}
}
}
}
客户端触发后:
selector在事件未处理时不会阻塞,但是可以使用
key.cancel();
取消