Java Nio基础

NIo(Non-Block IO)是java1.4以后提供的API,它是一个异步,面向缓冲区的IO,遵循React线程模型。
NIO与BIO的区别
  1. NIO是非阻塞的IO。NIO在进行读操作时,如果当前没有可读数据,则不会堵塞在这里,而是继续向下执行,进行写操作时也不会等待数据全部写入,而是可以继续执行。BIO即我们以前使用的IO流,它是一个同步阻塞的IO流。它在进行读操作时,必须读取到有效数据才会继续执行,否则就堵塞,同样的进行写操作时也必须等待数据全部写入,才能袭击执行。

  2. NIO是通过缓冲区读写数据,BIO则是通过流来读写数据。

  3. NIO只使用一个Channel来读写数据,BIO则必须通过两个读写流来读写数据。

NIO基本概念
  1. Channel:IO通道,配合缓冲区可以进行读写操作。它与流的不同之处在于它是全双工的,可读可写,因此它比流更好映射底层系统的API

  2. Selector:多路复用器,用于监听读写连接事件,是NIO 的核心组件之一。它会不断轮询已注册的channel,一旦发生监听的事件就会将事件放入selectorkey中,

  3. Buffer:缓冲区,NIO中都是通过缓冲区来进行读取写入的。它本质上是一个数组,内部除了数组外还维护了读写位置的信息。

ByteBuffer的重要属性

属性作用
Capacity容量,即可以容纳的最大数据量;在缓冲区创建时被设定并且不能改变
Limit在写模式下表示最多能写入多少数据,此时和capacity相同。在读模式下表示最多能读多少数据,此时和缓存区的实际数据大小相同。
Position位置,下一个要被读或写的元素的索引,每次读写缓冲区数据时都会改变改值,为下次读写作准备
Mark标记,调用mark()来设置mark=position,再调用reset()可以让position恢复到标记的位置
NIO服务器客户端通信例子

NIO 服务端

package FrameWork.com.nio.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * Created by forget on 2019/11/26.
 */
public class NioServer implements Runnable{

    private ServerSocketChannel serverSocketChannel;

    private String host="localhost";

    private Selector selector;

    private int port=8011;

    public NioServer() {
    }

