Java的NIO编程-Channel

0x1 主要类型

在Java中有许多NIO Channel实现,本文只选最主要的四种Channel

  • FileChannel:文件通道,用于文件的数据读写
  • SocketChannel:套接字通道,用于Socket套接字TCP连接的数据读写。
  • ServerSocketChannel:服务器嵌套字通道(或服务器监听通道),允许我们监听TCP连接请求,为每个监听到的请求,创建一个SocketChannel套接字通道。
  • DatagramChannel:数据报通道,用于UDP协议的数据读写。

0x2 使用

FileChannel

读取通道数据

首先在本地创建文件/home/zheng/channeltest,在里面编写内容:hello,world!

public class ChannelTest {

    public static void main(String[] args) throws IOException {
        // 创建输入流
        File file = new File("/home/zheng/channeltest");
        FileInputStream fis = new FileInputStream(file);
        // 获取通道
        FileChannel fileChannel = fis.getChannel();
        // 创建缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        int length = -1;
        // 读取通道数据到缓冲区
        while ((length=fileChannel.read(byteBuffer))!=-1){
            System.out.println("缓冲区size:"+length);
        }
        fis.close();
        fileChannel.close();
        // 读取Buffer缓存数据
        byteBuffer.flip();
        StringBuilder str = new StringBuilder();
        while (byteBuffer.position()!=byteBuffer.limit()){
            char c = (char) byteBuffer.get();
            str.append(c);
        }
        System.out.println(str.toString());
    }
}

//输出
缓冲区size13
hello,world!

写入数据到通道

public class ChannelTest {

    public static void main(String[] args) throws IOException {
		// 创建输入流
        File file = new File("/home/zheng/channelOut");
        FileOutputStream fos = new FileOutputStream(file);
        // 获取通道
        FileChannel fileChannel = fos.getChannel();
        // 创建缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byte c = 'c';
        byte o = 'o';
        byte d = 'd';
        byte e = 'e';
        byteBuffer.put(c);
        byteBuffer.put(o);
        byteBuffer.put(d);
        byteBuffer.put(e);
        int length = 0;
        // 缓冲区转换为读模式
        byteBuffer.flip();
        // 向通道写入数据
        while ((length=fileChannel.write(byteBuffer))!=0){
            System.out.println("写入数据size:"+length);
        }
        //强制刷盘
        fileChannel.force(true);
        fileChannel.close();
    }
}

// 输出
写入数据size4

此时查看文件/home/zheng/channelOut,里面内容是:code

SocketChannel和ServerSocketChannel

  • 服务端

    public class SocketServer {
        public static void main(String[] args) throws IOException {
            // 打开ServerSocketChannel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(8989));
            // 设置为非阻塞式工作模式(io多路复用)
            serverSocketChannel.configureBlocking(false);
            while (true){
                // 等待客户端连接
                SocketChannel channel = serverSocketChannel.accept();
                if (channel!=null){
                    System.out.println("==================");
                    System.out.println("客户端地址:"+channel.getRemoteAddress());
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    // 读取通道的数据到缓冲区
                    channel.read(byteBuffer);
                    // 读取Buffer缓存数据
                    byteBuffer.flip();
                    StringBuilder str = new StringBuilder();
                    while (byteBuffer.position()!=byteBuffer.limit()){
                        char c = (char) byteBuffer.get();
                        str.append(c);
                    }
                    System.out.println("接收到消息:"+str.toString());
                }
            }
        }
    }
    
  • 客户端

    public class SocketClient {
        public static void main(String[] args) throws IOException, InterruptedException {
            // 打开SocketChannel
            SocketChannel channel = SocketChannel.open();
            // 设置为非阻塞式工作模式(io多路复用)
            channel.configureBlocking(false);
            // 连接
            channel.connect(new InetSocketAddress("127.0.0.1",8989));
            while (!channel.finishConnect()){
                System.out.println("连接中...");
                Thread.sleep(1000);
            }
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            byteBuffer.put((byte)'s');
            byteBuffer.flip();
            // 从缓冲区写入数据到通道
            channel.write(byteBuffer);
            channel.close();
        }
    }
    

先运行服务端,然后在运行客户端,此时服务端控制台打印出:

==================
客户端地址:/127.0.0.1:44728
接收到消息:s

DatagramChannel

  • 发送端

    public class DatagramClient {
        public static void main(String[] args) throws IOException {
            // 开启DatagramChannel
            DatagramChannel channel = DatagramChannel.open();
            // 设置为非阻塞式工作模式(io多路复用)
            channel.configureBlocking(false);
            // 创建缓存区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入要发送的消息:");
            while (scanner.hasNext()){
                String s = scanner.next();
                byteBuffer.put(s.getBytes());
                byteBuffer.flip();
                // 发送数据
                channel.send(byteBuffer,new InetSocketAddress("127.0.0.1",8989));
                byteBuffer.clear();
            }
            channel.close();
        }
    }
    
  • 接收端

    public class DatagramServer {
        public static void main(String[] args) throws IOException {
            // 开启DatagramChannel
            DatagramChannel serverChannel = DatagramChannel.open();
            // 设置为非阻塞式工作模式(io多路复用)
            serverChannel.configureBlocking(false);
            // 绑定监听地址
            serverChannel.bind(new InetSocketAddress("127.0.0.1",8989));
            // 创建缓存区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            while (true){
                // 接收消息并保存到缓存区
                serverChannel.receive(byteBuffer);
                byteBuffer.flip();
                if (byteBuffer.limit()!=0){
                    // 读取Buffer缓存数据
                    StringBuilder str = new StringBuilder();
                    while (byteBuffer.position()!=byteBuffer.limit()){
                        char c = (char) byteBuffer.get();
                        str.append(c);
                    }
                    System.out.println("接收到消息:"+str.toString());
                    byteBuffer.clear();
                }
                byteBuffer.clear();
            }
        }
    }
    

源码地址:https://github.com/GreyCode9/nio-demo