NIO Selector Channel选择器深入理解

Selector和非阻塞网络编程
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

​	选择器提供选择执行已经就绪的任务的能力.从底层来看,Selector提供了询问通道是否已经准备好执行每个I/O操作的能力。Selector 允许单线程处理多个Channel。仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道,这样会大量的减少线程之间上下文切换的开销。

**选择器(Selector):**Selector选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态。

**可选择通道(SelectableChannel):**SelectableChannel这个抽象类提供了实现通道的可选择性所需要的公共方法。它是所有支持就绪检查的通道类的父类。因为FileChannel类没有继承SelectableChannel因此是不是可选通道,而所有socket通道都是可选择的,SocketChannel和ServerSocketChannel是SelectableChannel的子类。

**选择键(SelectionKey):**选择键封装了特定的通道与特定的选择器的注册关系。选择键对象被SelectableChannel.register()返回并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数的形式进行编码),选择键支持四种操作类型:

  • Connect 连接
  • Accept 接受请求
  • Read 读
  • Write 写
NIO实现服务器(单线程接受多个客户端) (如果理解不了,看最下边自己画的图)

适合短暂客户,节省了创建销毁线程的空间和性能

流程图:

在这里插入图片描述

//服务器:

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;
import java.util.Set;

//单线程接受多个客户端请求
public class Demo1 {
public static void main(String[]args) throws Exception{
        ServerSocketChannel ssc=ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(65524));
        System.out.println("服务器已开启");
        //设置为非阻塞   非阻塞解释在9.1
        ssc.configureBlocking(false);
        //创建轮询器selector
        Selector selector= Selector.open();
        //注册轮询器,更新通道状态为就绪
        ssc.register(selector, SelectionKey.OP_ACCEPT);//注册一个服务器通道,用来接收客户端的连接请求
        
        while(selector.select()>0){  //首先调用select()方法,该方法会阻塞,直到至少有一个事件发生,然后再使用selectedKeys()方法获取发生事件的selectionKey,再使用迭代器进行循环。
              
                Set<SelectionKey> selectionKeys = selector.selectedKeys();  //取出所有事件的selectionkey放入集合,用迭代器循环
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()){
                        SelectionKey selectionKey=iterator.next();
                        if(selectionKey.isAcceptable()){ //如果满足Acceptable条件,则必定是一个ServerSocketChannel通道中的事件
                            
                               // ServerSocketChannel sscTemp = (ServerSocketChannel) selectionKey.channel();  //可以不写,如果服务器注册了多条ServerSocketChannel,需要写,用来找出对应的服务器通道。本题只注册了一个服务器通道ServerSocketChannel来接收客户端连接信号,所以不需要写
                            
                                SocketChannel socketChannel=sscTemp.accept();  //ServerSocketChannel服务器通道中为OP_ACCEPT的操作,所以接受为一个对应的Socketchannel(客户端通道)
                            
                                socketChannel.configureBlocking(false);  //设置为非阻塞
                                socketChannel.register(selector,SelectionKey.OP_READ); //把此该socketChannel注册到Selector上,成为一个客户端通道,键为OP_read,说明此客户端已经与服务器建立连接,该通道对应的客户端以后发来的信息,会进行读取操作。
                        }else if(selectionKey.isReadable()){  //如果满足Readable条件,则必定是一个之前已经建立过连接的SocketChannel
                                      //接收数据
                                SocketChannel sc=(SocketChannel) selectionKey.channel();
//找出此selectionKey对应的SocketChannel,读取此socketChannel(客户端)中的信息
                                ByteBuffer buf=ByteBuffer.allocate(1024*4);
                                int len;
                                while((len=sc.read(buf))>0){  //此时的read不会阻塞
                                       buf.flip();
                                       String str=new String(buf.array(),0,buf.limit());
                                       System.out.println(sc.getRemoteAddress()+":"+str);
                                       buf.clear();
                                }
                                if(len==-1){
                                        sc.close();
                                }
                        }
                        iterator.remove();
                }
        }
        }
}






//客户端(客户端一般不需要设置不阻塞,此代码仅仅为演示不阻塞)  
package NIO;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class Demo1Client {
    public static void main(String[] args) throws Exception{
        InetSocketAddress is=new InetSocketAddress("10.9.21.178",65524);
        SocketChannel sc=SocketChannel.open(is);

        sc.configureBlocking(false);

        Scanner input=new Scanner(System.in);  
        ByteBuffer bf=ByteBuffer.allocate(1024*4);
        while(true){
            String str=input.next();
            bf.put(str.getBytes());
            bf.flip();
            sc.write(bf);
            bf.clear();
            if(str.equals("886")){
                break;
            }
        }
        sc.close();
    }
}

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值