NIO阻塞模式和非阻塞模式操作

####使用阻塞式的NIO的操作

package com.diedline.juc.nio;

import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * 使用   NIO完成网络通信的三个核心
 *  1. 通道channel : 负责连接
 *      java.nio.Channel    接口
 *          | --   SelectableChannel
 *                  |   -- SocketChannel
 *                  |   -- ServerSocketChannel
 *                  |   -- DatagramChannel
 *
 *                  |   -- Pipe.SinkChannel
 *                  |   -- Pipe.SourceChannel
 *
 *  2.  缓冲区: 负责数据的存取
 *
 *  3.  选择器Selector 是SelectableChannel 的多路复用器,用于监控SelectableChannel 的IO状况
 *
 */
public class TestBlockingNIO {
    //客户端
    @Test
    public void  client(){
        try {
            //获取通道
            SocketChannel socketChannel =  SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));

            //使用缓存区存取数据
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

            //先从本地读取文件并发送到服务端
            FileChannel fileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);

            while (fileChannel.read(byteBuffer) != -1){
                //先将缓冲区切换成读数据模式
                byteBuffer.flip();
                socketChannel.write(byteBuffer);
                byteBuffer.clear();
            }
            socketChannel.close();
            fileChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void server(){
        try {
            //获取通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //创建一个FileChannel
            FileChannel fileChannel = FileChannel.open(Paths.get("2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
            //绑定端口号
            serverSocketChannel.bind(new InetSocketAddress(9898));

            //获取客户端的连接的通道
            SocketChannel socketChannel =  serverSocketChannel.accept();

            //创建一个缓存区
            ByteBuffer bf = ByteBuffer.allocate(1024);

            //接收客户端的数据并保存到本地
            while (socketChannel.read(bf) != -1){
                   bf.flip();
                   fileChannel.write(bf);
                   bf.clear();
            }

            //关闭对应的通道
            fileChannel.close();
            serverSocketChannel.close();
            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

TestNIO

package com.diedline.juc.nio;

import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class TestBlockingNIO2 {

    //客户端
    @Test
    public void client(){
        try {
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));

            FileChannel fileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
            //创建一个缓存
            ByteBuffer buf = ByteBuffer.allocate(1024);
            //发送数据
            while (fileChannel.read(buf) != -1 ){
                buf.flip();
                socketChannel.write(buf);
                buf.clear();
            }
            //关闭连接进行写入 不关闭通道
            socketChannel.shutdownOutput();
            //接收服务端的反馈
            int len = 0;
            while ((len = socketChannel.read(buf)) != -1 ){
                buf.flip();
                System.out.println(new String(buf.array(),0, len));
                buf.clear();
            }
            socketChannel.close();
            fileChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //服务端
    @Test
    public void server(){
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(9898));
            SocketChannel channel =  serverSocketChannel.accept();
            ByteBuffer bf = ByteBuffer.allocate(1024);
            FileChannel fileChannel = FileChannel.open(Paths.get("2.png"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
            while (channel.read(bf) != -1){
                bf.flip();
                fileChannel.write(bf);
                bf.clear();
            }
            //发送反馈给客户端
            bf.put("服务端接受客户端的数据成功".getBytes());
            bf.flip();
            channel.write(bf);

            serverSocketChannel.close();
            channel.close();
            fileChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

####使用非阻塞模式的TcpNIO 如果还需要实现聊天可以使用多线程

package com.diedline.juc.nio;

import org.junit.jupiter.api.Test;

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

/**
 *
 /**
 * 使用   NIO完成网络通信的三个核心
 *  1. 通道channel : 负责连接
 *      java.nio.Channel    接口
 *          | --   SelectableChannel
 *                  |   -- SocketChannel
 *                  |   -- ServerSocketChannel
 *                  |   -- DatagramChannel
 *
 *                  |   -- Pipe.SinkChannel
 *                  |   -- Pipe.SourceChannel
 *
 *  2.  缓冲区: 负责数据的存取
 *
 *  3.  选择器Selector 是SelectableChannel 的多路复用器,用于监控SelectableChannel 的IO状况
 */
public class TestNonBlockingNIO {
    //客户端
    @Test
    public void client(){
        try {
            //获取通道
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
            //切换成非阻塞模式
            socketChannel.configureBlocking(false);

            //分配指定大小的缓存区
            ByteBuffer bf = ByteBuffer.allocate(1024);
            //发送数据给服务器
            bf.put(new Date().toString().getBytes());
            bf.flip();
            socketChannel.write(bf);
            bf.clear();

            //关闭通道
            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //服务端
    @Test
    public void server() throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //切换成非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //绑定端口号
        serverSocketChannel.bind(new InetSocketAddress(9898));

        //获取选择器
        Selector selector =  Selector.open();

        //将通道注册到选择器中,并且指定监听事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //轮询的获取选择器上已经就绪的事件  当大于0就代表有就绪的事件了
        while (selector.select() >0){
            //获取当前选择器中所有注册的选择键(已注册的监听事件)
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()){
                //迭代获取 SelectionKey(准备就绪的事件)
                SelectionKey selectionKey = it.next();
                //判断具体是什么事件准备就绪
                if(selectionKey.isAcceptable()){
                    //获取serverSocketChannel 所得的SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //切换非阻塞模式
                    socketChannel.configureBlocking(false);
                    //将该通道注册到选择器上
                    socketChannel.register(selector,SelectionKey.OP_READ);
                }else if(selectionKey.isReadable()){
                    //获取当前选择器上读就绪状态的通道  因为默认是selectorChannel 所以要强转
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                    //读取数据
                    ByteBuffer buf = ByteBuffer.allocate(1024);
                    int len = 0;
                    //如果有数据
                    while ((len = socketChannel.read(buf)) > 0){
                        buf.flip();
                        System.out.println(new String(buf.array(),0,len));
                        buf.clear();
                    }
                }
                //取消选择键SelectionKey
                it.remove();
            }
        }

    }
}