java--NIO详解--Buffer、channel与网络编程

一、buffer–缓冲区的基本使用

1.核心与基础用法

初始化缓冲区:XXXBuffer.allocate(int capacity)
数据加入缓冲区:buffer.put(byte[] data)
切换为读模式:byteBuffer.flip()
读取缓冲区中的数据:buffer.get(byte[] data)

代码示例:

package nio;

import org.junit.Test;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.SortedMap;

public class BufferTest {
    @Test  // buffer的基本使用
    public void nioBufferTest(){
        System.out.println("********初始化缓冲区********");
        ByteBuffer byteBuffer = ByteBuffer.allocate(512); // 初始化缓冲区
        System.out.println("capacity:"+byteBuffer.capacity());  // 容量
        System.out.println("limit:"+byteBuffer.limit());  // 界限,可操作容量
        System.out.println("position:"+byteBuffer.position());  // 正在操作的位置

        System.out.println("********加入数据********");
        byte[] bytes = "hello,nio".getBytes();
        byteBuffer.put(bytes); // 加入数据
        System.out.println("position:"+byteBuffer.position()); // position变化
        System.out.println("limit:"+byteBuffer.limit());  // 不变

        System.out.println("********读模式********");
        byteBuffer.flip(); // 切换为读模式
        System.out.println("limit:"+byteBuffer.limit());  // 加入数据字节数
        System.out.println("position:"+byteBuffer.position());  // 0

        System.out.println("********读取数据********");
        byte[] bytes1 = "1234567".getBytes();
        byteBuffer.get(bytes1);
        System.out.println(new String(bytes1));
        System.out.println("limit:"+byteBuffer.limit());  // 加入数据字节数
        System.out.println("position:"+byteBuffer.position());  // 已读取的总长度7
        byteBuffer.get("89".getBytes());
        System.out.println("limit:"+byteBuffer.limit());  // 加入数据字节数
        System.out.println("position:"+byteBuffer.position());  // 已读取的总长度7+2=9

        System.out.println("********重复读********");
        byteBuffer.rewind();  // 恢复到刚切换为读状态时的状态
        System.out.println("limit:"+byteBuffer.limit());  // 加入数据字节数
        System.out.println("position:"+byteBuffer.position());  // 0

        System.out.println("********清空********");
        byteBuffer.clear(); // 恢复到刚初始化时的状态
        System.out.println("limit:"+byteBuffer.limit());  // 加入数据字节数
        System.out.println("position:"+byteBuffer.position());  // 0
        byteBuffer.put("1234567".getBytes());

        System.out.println("********标记与reset********");
        byteBuffer.flip();
        byteBuffer.get(bytes1,0,4);
        System.out.println(new String(bytes1, 0, 4));
        System.out.println("position:"+byteBuffer.position());  // 0

        byteBuffer.mark();
        byteBuffer.get(bytes1,0,2);
        System.out.println(new String(bytes1, 0, 2));
        System.out.println("position:"+byteBuffer.position());  // 0
        byteBuffer.reset();
        System.out.println("position:"+byteBuffer.position());  // 0

        System.out.println(byteBuffer.hasRemaining()); // 是否还有未操作数量
        System.out.println(byteBuffer.remaining());  // 可操作数量
        // 打印所有未操作的
        if (byteBuffer.hasRemaining()) {
            int n = byteBuffer.remaining();
            byteBuffer.get(bytes,0,n);
            System.out.println(new String(bytes, 0, n));
        }
    }
}

2.实现字符集转换

 @Test // 字符集转换
    public void charsetTest() throws IOException {
        SortedMap<String, Charset> availableCharsets = Charset.availableCharsets();
        System.out.println("********可用字符集********");
        System.out.println(availableCharsets);

        System.out.println("****************");
        Charset gbk = Charset.forName("GBK");  // 通过名称获取字符集实例
        Charset utf8 = Charset.forName("utf-8");  // 通过名称获取字符集实例

        CharsetEncoder charsetEncoder = gbk.newEncoder();// 通过字符集实例获取编码器(编码:字符串-->字节数组)
        CharsetDecoder charsetDecoder = utf8.newDecoder(); // 通过字符集实例获取解码器(编码:字节数组-->字符串)

        CharBuffer charBuffer = CharBuffer.allocate(100);
        charBuffer.put("encode decode 字符转换");
        charBuffer.flip(); // 转换为读模式才可以正确执行下面的语句

        ByteBuffer byteBuffer = charsetEncoder.encode(charBuffer); // 将charBuffer转换为byteBuffer

        System.out.println("********ByteBuffer********");
        for (int i = 0; i < byteBuffer.limit(); i++) {
            System.out.print(byteBuffer.get() + " ");
        }
        System.out.println();

        System.out.println("********CharBuffer********");
        byteBuffer.flip();
        CharBuffer charBuffer1 = charsetDecoder.decode(byteBuffer); // 执行报错,不能转换为utf-8
        System.out.println(charBuffer1);
    }

