NIO相关概念之Selector

选择器(selector):

选择器管理者一个被注册的通道的集合信息和它们的就绪状态.通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态,当这么做的时候,可以选择被激发的线程挂起,直到有就绪的通道

可选择通道(SelectableChannel)

这个抽象类提供了实现通道的可选择性所需要的公共方法。它是所有支持就绪检查的通道类的父类。FileChannel对象不是可选择的,因为它们没有继承SelectableChannel。所有socket通道都是可选择的,包括从管道(Pipe)对象的中获得的通道。SelectableChannel可以被注册到Selector对象上,同时可以指定对那个选择器而言,那种操作是感兴趣的。一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。

选择键(SelectionKey)
选择键封装了特定的通道与特定的选择器的注册关系。选择键对象被SelectableChannel.register( ) 返回并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数的形式进行编码),指示了该注册关系所关心的通道操作,以及通道已经准备好的操作。

就绪选择的相关类之间的关系如下:

调用可选择通道的register( )方法会将它注册到一个选择器上。如果您试图注册一个处于阻塞状态的通道,register( )将抛出未检查的IllegalBlockingModeException异常。

此外,通道一旦被注册,就不能回到阻塞状态。试图这么做的话,将在调用configureBlocking( )方法时将抛出IllegalBlockingModeException异常。并且,理所当然地,试图注册一个已经关闭的SelectableChannel实例的话,也将抛出ClosedChannelException异常,就像方法原型指示的那样

尽管SelectableChannel类上定义了register( )方法,还是应该将通道注册到选择器上,而不是另一种方式。选择器维护了一个需要监控的通道的集合。一个给定的通道可以被注册到多于一个的选择器上,而且不需要知道它被注册了那个Selector对象上。将register( )放在SelectableChannel上而不是Selector上,这种做法看起来有点随意。它将返回一个封装了两个对象的关系的选择键对象。重要的是要记住选择器对象控制了被注册到它之上的通道的选择过程。

选择器才是提供管理功能的对象,而不是可选择通道对象。选择器对象对注册到它之上的通道执行就绪选择,并管理选择键。

对于键的interest(感兴趣的操作)集合和ready(已经准备好的操作)集合的解释是和特定的通道相关的。每个通道的实现,将定义它自己的选择键类。在register( )方法中构造它并将它传递给所提供的选择器对象。

以下是一个基于selector来实现的一个Http服务器端:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;

/**
 * Created with Intellij IDEA
 *
 * @author: jiaoyiping
 * Mail: [email protected]
 * Date: 2018/07/22
 * Time: 11:18
 * To change this template use File | Settings | Editor | File and Code Templates
 */

public class HttpServer {
    private static final Logger logger = LoggerFactory.getLogger(HttpServer.class);

    private static final int DEFAULT_TIME_OUT = 3000;
    private static final int DEFAULT_HTTP_PORT = 8080;

    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(DEFAULT_HTTP_PORT));

        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            if (selector.select(DEFAULT_TIME_OUT) == 0) {
                logger.info("没有接收到客户端的请求,继续等待");
                continue;
            }
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            Handler handler = new Handler();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();

                if (key.isAcceptable()) {
                    handler.handleAccept(key);
                }

                if (key.isReadable()) {
                    handler.handleRead(key);
                }

                keyIterator.remove();
            }
        }
    }

    private static class Handler {
        private static final int DEFAULT_BUFFER_SIZE = 4096;
        private static final String DEFAULT_HTTP_RESPONSE_HEADER = "HTTP/1.1 200 OK\nContent-Type: text/html;charset=UTF-8\n\r\n";
        private static final String DEFAULT_CHAR_SET = "UTF-8";

        public void handleAccept(SelectionKey key) throws IOException {
            SocketChannel socketChannel = ((ServerSocketChannel) key.channel()).accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(DEFAULT_BUFFER_SIZE));
        }

        public void handleRead(SelectionKey key) throws IOException {
            SocketChannel socketChannel = (SocketChannel) key.channel();
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            buffer.clear();

            if (socketChannel.read(buffer) == -1) {
                logger.debug("没有从请求中读取到内容");
                socketChannel.close();
                return;
            }

            buffer.flip();
            String receivedString = Charset.forName(DEFAULT_CHAR_SET).newDecoder().decode(buffer).toString();
            logger.info("收到了客户端的请求:\n{}", receivedString);

            //返回数据给客户端
            String message = "<html><body>黄河远上白云间</body></html>";
            ByteBuffer sendData = ByteBuffer.wrap((DEFAULT_HTTP_RESPONSE_HEADER + message).getBytes());
            socketChannel.write(sendData);
            socketChannel.close();

        }
    }

}

