java_NIO

 

 

/**
 * 缓冲区(Buffer):在java NIO 中负责存储数据;缓冲区就是数组,存储不同类型的数据;
 *
 * 根据不同的类型(boolean类型除外),提供了相应类型的缓冲区;
 * ByteBuffer
 * CharBuffer
 * ShortBuffer
 * IntBuffer
 * LongBuffer
 * FloatBuffer
 * DoubleBuffer
 *
 * 上述缓冲区的管理方式几乎一致,都是使用 allocate();获取缓冲区
 *
 * 缓冲区中存储数据的核心
 * put();存入数据到缓存区中
 * get();获取缓冲区中的数据
 *
 * 缓冲区的四大核心属性:
 * capacity:容量、表示缓冲区中最大存储数据的容量、一旦声明不能更改;
 * limit:界限、表示缓冲区中可以操作数据的大小;(limit 后数据不能进行读写);
 * position:位置、表示缓冲区中正在操作数据的位置;
 *
 * mark:标记、表示记录当前 position 的位置;可以通过 reset() 将恢复 position 到 mark记录的位置;
 *
 * mark <= position <= limit <= capacity
 */
public class TestBuffer {
    public static void main(String[] args) {
        test01();
    }

    public static void test01(){
        //一个大小为 5 个字节的字符串
        String str = "abcde";
        //1、分配一个指定大小的缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        System.out.println("--------allocate()--------");
        //查看当前缓冲区、正在操作数据的位置(当前缓冲区未写入数据、因此 position指向第一个位置、也就是起始位置、下标为 0)
        System.out.println(byteBuffer.position());
        //查看当前缓冲区、可操作数据最大位置(当前是写入模式、最多可以写入 1024 字节)
        System.out.println(byteBuffer.limit());
        //查看当前缓冲区、容量(当前缓冲区容量为 1024 字节)
        System.out.println(byteBuffer.capacity());
        //2、利用 put() 将数据存入到缓冲区
        System.out.println("---------put()------------");
        //将字符串转换为字节并存储到缓冲区
        byteBuffer.put(str.getBytes());
        //当前缓冲区存入了5个字节、下标 0-4、因此 position 指向下标为 5;
        System.out.println(byteBuffer.position());
        //当前缓冲区可存入的最大位置为 1024
        System.out.println(byteBuffer.limit());
        //当前缓冲区的容量是 1024
        System.out.println(byteBuffer.capacity());
        //3、切换到读取数据的模式
        System.out.println("---------flip()------------");
        byteBuffer.flip();
        //当前是读取数据模式、当前 position在起始位置、下标为 0
        System.out.println(byteBuffer.position());
        //当前缓冲区存入了 5字节、下标 0-4、limit指向 下标 5(表示当前缓冲区最多可操作 5个字节、下标0-4)
        System.out.println(byteBuffer.limit());
        //当前缓冲区容量为 1024
        System.out.println(byteBuffer.capacity());
        //4、利用 get() 读取缓冲区中的数据
        System.out.println("---------get()------------");
        //创建一个字节数组、5 个长度
        byte[] bytes = new byte[byteBuffer.limit()];
        //获取缓冲区的所有数据、并存入到字节数组中
        byteBuffer.get(bytes);
        //创建一个字符串(当前字节数组从 下标 0开始、5个长度)并输出
        System.out.println(new String(bytes,0,bytes.length));
        //当前取出了所以数据、下标 0-4、因此 position指向 5
        System.out.println(byteBuffer.position());
        //当前可操作的数据是 5个长度
        System.out.println(byteBuffer.limit());
        //容量 1024
        System.out.println(byteBuffer.capacity());
        //5、利用 rewind() 可重复读取缓冲区中的数据
        System.out.println("---------rewind()------------");
        //将当前缓冲区的 position 置为起始位置处 下标 0
        //并且将 mark 置为默认值 -1
        byteBuffer.rewind();
        //0
        System.out.println(byteBuffer.position());
        //5
        System.out.println(byteBuffer.limit());
        //1024
        System.out.println(byteBuffer.capacity());

        System.out.println("---------mark()------------");
        //6、利用 mark()记录当前 position的位置
        byte[] byteArr = new byte[10];
        //从 byteBuffer缓冲区中取出 2个元素、下标 从 0开始 即 0、1
        byteBuffer.get(byteArr,0,2);
        //输出
        System.out.println(new String(byteArr,0,10));
        //当前 position下标 指向 2
        System.out.println(byteBuffer.position());
        //当前 limit下标 指向 5
        System.out.println(byteBuffer.limit());
        //容量 1024
        System.out.println(byteBuffer.capacity());
        //使用 mark()记录当前 position
        byteBuffer.mark();
        //当前从缓冲区再取出一个字节、若当前 position指向 limit(最大可操作数量)、再次取出数据时会抛出异常
        byteBuffer.get();
        //当前 position下标 指向 3
        System.out.println(byteBuffer.position());
        //当前 limit下标 指向 5
        System.out.println(byteBuffer.limit());
        //容量 1024
        System.out.println(byteBuffer.capacity());
        //使用reset()复原 position、基于 mark的记录、如果 mark 为默认值 -1、则会抛出异常
        byteBuffer.reset();
        //当前 position下标 指向 2
        System.out.println(byteBuffer.position());
        //当前 limit下标 指向 5
        System.out.println(byteBuffer.limit());
        //容量 1024
        System.out.println(byteBuffer.capacity());

        System.out.println("----------------------");
        //判断缓冲区是否还有剩余数据
        if (byteBuffer.hasRemaining()){
            //返回缓冲区剩余数据的数量
            System.out.println(byteBuffer.remaining());
        }

        //10、利用 clear() 清空缓冲区、缓冲区中的数据依然存在、处于一个 ”被遗忘“的状态;
        System.out.println("---------clear()------------");
        //将 position 置为起始位置处 下标 0
        //将 limit 置为 容量值 capacity
        //并且将 mark 置为默认值 -1
        byteBuffer.clear();
        System.out.println(byteBuffer.position());
        System.out.println(byteBuffer.limit());
        System.out.println(byteBuffer.capacity());
    }

}

 

 

