0
点赞
收藏
分享

微信扫一扫

Java NIO Selector 演示

一葉_code 2022-01-23 阅读 54

Java NIO Selector 演示

一、基本参数

1.属性

SelectionKey: 通过select方法监听通道事件,有事件发生时将该通道添加到SelectionKey集合并返回;遍历SelectionKey可获得对应通道

2.方法

select()       阻塞,直到至少有一个事件发生
select(time)   阻塞time毫秒数,没有事件就返回
wakeup()       唤醒
selectNow()    不阻塞,立即返回

3.流程

1.当 Client 连接时,ServerSocketChannel 得到 SocketChannel
2.SocketChannel 注册到 Selector 上,一个 Selector 可以注册多个 SocketChannel
3.注册后返回一个 SelectionKey 将 Selector 和 SocketChannel 关联
4.Selector 通过 select 方法监听事件 OP_READ OP_WRITE OP_CONNECT OP_ACCEPT
5.获取有事件发生的 SelectionKey ,通过channel方法获取对应 SocketChannel,进行业务处理

二、通信演示

1.服务端

package com.example.netty.io.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * Description: 非阻塞 IO 服务端
 *
 * @Author: zhx & moon hongxu_1234@163.com
 * @Date: 2021-11-03 23:32
 * @version: V1.0.0
 */
public class NIOServer {

    public static void main(String[] args) throws Exception {

        //创建serverSocketChannel -> serverSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //获取Selector
        Selector selector = Selector.open();

        //监听
        serverSocketChannel.socket().bind(new InetSocketAddress(7800));

        //设为非阻塞
        serverSocketChannel.configureBlocking(false);

        //把serverSocketChannel注册到selector 并将关心事件设为 OP_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //循环等待客户端连接
        while (true){

            if (selector.select(1000)==0){
                //System.out.println("服务器等待了 1S 没有事件发生");
                continue;
            }

            /**
             * 1.如果返回>0 就获取相关的 selectionKeySet
             * 2.selector.selectedKeys() 返回关心事件的集合
             * 3.通过 selectionKeySet 反向获取通道
             */

            Set<SelectionKey> selectionKeySet = selector.selectedKeys();

            Iterator<SelectionKey> iterator = selectionKeySet.iterator();

            while (iterator.hasNext()){

                SelectionKey k = iterator.next();

                //根据事件类型做处理
                if(k.isAcceptable()){
                    //如果是连接请求,就为客户端生成一个SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    //把serverSocketChannel注册到selector 并将关心事件设为 OP_READ 同时关联一个 Buffer
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));

                    System.out.println("客户端连接成功,生成了一个 SocketChannel : " + socketChannel.hashCode());
                }

                if (k.isReadable()){
                    //通过 selectionKeySet 反向获取通道
                    SocketChannel channel = (SocketChannel)k.channel();
                    //获取到该Channel关联的Buffer
                    ByteBuffer buffer = (ByteBuffer)k.attachment();

                    int read = channel.read(buffer);

                    String message = new String(buffer.array(), StandardCharsets.UTF_8);

                    System.out.println("From Client Message : " + message.trim());

                    if (read==-1){
                        channel.close();
                    }
                }

                //手动从集合中移除当前的 SelectionKey 防止重复操作
                iterator.remove();
            }
        }
    }
}

2.客户端

package com.example.netty.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;

/**
 * Description: 非阻塞 IO 客户端
 *
 * @Author: zhx & moon hongxu_1234@163.com
 * @Date: 2021-11-04 0:09
 * @version: V1.0.0
 */
public class NIOClient {

    public static void main(String[] args) throws IOException {
        //得到一个网络通道
        SocketChannel socketChannel = SocketChannel.open();
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //设置服务地址和端口
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1",7800);
        //连接服务器
        if(!socketChannel.connect(inetSocketAddress)){
            while (!socketChannel.finishConnect()){
                System.out.println("因为连接需要时间 客户端不会阻塞 可以处理其他工作...");
            }
        }
        //如果连接成功,就发送数据
        String str = "hello moon";
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8));
        //将 buffer 数据 写入 channel
        socketChannel.write(buffer);
        //改变通道状态
        buffer.clear();
        buffer.flip();
        //使代码停住
        System.in.read();
        //关闭连接
        //socketChannel.close();
    }
}

3.调试 先启动服务 在启动客户端

在这里插入图片描述

举报

相关推荐

0 条评论