原文地址:https://www.cnblogs.com/jiaoyiping/p/9220367.html

时间: 2024-10-18 00:31:37

NIO相关概念之Selector的相关文章

epoll 浅析以及 nio 中的 Selector

首先介绍下epoll的基本原理,网上有很多版本,这里选择一个个人觉得相对清晰的讲解(详情见reference): 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流. 之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据:通过write,我们可以往流写入数据.现在假定一个情形,我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读如数据,但是服务器还没

epoll浅析以及nio中的Selector

出处: https://my.oschina.net/hosee/blog/730598 首先介绍下epoll的基本原理,网上有很多版本,这里选择一个个人觉得相对清晰的讲解(详情见reference): 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流. 之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据:通过write,我们可以往流写入数据.现在假定一个情形,我们需要从流中

【总结】两种 NIO 实现:Selector 与 Epoll

时间2012-11-17 08:38:42 开源中国新闻原文  http://my.oschina.net/ielts0909/blog/89849 我想用这个话题小结下最近这一阶段的各种测试和开发.其实文章的内容主要还是想总结一下 NIO Socket ,以及两种不同操作系统实现 NIO 的方式, selector 和epoll . 问题应该从服务器端开始说起.我们都写过net包下的socket,用socket的accept方法来等待客户端的请求,请求来了则处理,没有则一直等待,然后反复循环.

Java NIO系列(四) - Selector

前言 Selector 是 Java NIO 中的一个组件,用于检查一个或多个通道 Channel 的状态是否处于可读.可写状态.如此可以实现单线程管理多个通道,也就是可以管理多个网络连接. 为什么使用Selector? 用单线程处理多个 Channel 的好处是我需要更少的线程来处理 Channel .实际上,你甚至可以用一个线程来处理所有的Channel.从操作系统的角度来看,切换线程的开销是比较昂贵的,并且每个线程都需要占用系统资源,因此暂用线程越少越好. 简而言之,通过 Selector

7. 彤哥说netty系列之Java NIO核心组件之Selector

<p align="right">--日拱一卒,不期而至!</p> 你好,我是彤哥,本篇是netty系列的第七篇. 简介 上一章我们一起学习了Java NIO的核心组件Buffer,它通常跟Channel一起使用,但是它们在网络IO中又该如何使用呢,今天我们将一起学习另一个NIO核心组件--Selector,没有它可以说就干不起来网络IO. 概念 我们先来看两段Selector的注释,见类java.nio.channels.Selector. 注释I A mul

NIO相关概念之Buffer

Buffer的定义: 概念上,缓冲区是包在一个对象内的基本数据元素数组.Buffer类相比一个简单数组的优点是它将关于数据的数据内容和信息包含在一个单一的对象中.Buffer类以及它专有的子类定义了一个用于处理数据缓冲区的API. Buffer类的有以下的几个最关键的属性 容量(Capacity) 缓冲区能够容纳的数据元素的最大数量.这一容量在缓冲区创建时被设定,并且永远不能被改变. 上界(Limit) 缓冲区的第一个不能被读或写的元素.或者说,缓冲区中现存元素的计数. 位置(Position)

Java NIO类库Selector机制解析--转

一.  前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: Buffer:包含数据且用于读写的线形表结构.其中还提供了一个特殊类用于内存映射文件的I/O操作. Charset:它提供Unicode字符串影射到字节序列以及逆映射的操作. Channels:包含socket,file和pipe三种管道,都是全双工的通道. Selector:多个异步I/O操作集

NIO原理剖析与Netty初步----浅谈高性能服务器开发(一)

除特别注明外,本站所有文章均为原创,转载请注明地址 在博主不长的工作经历中,NIO用的并不多,由于使用原生的Java NIO编程的复杂性,大多数时候我们会选择Netty,mina等开源框架,但理解NIO的原理就不重要了吗?恰恰相反,理解NIO底层机制是理解这一切的基础,由此我总结一下当初学习NIO时的笔记,以便后续复习. 以下是我理解的Java原生NIO开发大致流程: 上图大致描述的是服务端的NIO操作. 第一步,绑定一个服务的端口 这与传统阻塞IO中的ServerSocket类似,没什么好说的

基于Java NIO的多人在线聊天工具源码实现(登录,单聊,群聊)

近来在学习Java NIO网络开发知识,写了一个基于Java NIO的多人在线聊天工具练练手.源码公开在Coding上: https://coding.net/u/hust_wsh/p/MyChat/git ,开发环境是Ubuntu14.04+Eclipse Mars+JDK1.8. 要想编写一个基于Java NIO的多人在线聊天工具,我总结需要以下几方面的地址:客户端服务器模型,Java NIO中的Selector,SocketChannel,ByteBuffer,Collections以及序