Java NIO 模型代码示例

在高并发(持续大量的连接同时请求)场景中,之前的两种 BIO 优化方案都需要消耗大量的线程来维持连接。并且 CPU 在线程切换上消耗很大。

Java NIO 模型的主要优势:少量的线程就可以处理大量连接的请求

主要组成:

  1. Channel 通道:IO 传输发生时数据通过的入口
  2. Buffer 缓冲区:可以理解为数据在管道传输时的起点和终点
  3. Selector 选取器(IO监听器):负责监听 IO 事件

所用通道都向 Selector 注册,Selector 负责轮询检测,然后服务端进程会阻塞在 Selector 的 select() 方法,直到注册的通道有事件就绪。

在这里插入图片描述

代码示例 : NIOServer.java

package org.io.nio;

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;
import java.util.Set;

public class NIOServer {

    // 服务端通道
    private ServerSocketChannel serverChannel;

    // 选择器
    private Selector selector;

    // 默认服务绑定端口
    private static int DEFAULT_BIND_PORT = 9000;

    public NIOServer(int port) {
        initServer(port);
    }

    private void initServer(int port) {
        try {
            // 开启一个服务通道
            this.serverChannel = ServerSocketChannel.open();
            // 将通道绑定到指定端口
            this.serverChannel.bind( (port < 1 || port > 65535) ?
                    new InetSocketAddress(DEFAULT_BIND_PORT) :
                    new InetSocketAddress(port));
            // 将通道设置为非阻塞模式
            this.serverChannel.configureBlocking(false);
            // 打开一个 IO 监视器:Selector
            this.selector = Selector.open();
            // 将服务通道注册到 Selector 上,并在服务端通道注册 OP_ACCEPT 事件
            this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
        } catch (IOException ioException) {
            ioException.printStackTrace();
            System.out.println("init exception: " + ioException);
        }
    }

    public void startServer() throws InterruptedException {
        while (true){
            System.out.println("Selector 巡查 IO 事件---------------开始");
            try {
                int ioEventCount = this.selector.select();  // 此处以收集到所有 IO 事件
                System.out.println("Selector 检测到:" + ioEventCount);
            } catch (IOException ioException) {
                ioException.printStackTrace();
                break;
            }
            // 对各个 IO 事件做出对应的响应
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();  //通过调用迭代器的 remove() 方法将这个键 key 从已选择键的集合中删除

                try {
                    // 可接收连接 能注册SelectionKey.OP_ACCEPT事件的只有 ServerSocketChannel通道
                    if (key.isAcceptable()) {
                        System.out.println("监控到 OP_ACCEPT 连接事件");
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();

                        // 接受客户端连接
                        SocketChannel client = server.accept();
                        System.out.println("Accept connection from " + client);
                        client.configureBlocking(false); // 设置客户端通道非阻塞
                        // 为客户端通道注册 OP_WRITE 和 OP_READ 事件
                        SelectionKey clientKey = client.register(selector,
                                SelectionKey.OP_WRITE |
                                        SelectionKey.OP_READ);
                        // 为客户端通道添加一个数据缓存区
                        ByteBuffer buffer = ByteBuffer.allocate(100);
                        clientKey.attach(buffer);
                    }
                    // 可读数据
                    if (key.isReadable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        int read = client.read(output);
                        System.out.println("Read data from client: " + client);
                        System.out.println("------------MSG : " + output.toString());
                    }
                    // 可写数据
                    if (key.isWritable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        output.flip();
                        client.write(output);
                        output.compact();
                        System.out.println("Write data to " + client);
                    }
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }

            }

            Thread.sleep(2000);// 为了观察控制台打印数据
            System.out.println("Selector 巡查 IO 事件---------------完成");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        NIOServer nioServer = new NIOServer(DEFAULT_BIND_PORT);
        nioServer.startServer();
    }
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值