直接缓冲区和非直接缓冲区

非直接缓冲区:通过 allocate(int size);分配的缓冲区、将缓冲区建立在 JVM 内存中;

直接缓冲区:通过 allocateDirect(int size);分配的缓冲区、将缓冲区建立在操作系统的物理内存之中;可以提高效率

public static void test02(){
        //创建一个非直接缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //byteBuffer.isDirect() 返回true:直接缓冲区、false:非直接缓冲区
        System.out.println(byteBuffer.isDirect());//false

        ByteBuffer byteBuffer2 = ByteBuffer.allocateDirect(1024);
        System.out.println(byteBuffer2.isDirect());//true
    }

 

 

 

 

通道之间的数据传输与内存映射文件

 

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.*;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 * 一、Channel:通道、用于源节点与目标节点的连接、在 Java NIO 中负责缓冲区数据的传输、Channel本身不存储数据、因此需要配合缓冲区进行传输;
 * <p>
 * 二、通道的主要实现类:
 * java.nio.channels.Channel接口:
 * FileChannel
 * SocketChannel
 * ServerSocketChannel
 * DatagramChannel
 * <p>
 * 三、获取通道
 * 1、在java中针对通道的类提供了 getChannels() 方法
 * 本地 IO
 * FileInputStream/FileOutStream
 * RandomAccessFile
 * <p>
 * 网络 IO
 * Socket
 * ServerSocket
 * DatagramSocket
 * <p>
 * 2、在 java中的 NIO针对各个通道提供了静态 open()方法;
 * 2、在 java中的 NIO的 Files工具类的 newByteChannel();
 * <p>
 * 四、通道之间的数据传输
 * transferTo()
 * transferFrom()
 * <p>
 * 五、分散与聚集
 * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中去
 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中去
 * <p>
 * 六、字符集 Charset
 * 编码:字符串 -> 字节数组
 * 解码:字节数组 -> 字符串
 */