    public NioServer(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws IOException {
        selector=Selector.open();
        serverSocketChannel=ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(host,port));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    @Override
    public void run() {
        try {
            start();
            System.out.println("开启服务器,地址是"+host+":"+port);
        } catch (IOException e) {
            e.printStackTrace();
        }
        while (true){
            try {
                if(selector.select(1000)==0)
                {
                    System.out.print(".");
                    continue;
                }

                Iterator<SelectionKey> keys=selector.selectedKeys().iterator();
                SelectionKey key=null;
                   while (keys.hasNext()){
                       key=keys.next();
                       keys.remove();
                       if(key.isValid())
                       {
                           if(key.isReadable())
                           {
                               SocketChannel socketChannel= (SocketChannel) key.channel();
                               ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                               int l=socketChannel.read(byteBuffer);
                               if(l>0)
                               {
                                   byteBuffer.flip();
                                   byte[] bytes=new byte[byteBuffer.remaining()];
                                   byteBuffer.get(bytes);
                                   String body=new String(bytes,"UTF-8");
                                   System.out.println("消息内容:"+body);
                                   write("你好,客户端,收到消息:"+body,socketChannel);
                               }
                               else if(l<0)
                               {
                                   key.cancel();
                                   socketChannel.close();
                               }
                           }

                           if(key.isAcceptable())
                           {
                               ServerSocketChannel socketChannel= (ServerSocketChannel) key.channel();
                               SocketChannel sc=socketChannel.accept();
                               sc.configureBlocking(false);
                               sc.register(selector,SelectionKey.OP_READ);
                           }
                       }

                   }
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
    }

    public void write(String message,SocketChannel socketChannel) throws IOException {
        byte [] bytes=message.getBytes();
        ByteBuffer byteBuffer=ByteBuffer.allocate(bytes.length);
        byteBuffer.put(bytes);
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
    }
}

NIO的ServerSocketChannel 对应的就是BIO的ServerSocket,此处我们先获取一个ServerSocketChannel 对象,绑定地址,将channel配置为非阻塞模式,register方法将channel注册到selector多路复用器上监听客户端的连接请求。Selector不断轮询Channel查看是否有就绪的事件。处理连接事件,配置客户端Socket参数,注册到Selector监听可读事件,处理读取事件,通过缓冲区异步读取数据,调用flip方法切换缓冲区至读模式。

package FrameWork.com.nio.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * Created by forget on 2019/11/26.
 */
public class Client implements Runnable{

    private Selector selector;

    private SocketChannel socketChannel;

    public Client() throws IOException {
        selector=Selector.open();
        socketChannel=SocketChannel.open();
    }

    public void start() throws IOException {
        socketChannel.configureBlocking(false);
        boolean res=socketChannel.connect(new InetSocketAddress("localhost",8011));
        if(res)
        {
            System.out.println("连接就绪");
            socketChannel.register(selector, SelectionKey.OP_READ);
        }
        else {
            socketChannel.register(selector,SelectionKey.OP_CONNECT);
        }
    }

    @Override
    public void run() {
        try {
            start();
        } catch (IOException e) {
            e.printStackTrace();
        }

        while(true){
            try {
                selector.select(1000);
                Iterator<SelectionKey> keys=selector.selectedKeys().iterator();
                SelectionKey key=null;
                while (keys.hasNext())
                {
                    key=keys.next();
                    keys.remove();
                    if(key.isValid())
                    {
                        if(key.isReadable())
                        {
                            SocketChannel socketChannel= (SocketChannel) key.channel();
                            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                            int l=socketChannel.read(byteBuffer);
                            if(l>0)
                            {
                                byteBuffer.flip();
                                byte [] bytes=new byte[byteBuffer.remaining()];
                                byteBuffer.get(bytes);
                                String body=new String(bytes,"UTF-8");
                                System.out.println("消息大小:"+bytes.length);
                                System.out.println("收到服务器消息:"+body);
                            }
                            else if(l<0)
                            {
                                key.cancel();
                                socketChannel.close();
                            }
                        }

                        if(key.isConnectable())
                        {
                            SocketChannel sc= (SocketChannel) key.channel();
                            if(sc.finishConnect())
                            {
                                sc.register(selector,SelectionKey.OP_READ);
                                System.out.println("连接服务器成功");
                                write("hello world!",socketChannel);
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }

    }

    public void write(String message, SocketChannel serverSocketChannel) throws IOException {
        byte [] bytes=message.getBytes();
        ByteBuffer byteBuffer=ByteBuffer.allocate(bytes.length);
        byteBuffer.put(bytes);
        byteBuffer.flip();
        serverSocketChannel.write(byteBuffer);
    }

    public Selector getSelector() {
        return selector;
    }

    public void setSelector(Selector selector) {
        this.selector = selector;
    }

    public SocketChannel getSocketChannel() {
        return socketChannel;
    }

    public void setSocketChannel(SocketChannel socketChannel) {
        this.socketChannel = socketChannel;
    }
}

NIO的SocketChannel 代表BIO的Socket,首先打开SocketChannel ,配置为非阻塞模式,连接服务器,判断是否连接成功,成功则向Selector注册读取事件,连接尚未成功则注册连接事件。Selector轮询Channel,查看是否有就绪的事件,如果是连接事件则获取SocketChannel注册读取事件到Selector,读取事件则通过缓冲区异步读取数据。

启动服务端和客户端测试效果

public class App 
{
    public static void main( String[] args )
    {
        Thread thread=new Thread(new NioServer());
        thread.start();
    }
}
public class TestClient {

    public static void main( String[] args ) throws IOException {
        Client client=new Client();
        Thread thread=new Thread(client);
        thread.start();

        Scanner scanner=new Scanner(System.in);
        String message=null;

        while(true)
        {
            message=scanner.nextLine();
            ByteBuffer byteBuffer=ByteBuffer.allocate(message.getBytes().length);
            byteBuffer.put(message.getBytes());
            byteBuffer.flip();
            client.getSocketChannel().write(byteBuffer);
        }

    }
}

结果如下:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值