I/O 与 NIO 模型基础(Netty 权威指南)

I/O

I/O 模型的五种类型
  1. 阻塞I/O模型:最常用的模型,进程处理过程中阻塞一致到结果返回
  2. 非阻塞I/O模型:轮询检查是否缓冲区有数据,没有直接跳过
  3. I/O复用模型:select/poll 或者epoll,select 等待多个文件描述符就绪,即多个I/O阻塞在一个select,等待select分配
  4. 信号驱动I/O模型:非阻塞,当数据准备就绪生成信号通知进程处理
  5. 异步I/O:通知程序执行,等程序执行完主动通知
epoll(I/O复用模型)相对于select的改进
  • 支持一个进程打开的socket文件描述符(fd)没有上限(仅限制操作系统的最大文件句柄数)
  • I/O效率不会随着FD数目的增加而下降
  • 使用mmap加速内核与用户空间的消息传递 转载:传送门mmap介绍
  • API更加简单

NIO(非阻塞IO)

  • 缓冲区buffer:包含写入/读出数据的对象。所有数据通过缓冲区处理,缓冲区本质为一个数组。常用byteBuffer,字节缓存区。
  • 通道channel
    在这里插入图片描述
  • 多路复用器selector:提供选择已经就绪的任务的能力。轮询channel获取就绪的channel集合,进行后续的I/O处理。由于采用epoll没有上限,selector之需要一个线程执行轮询则可接入n多的客户端。

NIO服务端

在这里插入图片描述

import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.net.InetAddress;
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;

public class ServerSocketChannelDemo implements Runnable{

    private Selector selector;
    private volatile  boolean flag = false;

    public ServerSocketChannelDemo() {
        try {
            ServerSocketChannel socketChannel = ServerSocketChannel.open();
            socketChannel.socket().bind(new InetSocketAddress(InetAddress.getByName("localhost"),8080));
            //设置非阻塞模式
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_ACCEPT);
            //创建selector
            selector = Selector.open();
        }catch (IOException e){
            e.printStackTrace();
        }

    }

    public void stop(){
        this.flag = true;
    }

    @SneakyThrows
    @Override
    public void run() {
        while (!flag){
            selector.select(1000);
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            SelectionKey next = null;
            while (iterator.hasNext()){
               next = iterator.next();
               iterator.remove();
               try {
                   this.handleInput(next);
               }catch (Exception e){
                  if(next != null){
                      next.cancel();
                      if(next.channel() != null){
                          next.channel().close();
                      }
                  }
               }
            }
        }

        if(selector != null){
            selector.close();
        }
    }

    @SneakyThrows
    public void handleInput(SelectionKey key){
        if(key.isValid()){
            //处理新接入的请求信息
            if(key.isAcceptable()){
                ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                SocketChannel channel = ssc.accept();
                channel.configureBlocking(false);
                channel.socket().setReuseAddress(true);
                channel.register(selector,SelectionKey.OP_READ);
            }

            if(key.isReadable()){
                SocketChannel channel = (SocketChannel)key.channel();
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int read = channel.read(byteBuffer);
                if(read > 0){
                    byteBuffer.flip();
                    byte[] bytes = new byte[byteBuffer.remaining()];
                    byteBuffer.get(bytes);
                    String body = new String(bytes, StandardCharsets.UTF_8);
                    this.doWrite(channel,body);
                }else if(read < 0){
                    key.channel();
                    channel.close();
                }
            }
        }
    }

    @SneakyThrows
    private void doWrite(SocketChannel channel,String response){
        if(!StringUtils.isBlank(response)){
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            channel.write(writeBuffer);
        }
    }
}

NIO客户端

在这里插入图片描述

import lombok.SneakyThrows;

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.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class ServerSocketChannelClientDemo implements Runnable{

    private Selector selector;
    private SocketChannel socketChannel;
    private volatile  boolean stop = false;


    @SneakyThrows
    public ServerSocketChannelClientDemo(){
        selector = Selector.open();
        socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
    }

    @SneakyThrows
    private void connect(){
        if(socketChannel.connect(new InetSocketAddress("localhost",8080))){
            socketChannel.register(selector, SelectionKey.OP_READ);
            //
            doWrite(socketChannel);
        }else {
            socketChannel.register(selector,SelectionKey.OP_CONNECT);
        }
    }

    @SneakyThrows
    private void doWrite(SocketChannel sc){
        byte[] req = "hello nio".getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
        writeBuffer.put(req);
        writeBuffer.flip();
        sc.write(writeBuffer);
        if(!writeBuffer.hasRemaining()){
            //已经全部发完
        }
    }


    @Override
    public void run() {
        connect();
        while (!stop){
            try {
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                SelectionKey key;
                while (iterator.hasNext()){
                    key = iterator.next();
                    iterator.remove();
                    try {
                        handleInput(key);
                    }catch (Exception e){
                        if(key != null){
                            key.channel();
                            if(key.channel() != null){
                                key.channel().close();
                            }
                        }
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        if(selector != null){
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @SneakyThrows
    private void handleInput(SelectionKey key) {
        if(key.isValid()){
            SocketChannel channel = (SocketChannel) key.channel();
            if(key.isConnectable()){
                if(channel.finishConnect()) {
                    channel.register(selector, SelectionKey.OP_READ);
                    doWrite(channel);
                }
            }

            if(key.isReadable()){
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int length = channel.read(readBuffer);
                if(length > 0){
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes, StandardCharsets.UTF_8);
                    System.out.println(body);
                    this.stop = true;
                }else if(length < 0){
                    key.cancel();
                    channel.close();
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值