public class TestChannel {
    public static void main(String[] args) throws Exception {
        test6();
    }

    //字符集、编码与解码
    public static void test6() throws Exception {
        //通过名称获取字符集
        Charset charsetUTF8 = Charset.forName("UTF-8");
        //通过当前字符集获取一个编码器
        CharsetEncoder ce = charsetUTF8.newEncoder();
        //通过当前字符集获取解码器
        CharsetDecoder cd = charsetUTF8.newDecoder();
        //创建一个1024个长度的 字符非直接缓冲区
        CharBuffer charBuffer = CharBuffer.allocate(1024);
        //为改字符非直接缓冲区存储数据
        charBuffer.put("汪洋大海!");
        //将该字符非直接缓冲区转换为读取模式
        charBuffer.flip();
        /**
         * 通过当前编码器进行编码操作(字符串 -> 字节数组)
         * 传入一个需要进行编码操作的字符缓冲区、返回一个字节缓冲区
         */
        ByteBuffer bBuf = ce.encode(charBuffer);
        //遍历
        for (int i = 0; i < bBuf.limit(); i++) {
            System.out.println(bBuf.get());
        }

        //通过当前解码器进行解码操作
        //将需要进行解码操作的字节缓冲区转换成读取模式
        bBuf.flip();
        //传入一个需要进行解码操作的字节缓冲区、返回一个字符缓冲区
        CharBuffer cBuf2 = cd.decode(bBuf);
        System.out.println(cBuf2.toString());
    }

