• java nio之selector


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

      二、选择器的创建以及使用

      1)创建 

    Selector selector = Selector.open();

      2)注册选择器(Channel这里不介绍)

    socketChannel.configureBlocking(false);
    socketChannel.register(selector, SelectionKey.OP_READ)

      注意:一个通道注册到选择器中,必须是非阻塞的。

      3)注册模式有4种

    SelectionKey.OP_CONNECT 
    SelectionKey.OP_ACCEPT 
    SelectionKey.OP_READ 
    SelectionKey.OP_WRITE

      4)SelectionKey的使用

      在选择其中会存在多个选择键SelectionKey,每一个选择键的类型可能不一样,所以我们这里需要判定是哪一种类型

    selector.selectedKeys() //获取所有选择键
    selectionKey.isConnectable() //是否是连接选择键
    selectionKey.isReadable() //读取
    selectionKey.isWritable() //写入
    selectionKey.isAcceptable() //接收

      获取对应的选择键过后可以强转成对应的通信管道。(示例)

    SocketChannel channel = (SocketChannel) selectionKey.channel();

      三、聊天室的基本写法(基本使用都在里面)

      1)客户端

    package com.troy.nio.application;
    
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    
    public class Client {
    
        //选择器
        private static Selector selector;
        //通信管道
        private static SocketChannel socketChannel;
        public static void main(String[] args) {
            try {
                clientInit();
                listen();
                //发送数据
                while (true) {
                    Thread.sleep(1000);
                    socketChannel.write(ByteBuffer.wrap(("hello server!").getBytes()));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        //初始化选择器和发送数据
        private static void clientInit() throws Exception {
            //打开一个通道管理器
            selector = Selector.open();
            //获取一个通信管道
            socketChannel = SocketChannel.open();
            //设置对应的发送地址和端口
            socketChannel.connect(new InetSocketAddress("localhost",9000));
            //设置非阻塞
            socketChannel.configureBlocking(false);
            //注册一个写入事件
            socketChannel.register(selector, SelectionKey.OP_READ);
        }
    
        //监听服务器返回的数据
        private static void listen() throws Exception {
            Runnable runnable = () -> {
                while (true) {
                    try {
                        //这里会一直阻塞,直到事件过来
                        selector.select();
                        //在选择器中获取对应的注册事件
                        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                        while (iterator.hasNext()) {
                            //注册事件
                            SelectionKey selectionKey = iterator.next();
                            iterator.remove();
                            //判断是否是读事件
                            if (selectionKey.isReadable()) {
                                //获取对应通信管道,并处理层数据
                                SocketChannel channel = (SocketChannel) selectionKey.channel();
                                //一次性读取数据量,这里应该做循环,我这里方便没有做
                                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                                channel.read(byteBuffer);
                                byteBuffer.flip();
                                System.out.println(new String(byteBuffer.array()).trim());
                            }
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e.getMessage());
                    }
                }
            };
            new Thread(runnable).start();
        }
    }

      2)服务端

    package com.troy.nio.application;
    
    
    import java.io.IOException;
    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;
    
    public class Server {
    
        //选择器
        private static Selector selector;
        //服务端通信管道
        private static ServerSocketChannel channel;
        public static void main(String[] args) {
            try {
                serverInit();
                listen();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        //初始化
        private static void serverInit() throws IOException {
            //打开一个选择器
            selector = Selector.open();
            //打开一个服务端通信管道
            channel = ServerSocketChannel.open();
            //设置接收端口
            channel.socket().bind(new InetSocketAddress(9000));
            //设置非阻塞
            channel.configureBlocking(false);
            //注册接收事件
            channel.register(selector, SelectionKey.OP_ACCEPT);
        }
    
        //监听
        private static void listen() throws IOException {
            while (true) {
                //形成阻塞事件,接口完成后进行下一步
                selector.select();
                //获取选择器中的事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    iterator.remove();
                    //判断是否是接受事件
                    if (selectionKey.isAcceptable()) {
                        SocketChannel socketChannel = channel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector,SelectionKey.OP_READ);
                    }
                    //是否是可读事件
                    if (selectionKey.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        //这里的目的是当这个服务端一直存在,因为读取数据存在异常,直接处理掉,下一个客户端景来可以继续接受
                        try {
                            socketChannel.read(byteBuffer);
                        } catch (Exception e) {
                  selectionKey.cancel();
    continue; } byteBuffer.flip(); System.out.println(new String(byteBuffer.array()).trim()); socketChannel.write(ByteBuffer.wrap("hello client!".getBytes())); } } } } }
  • 相关阅读:
    extjs 登录
    Digg 顶 踩(jQuery+Ajax)
    长度不能小于 0。 参数名: length
    .net 资料
    Micromedia Flash Player已终止一项可能不安全的操作解决办法
    SQL SERVER 与ACCESS、EXCEL的数据转换
    排名 sql
    FileUploaderAJAX 无刷新上传
    Microsoft Office Access ActiveX 部件不能创建对象
    流量分析作用
  • 原文地址:https://www.cnblogs.com/ll409546297/p/7929646.html
Copyright © 2020-2023  润新知