• NIO组件Selector调用实例


    *对于nio的非阻塞I/O操作,使用Selector获取哪些I/O准备就绪,注册的SelectionKey集合记录关联的Channel这些信息.SelectionKey记录Channel对buffer的操作方式.

    ---SelectableChannel,Selector,SelectionKey是nio中Channel操作的3个主要部件.

    ---对应关系,

    一个SelectableChannel,记录了一组注册的SelectionKey[]

    一个SelectionKey,关联了一个Channel和一个Selector.

    一个Selector,维护着注册的一组SelectionKey

    *使用示例

    ---注,这段代码来自"TCP/IP Sockets in Java",典型示例,我做了注解

    package chapter5;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.util.Iterator;
    
    public class TCPServerSelector {
    	private static final int BUFSIZE = 256;  //Buffer,EchoSelectorProtocol
    	private static final int TIMEOUT = 3000; //Selector.select(long timeout) 
    	
    	public static void main(String[] args) throws IOException {
    		if(args.length<1){
    			throw new IllegalArgumentException("Parameter(s): <Port> ...");
    		}
    		
    		Selector selector =Selector.open(); //创建Selector实例
    		for(String arg:args){
    			ServerSocketChannel listnChannel =ServerSocketChannel.open();
    			listnChannel.socket().bind(new InetSocketAddress(Integer.parseInt(arg)));
    			listnChannel.configureBlocking(false); //nonblocking
    			listnChannel.register(selector, SelectionKey.OP_ACCEPT); //Channel注册selector,并告知channel感兴趣的操作
    		}
    		
    		TCPProtocol protocol =new EchoSelectorProtocol(BUFSIZE);
    		while(true){ //循环
    			if(selector.select(TIMEOUT)==0){ //返回准备就绪I/O的SelectionKey数
    				System.out.println("."); //to do others
    				continue;
    			}
    			
    			Iterator<SelectionKey> keyIter =selector.selectedKeys().iterator();//获取已选的SelectionKey集合
    			while(keyIter.hasNext()){ //遍历key,根据key的类型做相应处理
    				SelectionKey key =keyIter.next();
    				if(key.isAcceptable())
    					protocol.handleAccept(key);
    				
    				if(key.isReadable())
    					protocol.handleRead(key);
    				
    				//SelectionKey is invalid if it is cancelled, its channel is closed, or its selector is closed.
    				if(key.isValid() && key.isWritable()) 
    					protocol.handleWrite(key);
    				
    				keyIter.remove(); //手动清空,因为Selector只会在已选的SelectionKey集中添加
    			}
    		}
    		
    	}
    }

    package chapter5;
    
    import java.io.IOException;
    import java.nio.channels.SelectionKey;
    
    public interface TCPProtocol {
    	void handleAccept(SelectionKey key) throws IOException;
    	void handleRead(SelectionKey key) throws IOException;
    	void handleWrite(SelectionKey key) throws IOException;
    }
    
    package chapter5;
    
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    
    public class EchoSelectorProtocol implements TCPProtocol {
    	
    	private int bufSize;
    	
    	public EchoSelectorProtocol(int bufSize){
    		this.bufSize =bufSize;
    	}
    	
    	@Override
    	public void handleAccept(SelectionKey key) throws IOException {
    		SocketChannel clntChan = ((ServerSocketChannel)key.channel()).accept();
    		clntChan.configureBlocking(false);
    		//对于已注册Selector的Channel,再次调用就更新注册信息,这里更新了SelectionKey的类型和附件,附件是需要操作的buffer.
    		clntChan.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocate(bufSize));
    	}
    
    	@Override
    	public void handleRead(SelectionKey key) throws IOException {
    		SocketChannel clntChan =(SocketChannel)key.channel();
    		ByteBuffer buf =(ByteBuffer) key.attachment(); //获取附件
    		long bytesRead = clntChan.read(buf);
    		if(bytesRead==-1) //channel已读到结束位置
    			clntChan.close();
    		else if(bytesRead > 0)
    			key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
    	}
    
    	@Override
    	public void handleWrite(SelectionKey key) throws IOException {
    		ByteBuffer buf =(ByteBuffer) key.attachment();
    		buf.flip();
    		SocketChannel clntChan =(SocketChannel) key.channel();
    		clntChan.write(buf);
    		if(!buf.hasRemaining()){
    			key.interestOps(SelectionKey.OP_READ); //设置Key的兴趣集
    		}
    		buf.compact();
    	}
    
    }

    看了代码,如何使用就清楚了吧:).

    下面再做些细节说明

    *Selector

    ---更新准备好的SelectionKey,移除isValid()为false的SelectionKey

    select() //阻塞等待,直至一个channel准备好或调用wakeup()才返回

    select(long timeout) //如上,返回条件多了个超时时间

    selectNow() //非阻塞,会立刻返回,没有时返回值=0

    wakeup() //使得Selector返回

    注意,select()会在上次已选择的键集中添加这次的可用键,故在2次select之间,手动移除已处理的SelectionKey.

    *SelectionKey

    ---兴趣操作集,通过它就可以知道channel可以去做哪些事了.有4种类型,如下:

    public static final int OP_READ = 1 << 0;

    public static final int OP_ACCEPT = 1 << 4;

    public static final int OP_WRITE = 1 << 2;

    public static final int OP_CONNECT = 1 << 3;
    通过SelectionKey.interestOps(int ops)就可以配置这些值

    ---附件,主要作用是为channel处理提供辅助信息,如上面示例中att为ByteBuffer

    SelectionKey.attach(Object ob) //添加附件,另一种方式SelectableChannel.register(Selector sel, int ops, Object att)

    SelectionKey.attachment()  //获取附件

    ---SelectionKey.cancel(),永久的注销键,加入Selector的注销集中,在下次select()时被移除

  • 相关阅读:
    Ignoring HTTPS certificates
    利用Httponly提升web应用程序安全性
    HttpUrlConnection java.net.SocketException: Software caused connection abort: recv failed
    DISPOSE_ON_CLOSE 和 EXIT_ON_CLOSE 的区别
    Swing多线程
    攒机知识积累
    数组最大子数组和
    fork()详解
    理解Socket编程【转载】
    STM32F407_LED代码
  • 原文地址:https://www.cnblogs.com/marcotan/p/4256937.html
Copyright © 2020-2023  润新知