美文网首页
辛星2018年nio教程第八篇:初识Selector

辛星2018年nio教程第八篇:初识Selector

作者: 辛星0913 | 来源:发表于2018-04-08 02:23 被阅读0次

先打个小广告,关注辛星教程,我的微信号xinxing0913,该项目源码所在的github地址: https://github.com/xinxing0913/xinxing-nio-guide

在传统的服务器模型中,一般都是用一个线程去处理一个连接,等这个连接处理完毕,这个线程就归还到线程池中,从而保证有条不紊的执行。

但是当连接数量非常多的时候,我们的线程数量就会非常多,这在连接数量非常多的情况下会有不小的挑战,为了解决这个问题,nio引入了selector这个概念。

selector即选择器,它表示检查一个或者多个nio的channel的状态是否处于可读可写的状态,它可以用于实现单线程管理多个channel,从而管理多个网络连接。

我们的一般使用方式就变成了多个连接对应一个selector,然后这个selector属于一个线程,这样就完成了一个线程内处理多个连接。

我们可以使用如下的方式来创建一个selector:

Selector selector = Selector.open();

然后我们把非阻塞的channel注册到selector上,注意这里的channel必须是非阻塞的:

channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

对于register的第二个参数,它表示关注集合,也就是我们关注的channel的状态,它的中可以有四个:
SelectionKey.OP_CONNECT表示连接状态
SelectionKey.OP_ACCEPT表示接受状态
SelectionKey.OP_READ表示读取数据
SelectionKey.OP_WRITE表示写入数据

当一个channel与服务端连接成功后,它就处于连接就绪状态,如果是可读的时候,就处于可读就绪状态。多个状态之间可以使用"|"来进行组合。

我们可以通过selectionKey的channel()方法获取Channel,我们可以通过它的selector()方法来获取Selector。

我们可以通过Selector对象的三个方法来获取一个或者多个Channel,它有三个方法:
第一个是select()方法,它在返回channel前处于阻塞状态,它返回当前就绪的channel的个数。
第二个是select(long timeout)方法,它在返回channel前也处于阻塞状态,它返回当前可以选择的channel的个数。
第三个是selectNow()方法,它不会阻塞,它会立即返回有多少合适的channel。

我们可以用selector来改造我们之前的基于TCP的服务端,我们这里新建范例代码如下:

/**
 * 基于nio并且使用了selector的服务端
 */
public class Demo11 {
    public static void main(String[] args) throws Exception {
        ServerSocketChannel channel = ServerSocketChannel.open();
        channel.configureBlocking(false);
        ServerSocket socket = channel.socket();
        socket.bind(new InetSocketAddress(8080));
        Selector selector = Selector.open();
        channel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器已启动...");
        while (true) {
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();

            while(iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                handle(key, selector);
            }
        }
    }

    public static void handle(SelectionKey key, Selector selector) throws Exception{
        if (key.isAcceptable()) {
            ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.finishConnect();
            socketChannel.register(selector, SelectionKey.OP_READ);
            System.out.println("客户端已连接...");
        } else if (key.isReadable()) {
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            SocketChannel socketChannel = (SocketChannel) key.channel();
            socketChannel.read(byteBuffer);
            socketChannel.write(ByteBuffer.wrap("received!!".getBytes()));
            socketChannel.close();
            System.out.println("从客户端收到的信息:" + new String(byteBuffer.array()));
        } else {
            System.out.println("内部错误...");
        }
    }

}

然后我们这里的客户端可以用Selector去重新实现,也可以直接复用Demo9的代码,不过我这里在读和写之间加了一秒钟的等待时间来确保写操作完成,如下:

/**
 * 基于nio实现的TCP客户端
 */
public class Demo12 {
    public static void main(String[] args) throws Exception{
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false); // 设置为非阻塞
        socketChannel.connect(new InetSocketAddress(8080));
        socketChannel.finishConnect(); // 等待连接完成

        // 向服务端发送一段文本
        byte[] bytes = "hello 辛星".getBytes();
        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        socketChannel.write(byteBuffer);

        // 等待一秒钟
        Thread.sleep(1000);

        // 最多只接受服务端的1024个字节
        ByteBuffer responseBuffer = ByteBuffer.allocate(1024);
        socketChannel.read(responseBuffer);
        System.out.println("服务端响应的数据:" + new String(responseBuffer.array()));
    }
}

我们首先运行服务器,如下所示:


image.png

然后我们运行Demo12这个客户端,如下所示:


image.png

然后我们在Demo11的控制台下就会看到对应的输出:


image.png

对于Selector,就暂时介绍到这里啦。

相关文章

网友评论

      本文标题:辛星2018年nio教程第八篇:初识Selector

      本文链接:https://www.haomeiwen.com/subject/cvtbzxtx.html