多路复用
单线程可以配合 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();
 
取消










