Java NIO学习笔记-Selector

Selector选择器是什么

Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。

Java NIO的选择器部分,实际上有三个重要的类:

  • Selector 选择器,完成主要的选择功能,维护着注册的一组SelectionKey。
  • SelectableChannel 记录了一组注册的SelectionKey[]。
  • SelectionKey 关联了一个Channel和一个Selector,描述一个Selector和SelectableChannel的关系。并保存有通道所关心的操作。

Selector

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

  • select() //阻塞等待,直至一个channel准备好或调用wakeup()才返回
  • select(long timeout) //如上,返回条件多了个超时时间
  • selectNow() //非阻塞,会立刻返回,没有时返回值=0
  • wakeup() //使得Selector返回

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)就可以配置这些值

SelectionKey

  • SelectionKey.attach(Object ob) //添加附件,另一种方式SelectableChannel.register(Selector sel, int ops, Object att)
  • SelectionKey.attachment() //获取附件
  • SelectionKey.cancel(),永久的注销键,加入Selector的注销集中,在下次select()时被移除

完整实例

@Test
public void client() throws IOException {
	//获取通道
	SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 7777));
	//切换成非阻塞模式
	socketChannel.configureBlocking(false);
	//分配缓冲区
	ByteBuffer buffer = ByteBuffer.allocate(1024);
	//发送数据给服务端
	Scanner scanner = new Scanner(System.in);
	while (scanner.hasNext()) {
		String str = scanner.next();
		buffer.put((LocalDateTime.now().toString().getBytes() + ":" + str).getBytes());
		buffer.flip();
		socketChannel.write(buffer);
		buffer.clear();
	}
	//关闭通道
	socketChannel.close();
}

@Test
public void server() throws IOException {
	//获取通道
	ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
	//切换非阻塞模式
	serverSocketChannel.configureBlocking(false);
	//绑定连接
	serverSocketChannel.bind(new InetSocketAddress(7777));
	//获取选择器
	Selector selector = Selector.open();
	//将通道注册到选择器
	//可以监听的事件类型
	//读 SelectionKey.OP_READ  1
	//写 SelectionKey.OP_WRITE 4
	//连接 SelectionKey.OP_CONNECT  8
	//接收SelectionKey.OP_ACCEPT 16
	serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
	//轮询获取选择器上已经准备就绪的事件
	while (selector.select() > 0) {
		//获取当前选择器中所有注册的选择键(已就绪的监听事件)
		Set<SelectionKey> selectedKeys = selector.selectedKeys();
		Iterator<SelectionKey> iterator = selectedKeys.iterator();
		while (iterator.hasNext()) {
			//获取准备就绪的事件
			SelectionKey selectionKey = (SelectionKey) iterator.next();
			//判断具体是什么事件准备就绪
			if (selectionKey.isAcceptable()) {
				//若接收就绪,就获取客户端的连接
				SocketChannel socketChannel = serverSocketChannel.accept();
				//切换非阻塞模式
				socketChannel.configureBlocking(false);
				//将该通道注册到选择器上
				socketChannel.register(selector, SelectionKey.OP_READ);
			} else if (selectionKey.isReadable()) {
				//若读就绪,获取当前选择器上读就绪状态的通道
				SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
				//读取数据
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				int length = 0;
				while ((length = socketChannel.read(buffer)) > 0) {
					buffer.flip();
					System.out.println(new String(buffer.array(), 0, length));
					buffer.clear();
				}
			}
			//取消选择键 SelectionKey
			iterator.remove();
		}
	}
}

参考:http://www.cnblogs.com/bronte/articles/1966550.html
http://ifeve.com/selectors/

代码托管:https://git.oschina.net/umgsai/java-nio

原文地址:https://www.cnblogs.com/umgsai/p/6684091.html