    //支持的字符集
    public static void test5() {
        Map<String, Charset> map = Charset.availableCharsets();
        Set<Map.Entry<String, Charset>> set = map.entrySet();
        for (Map.Entry entry : set) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }
    }

    //分散读取和聚集写入
    public static void test4() throws Exception {
        /**
         * 创建一个随机访问文件
         * rw:读写模式
         */
        RandomAccessFile raf1 = new RandomAccessFile("d://分散读取.txt", "rw");
        //获取通道
        FileChannel channel1 = raf1.getChannel();
        //分配指定大小的缓冲区
        ByteBuffer buf1 = ByteBuffer.allocate(100);
        ByteBuffer buf2 = ByteBuffer.allocate(1024);

        ByteBuffer[] bufs = {buf1, buf2};
        //分散读取、传入一个 ByteBuffer数组
        //将通道中的数据分散读取到 bufs数组中去
        channel1.read(bufs);

        for (ByteBuffer byteBuffer : bufs) {
            //将 bufs数组中的 buf缓冲区切换到读取模式
            byteBuffer.flip();
        }
        //将 bufs数组中的缓冲区转换重字符串打印到控制台
        System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
        System.out.println("--------------------------------------------");
        System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));

        //聚集写入
        RandomAccessFile raf2 = new RandomAccessFile("d://聚集写入.txt", "rw");
        //获取通道
        FileChannel channel2 = raf2.getChannel();
        //聚集写入、将 bufs数组中的缓冲区数据写入到通道中
        channel2.write(bufs);
        //关闭
        channel2.close();
        channel1.close();
        raf2.close();
        raf1.close();
    }

    //通道之间的数据传输(直接缓冲区)
    public static void test3() throws Exception {
        FileChannel inChannel = FileChannel.open(Paths.get("d://卡比兽.png"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("d://憨憨.png"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

        //将 inChannel 通道的字节(从下标 0开始 inChannel.size()结束)传输到 outChannel通道
        inChannel.transferTo(0, inChannel.size(), outChannel);

        //关闭通道
        inChannel.close();
        outChannel.close();
    }

    //使用直接缓冲区完成文件的复制(内存映射文件)
    public static void test2() throws Exception {
        //创建一个通道、读取 d盘下的 卡比兽.png、StandardOpenOption.READ:读取模式(枚举)
        FileChannel inChannel = FileChannel.open(Paths.get("d://卡比兽.png"), StandardOpenOption.READ);
        /**
         * 创建一个通道、输出到 d盘下、名称为 憨憨.png;
         * StandardOpenOption.WRITE:写模式(枚举)
         * StandardOpenOption.CREATE:如果目标文件存在、则覆盖,不存在:则创建;
         * StandardOpenOption.CREATE_NEW:如果目标文件存在、则抛出异常,不存在:则创建;
         */
        FileChannel outChannel = FileChannel.open(Paths.get("d://憨憨.png"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

        //内存映射文件
        MappedByteBuffer inMapBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMapBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());

        //直接对缓冲区的数据进行读写操作
        byte[] dst = new byte[inMapBuf.limit()];
        //将 inMapBuf 内存映射文件中的数据读取到 dst字节数组中去
        inMapBuf.get(dst);
        //将 dst字节数组中的数据 写入到 outMapBuf 内存映射文件中去
        outMapBuf.put(dst);

        //关闭通道
        outChannel.close();
        inChannel.close();
    }

    //利用通道完成文件的复制(非直接缓冲区)
    public static void test1() throws Exception {
        //创建一个文件输入流、读取 d盘下的 卡比兽.png
        FileInputStream fls = new FileInputStream("d://卡比兽.png");
        //创建一个文件输出流、写出到 d盘下、名称为 憨憨.png
        FileOutputStream fos = new FileOutputStream("d://憨憨.png");

        //①、获取通道
        FileChannel inChannel = fls.getChannel();
        FileChannel outChannel = fos.getChannel();

        //②、分配一个指定大小的非直接缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //③、将通道 inChannel 中的数据存入到 buf 缓冲区中( != -1:代表当前通道获取到了数据)
        while (inChannel.read(buf) != -1) {
            //将缓冲区切换到读取数据的模式
            buf.flip();
            //④、将缓冲区中的数据写入通道
            outChannel.write(buf);
            //清空 buf 缓冲区
            buf.clear();
        }

        //关闭通道
        outChannel.close();
        inChannel.close();
        //关闭流
        fos.close();
        fls.close();
    }
}

 

客户端与服务端交互(阻塞式)

import java.net.InetSocketAddress;
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、负责连接;
 *  2、缓冲区:Buffer、负责数据的存储;
 *  3、选择器:Selector、是SelectableChannel的多路复用器,用于监控 SelectableChannel 的IO状况;
 */
public class TestBlockingClient {
    public static void main(String[] args)throws Exception{
        client();
    }

    //客户端
    public static void client() throws Exception {
        System.out.println("------client start------");
        //1、获取通道
        //获取一个Socket通道、用来向服务端发送数据、传入一个 ip地址和 端口
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
        //获取一个文件通道、读取 d 盘下的 卡比兽.png、读取模式
        FileChannel inChannel = FileChannel.open(Paths.get("d://卡比兽.png"), StandardOpenOption.READ);

        //2、分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //3、读取本地文件、并发送到服务器
        //从 inChannel 通道中读取数据、若等于 -1则代表未读取到数据
        while (inChannel.read(buf) != -1){
            //将缓冲区转换成读取模式
            buf.flip();
            //将缓冲区的数据
            sChannel.write(buf);
            buf.clear();
        }

        //关闭连接以进行写入,而不关闭通道。
        sChannel.shutdownOutput();

        //接收服务端的反馈
        int len = 0;
        while ((len = sChannel.read(buf)) != -1){
            buf.flip();
            System.out.println(new String(buf.array(),0,len));
        }

        //4、关闭通道
        inChannel.close();
        sChannel.close();

        System.out.println("------client end------");
    }
}

class TestBlockingServer{
    public static void main(String[] args)throws Exception {
        server();
    }
    //服务端
    public static void server()throws Exception{
        System.out.println("-----server start-------");
        //1、获取通道
        //获取一个Socket通道、用来接收客户端发送的数据
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        //将该通道绑定 9898 端口
        ssChannel.bind(new InetSocketAddress(9898));
        //获取客户端连接的通道
        SocketChannel sChannel = ssChannel.accept();
        //获取一个文件通道、将数据写入到 d 盘下 憨憨.png
        FileChannel outChannel = FileChannel.open(Paths.get("d://憨憨.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        //2、分配一直指定的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //3、接收客户端的数据
        //将客户端通道中的数据读取到缓冲区中
        while (sChannel.read(buf) != -1){
            //将缓冲区转换为读取模式
            buf.flip();
            //将缓冲区中的数据写入到 outChannel中
            outChannel.write(buf);
            //清空缓冲区
            buf.clear();
        }

        //发送反馈给客户端
        buf.put("服务器接收数据成功".getBytes());
        buf.flip();
        sChannel.write(buf);

        //4、关闭通道
        sChannel.close();
        outChannel.close();
        ssChannel.close();

        System.out.println("-----server end-------");
    }
}

 

客户端与服务端交互(非阻塞式)SocketChannel(TCP)

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

/**
 * 非阻塞式
 * SocketChannel 使用的是 TCP协议
 * 一、使用 NIO完成网络通信的三个核心
 * 1、通道:Channel、负责连接;
 * 2、缓冲区:Buffer、负责数据的存储;
 * 3、选择器:Selector、是SelectableChannel的多路复用器,用于监控 SelectableChannel 的IO状况;
 */
public class TestNonBlockClient {
    public static void main(String[] args) throws Exception {
        client();
    }

    public static void client() throws Exception {
        //1、获取通道、该通道用于连接客户端与服务端
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
        //2、设置当前通道为非阻塞模式、false:非阻塞、true:阻塞
        sChannel.configureBlocking(false);
        //3、分配一个指定的缓冲区大小
        ByteBuffer buf = ByteBuffer.allocate(1024);
        //5、发送数据给服务器
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String next = scanner.nextLine();
            //将数据存入到缓冲区中
            buf.put((new Date().toString() + "\n" + next).getBytes());
            buf.flip();
            //读取缓冲区的数据并写入到 sChannel通道中
            sChannel.write(buf);
            buf.clear();
        }
        //6、关闭通道
        sChannel.close();
    }
}

class TestNonBlockServer {
    public static void main(String[] args) throws Exception {
        server();
    }

    public static void server() throws Exception {
        //1、获取通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        //2、切换到非阻塞模式
        ssChannel.configureBlocking(false);
        //3、绑定连接
        ssChannel.bind(new InetSocketAddress(9898));
        //4、获取选择器
        Selector selector = Selector.open();
        /**
         * 5、将通道注册到选择器上、并且指定 “监听接收事件”
         * selector:选择器
         *
         * SelectionKey的四个静态常量
         * static final int OP_ACCEPT:操作集位用于插座接受操作。(接收)
         * static final int OP_CONNECT:用于套接字连接操作的操作集位。(连接)
         * static final int OP_READ:读操作的操作位。(读)
         * static final int OP_WRITE:写操作的操作位。(写)
         */
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        //6、轮询的获取选择器上已经“准备就绪”的事件
        while (selector.select() > 0) {
            //7、获取当前选择器中所有注册的选择键(已就绪的监听事件)
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                //8、获取 “准备就绪”的事件
                SelectionKey sk = iterator.next();
                //9、判断具体是什么事件
                //如果当前是接收事件
                if (sk.isAcceptable()) {
                    //10、若 “接收就绪”,则获取客户端连接
                    SocketChannel sChannel = ssChannel.accept();
                    //11、将客户端通道切换成非阻塞模式
                    sChannel.configureBlocking(false);
                    //12、将客户端通道注册到选择器上、并监控 “读数据” 操作
                    sChannel.register(selector, SelectionKey.OP_READ);
                    //如果当前是 读取事件
                } else if (sk.isReadable()) {
                    //13、获取当前选择器上 “读就绪” 状态的通道
                    SocketChannel sChannel = (SocketChannel) sk.channel();
                    //分配一个缓冲区
                    ByteBuffer buf = ByteBuffer.allocate(1024);
                    //14、读取数据
                    int len = 0;
                    /**
                     * 将当前客户端通道中的数据读取到 buf 缓冲区中
                     * 当前若 sChannel.read(buf) == -1 :当前未读取到数据
                     * 当前若 sChannel.read(buf) == 0 :当前读取到了数据、数据为 “”
                     */
                    while ((len = sChannel.read(buf)) > 0) {
                        //避免当前读取的数据为 “”
                        buf.flip();
                        System.out.println(new String(buf.array(), 0, len));
                        buf.clear();
                    }
                }
                //15、取消选择键
                iterator.remove();
            }
        }
    }
}

 

客户端与服务端交互(非阻塞式)DatagramChannel(UDP)

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

/**
 * 非阻塞式
 * DatagramChannel 是一个能收发 UDP 包的通道
 */
public class TestNonBlockSend {
    public static void main(String[] args) throws Exception {
        send();
    }

    //发送端
    public static void send() throws Exception {
        //创建一个通道
        DatagramChannel dc = DatagramChannel.open();
        //转换为 非阻塞 模式
        dc.configureBlocking(false);
        //创建一个缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        //监听键盘信息输入
        Scanner scanner = new Scanner(System.in);
        //如果输入了信息
        while (scanner.hasNext()) {
            //获取输入的一行信息
            String nextLine = scanner.nextLine();
            //将当前输入的信息存入到缓冲区中
            buf.put((new Date().toString() + "\n" + nextLine).getBytes());
            //将缓冲区转换成读取模式
            buf.flip();
            //使用 DatagramChannel通道将信息发送指定的目的地
            dc.send(buf, new InetSocketAddress("127.0.0.1", 9898));
            buf.clear();
        }
        //关闭通道
        dc.close();
    }

}

class TestNonBlockReceive {
    public static void main(String[] args) throws Exception {
        receive();
    }

    //接收端
    public static void receive() throws Exception {
        //1、获取通道
        DatagramChannel dc = DatagramChannel.open();
        //2、切换到非阻塞模式
        dc.configureBlocking(false);
        //3、绑定连接
        dc.bind(new InetSocketAddress(9898));
        //4、获取选择器
        Selector selector = Selector.open();
        //5、将通道注册到选择器上、并且指定 “读取事件”
        dc.register(selector, SelectionKey.OP_READ);
        //6、轮询的获取选择器上已经“准备就绪”的事件
        while (selector.select() > 0) {
            //7、获取当前选择器中所有注册的选择键(已就绪的监听事件)
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                //8、获取 “准备就绪”的事件
                SelectionKey sk = iterator.next();
                if (sk.isReadable()) {
                    //9、创建一个缓冲区
                    ByteBuffer buf = ByteBuffer.allocate(1024);
                    //10、将当前接收的数据存入到缓冲区红
                    dc.receive(buf);
                    //将缓冲区转换为读取模式
                    buf.flip();
                    System.out.println(new String(buf.array(),0,buf.limit()));
                    buf.clear();
                }
            }
            //11、取消选择键
            iterator.remove();
        }
    }
}

 

 

import java.nio.ByteBuffer;
import java.nio.channels.Pipe;

/**
 * java nio 管道中是两个线程之间的单向数据连接,Pipe有source、sink两个通道;
 *          数据会被写入到 sink通道中、从source通道中读取
 */
public class TestPipe {
    public static void main(String[] args)throws Exception {
        test();
    }

    public static void test()throws Exception {
        //1、获取管道
        Pipe pipe = Pipe.open();
        //创建一个缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        buf.put("通过单向管道发送数据".getBytes());
        //2、将缓冲区的数据写入到管道中
        //获取 Pipe 中的 sink通道
        Pipe.SinkChannel sink = pipe.sink();
        //将缓冲区转换为读取模式
        buf.flip();
        //将缓冲区的数据写入 sink 通道中
        sink.write(buf);

        //3、读取缓冲区中的数据
        //获取 Pipe 中的 source通道
        Pipe.SourceChannel source = pipe.source();
        buf.flip();
        //将source通道中读取数据到缓冲区中
        source.read(buf);
        System.out.println(new String(buf.array(),0,buf.limit()));

        source.close();
        sink.close();
    }
}

 

posted @ 2021-05-20 16:48  DHaiLin  阅读(53)  评论(0编辑  收藏  举报