BIO和NIO的区别

概述

BIONIO

阻塞等待IO:

连接建立后,如果当前线程暂时没有数据可读,则当前线程会一直阻塞在 Read 操作上,造成线程资源浪费。

非阻塞IO:

当线程从某通道进行读写数据时,若没有数据可用时,该线程会去执行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理多个输入和输出通道

BIO 基于字节流和字符流进行操作NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作

BIO处理流程

 

NIO处理流程

 

 

 BIO服务端代码示例

public class Server {

    public static void main(String[] args) throws IOException {
        //创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //创建serverSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        for (; ; ) {
            System.out.println("等待连接中...");
            //监听,等待客户端连接
            Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");
            executorService.execute(() -> handler(socket));
        }
    }

    //编写一个handler方法,和客户端通讯
    public static void handler(Socket socket) {
        byte[] bytes = new byte[1024];
        System.out.println("当前线程信息: " + Thread.currentThread().getName());
        try {
            //通过socket获取输入流
            // 阻塞直到有IO事件发生
            InputStream inputStream = socket.getInputStream();
            //循环读取客户端发送的数据
            while (inputStream.read(bytes) != -1) {
                System.out.println(Thread.currentThread().getName()+ " : 发送信息为 :"+ new String(bytes, 0, bytes.length));
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("关闭连接");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

NIO服务端代码示例

package nio;
 
import org.springframework.util.StringUtils;
 
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.Date;
import java.util.Iterator;
import java.util.Set;
 
public class ServerSocketChannels implements Runnable {
 
    private  ServerSocketChannel serverSocketChannel;
 
    private  Selector selector;
 
    private volatile boolean stop;
 
 
    public ServerSocketChannels(int port){
 
        try {
            //创建多路复用器selector,工厂方法
            selector = Selector.open();
            //创建ServerSocketChannel,工厂方法
            serverSocketChannel = ServerSocketChannel.open();
            //绑定ip和端口号,默认的IP=127.0.0.1,对连接的请求最大队列长度设置为backlog=1024,如果队列满时收到连接请求,则拒绝连接
            serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
            //设置非阻塞方式
            serverSocketChannel.configureBlocking(false);
            //注册serverSocketChannel到selector多路服用器上面,监听accrpt请求
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("the time is start port = " + port);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
 
    public void stop(){
        this.stop = true;
    }
 
    @Override
    public void run() {
        //如果server没有停止
        while(!stop){
            try {
                //selector.select()会一直阻塞到有一个通道在你注册的事件上就绪了
                //selector.select(1000)会阻塞到1s后然后接着执行,相当于1s轮询检查
                selector.select(1000);
                //找到所有准备接续的key
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey key = null;
               while(it.hasNext()){
                   key = it.next();
                   it.remove();
                   try {
                       //处理准备就绪的key
                       handle(key);
                   }catch (Exception e){
                       if(key != null){
                           //请求取消此键的通道到其选择器的注册
                           key.cancel();
                           //关闭这个通道
                           if(key.channel() != null){
                               key.channel().close();
                           }
                       }
                   }
               }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        if(selector != null){
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    public void handle(SelectionKey key) throws IOException {
         //如果key是有效的
          if(key.isValid()){
              //监听到有新客户端的接入请求
              //完成TCP的三次握手,建立物理链路层
              if(key.isAcceptable()){
                  ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                  SocketChannel sc = (SocketChannel) ssc.accept();
                  //设置客户端链路为非阻塞模式
                  sc.configureBlocking(false);
                  //将新接入的客户端注册到多路复用器Selector上
                  sc.register(selector, SelectionKey.OP_READ);
              }
              //监听到客户端的读请求
              if(key.isReadable()){
                  //获得通道对象
                  SocketChannel sc = (SocketChannel) key.channel();
                  ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                  //从channel读数据到缓冲区
                 int readBytes = sc.read(readBuffer);
                 if (readBytes > 0){
                    //Flips this buffer.  The limit is set to the current position and then
                     // the position is set to zero,就是表示要从起始位置开始读取数据
                     readBuffer.flip();
                     //eturns the number of elements between the current position and the  limit.
                     // 要读取的字节长度
                     byte[] bytes = new byte[readBuffer.remaining()];
                     //将缓冲区的数据读到bytes数组
                     readBuffer.get(bytes);
                     String body = new String(bytes, "UTF-8");
                     System.out.println("the time server receive order: " + body);
                     String currenttime = "query time order".equals(body) ? new Date(System.currentTimeMillis()).toString(): "bad order";
                     doWrite(sc, currenttime);
                 }else if(readBytes < 0){
                    key.channel();
                    sc.close();
                  }
              }
          }
    }
 
    public static void doWrite(SocketChannel channel, String response) throws IOException {
        if(!StringUtils.isEmpty(response)){
            byte []  bytes = response.getBytes();
            //分配一个bytes的length长度的ByteBuffer
            ByteBuffer  write = ByteBuffer.allocate(bytes.length);
            //将返回数据写入缓冲区
            write.put(bytes);
            write.flip();
            //将缓冲数据写入渠道,返回给客户端
            channel.write(write);
        }
    }
}
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值