二、Channel的基本使用

第一步:获取通道:

  • fileXXXStream.getChannel()
  • FileChannel fc = FileChannel.open(path,openOption)
    .

第二步:实例化一个/多个缓冲区,例如:
ByteBuffer bb= ByteBuffer.allocate(512);
.
第三步:数据读入通道读取数据并写入到缓冲区:
fc.read(bb);
.
第n步:用其他通道操作该缓冲区;
fcn.write(bb);

package nio;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class ChannelTest {
    // 通道用于源节点与目标节点的连接,不负责存储
    // FileChannel  SocketChannel  ServerSocketChannel  DatagramChannel


    @Test   获取通道方式一:getChannel()
    public void channelTest1()  {

        FileInputStream fileInputStream = null; // 文件输入流,读取文件
        FileOutputStream fileOutputStream = null; // 文件输出流
        FileChannel fileInputStreamChannel = null;  // 数据读入通道
        FileChannel fileOutputStreamChannel = null; // 数据写出通道
        try {
            fileInputStream = new FileInputStream("yellowBoy.jpg");
            fileOutputStream = new FileOutputStream("yb.jpg");

            fileInputStreamChannel = fileInputStream.getChannel();
            fileOutputStreamChannel = fileOutputStream.getChannel();

            ByteBuffer byteBuffer = ByteBuffer.allocate(512); //  缓冲区

            while (fileInputStreamChannel.read(byteBuffer) != -1) {  // 数据读入通道读取数据并写入到缓冲区
                byteBuffer.flip(); // 缓冲区读模式
                fileOutputStreamChannel.write(byteBuffer); // 数据写出通道写入缓冲区中的数据
                byteBuffer.clear(); // 清空数据,下次继续写入
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileInputStreamChannel != null) {
                    fileInputStreamChannel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fileOutputStreamChannel != null) {
                    fileOutputStreamChannel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fileOutputStream == null) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }



    }

    @Test   获取通道方式一:getChannel(),使用多个缓冲区
    public void channelTest2()  {
        FileInputStream fileInputStream = null; // 文件输入流,读取文件
        FileOutputStream fileOutputStream = null; // 文件输出流
        FileChannel fileInputStreamChannel = null;  // 数据读入通道
        FileChannel fileOutputStreamChannel = null; // 数据写出通道
        try {
            fileInputStream = new FileInputStream("channel.txt");
            fileOutputStream = new FileOutputStream("c.txt");

            fileInputStreamChannel = fileInputStream.getChannel();
            fileOutputStreamChannel = fileOutputStream.getChannel();

            ByteBuffer byteBuffer = ByteBuffer.allocate(10); //  缓冲区
            ByteBuffer byteBuffer1 = ByteBuffer.allocate(200); //  缓冲区
            ByteBuffer[] byteBuffers = {byteBuffer,byteBuffer1};

            while (fileInputStreamChannel.read(byteBuffers) != -1) {
                fileInputStreamChannel.read(byteBuffers);  // 读取数据按顺序写入到多个缓冲区(缓冲区为写模式)
                System.out.println(new String(byteBuffer.array()));
                byteBuffer.flip();  // 缓冲区切换为读模式
                byteBuffer1.flip();
                fileOutputStreamChannel.write(byteBuffers); // 从多个缓冲区读取数据写入到通道(缓冲区为读模式)
                byteBuffer.clear();
                byteBuffer1.clear();
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileInputStreamChannel != null) {
                    fileInputStreamChannel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fileOutputStreamChannel != null) {
                    fileOutputStreamChannel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fileOutputStream == null) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }



    }

    @Test  // FileChannel.open(),通过直接缓冲区
    public void channelTest3(){
        FileChannel readFileChannel = null;
        FileChannel writeFileChanel = null;
        try {
            // 初始化一个通道,参数一:java.nio.file.Paths.get(文件路径),参数二:OpenOption
            readFileChannel = FileChannel.open(Paths.get("yellowBoy.jpg"), StandardOpenOption.READ);
            long size = readFileChannel.size();
            writeFileChanel = FileChannel.open(Paths.get("yb.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);

            readFileChannel.transferTo(0,size,writeFileChanel);

            // 等价
            // writeFileChanel.transferFrom(readFileChannel,0,size);

            // 等价
            /*MappedByteBuffer readBuffer = readFileChannel.map(FileChannel.MapMode.READ_ONLY, 0,size);
            MappedByteBuffer writeBuffer = writeFileChanel.map(FileChannel.MapMode.READ_WRITE, 0, size);

            byte[] bytes = new byte[(int) size];
            readBuffer.get(bytes);
            writeBuffer.put(bytes);*/

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

三、socketChannel网络编程–阻塞式

package nio;

import org.junit.Test;

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

public class SocketChannelTest {
    @Test  // 使用socketChannel进行网络编程
    public void socketChannelClient() throws IOException {
        // 创建SocketChannel实例
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",33333));

        // FileChannel实例用于读取本地文件
        FileChannel fileChannel = FileChannel.open(Paths.get("yellowBoy.jpg"), StandardOpenOption.READ);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        // fileChannel读取数据到byteBuffer中
        while (fileChannel.read(byteBuffer)!=-1){
            byteBuffer.flip();
            // socketChannel读取byteBuffer中的数据并写入到socketChannel
            socketChannel.write(byteBuffer);
            byteBuffer.clear();
        }
        socketChannel.shutdownOutput();  // 告诉服务器输出完成,不然会阻塞

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

        socketChannel.close();
        fileChannel.close();

    }
    @Test
    public void socketChannelServer()  {
        ServerSocketChannel serverSocketChannel = null;
        FileChannel fileChannel = null;
        SocketChannel socketChannel = null;
        try {
            // 服务端专用ServerSocketChannel实例
            serverSocketChannel = ServerSocketChannel.open();

            // 跟客户端一个道理,用于接收到客户端的文件保存到本地
            fileChannel = FileChannel.open(Paths.get("boyY.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

            serverSocketChannel.bind(new InetSocketAddress(33333)); // 绑定端口,跟客户端的socketChannel同样的端口

            // 接收文件,生成socketChannel实例
            socketChannel = serverSocketChannel.accept();

            ByteBuffer byteBuffer = ByteBuffer.allocate(512);

            // 读取
            while(socketChannel.read(byteBuffer)!=-1){
                byteBuffer.flip();
                // 写入到本地
                fileChannel.write(byteBuffer);
                byteBuffer.clear();
            }
            // 发送反馈信息
            byteBuffer.put("收到了一张小黄人的图片".getBytes());
            byteBuffer.flip();
            socketChannel.write(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                serverSocketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

四、socketChannel网络编程–非阻塞式

1.Client客户端类

package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws IOException {
        SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1",33333));
        sc.configureBlocking(false); // 切换为非阻塞模式

        ByteBuffer bb = ByteBuffer.allocate(1024);
        Scanner scanner = new Scanner(System.in);
        System.out.println("input: ");
        while (!scanner.hasNext("exit")){  // 键盘写入,exit停止写入
            String str = scanner.next();
            bb.put(str.getBytes());  // 将写入的内容存储到缓冲区
            bb.flip(); // 读模式
            sc.write(bb); // 写入到Socket通道
            bb.clear();
            System.out.println("input: ");
        }
        System.out.println("已退出");
        scanner.close();
        sc.close();
    }
}

2.Server服务端

package nio;

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.Iterator;
import java.util.Set;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        ssc.bind(new InetSocketAddress(33333));

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

        ssc.register(selector,SelectionKey.OP_ACCEPT); // 注册,selector选择器监听ssc通道的accept事件

        //ssc.accept(); // ssc开始accept事件,并被selector监听

        while (selector.select() > 0){  // 获取selector上所有的就绪的事件个数
            // 获取所有selector上所有的就绪的事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()){
                // 一个就绪事件
                SelectionKey selectionKey = iterator.next();
                // 就绪事件为accept
                if(selectionKey.isAcceptable()){
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    sc.register(selector,SelectionKey.OP_READ);

                // 就绪事件为read
                }else if (selectionKey.isReadable()){
                    SocketChannel selectableChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    int len = 0;
                    while ((len = selectableChannel.read(byteBuffer)) > 0){
                        byteBuffer.flip();
                        System.out.println(new String(byteBuffer.array(),0,len));
                        byteBuffer.clear();
                    }
                }
            }
            iterator.remove();
        }
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

运维小菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值