NIO&使用NIO传输图片

NIO&使用NIO传输图片

码农世界 2024-05-15 前端 65 次浏览 0个评论

相比于传统的阻塞IO,NIO提供了一种更灵活和高效的 I/O 操作方式,NIO 提供的非阻塞式的 I/O 操作,使得一个单独的线程可以管理多个通道(Channel),从而更好地处理并发连接和大量的 I/O 操作。

1. 核心组件

NIO 的核心组件包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。

(1)通道(Channel)

  • 通道是 NIO 中用于读取和写入数据的抽象。它可以连接到文件、网络套接字等输入/输出设备。
  • Java NIO 提供了不同类型的通道,包括文件通道(FileChannel)、套接字通道(SocketChannel 和 ServerSocketChannel)、Datagram 通道(DatagramChannel)等。
  • 通道与传统的流(Stream)不同,通道可以双向传输数据,而流是单向的。

    (2) 缓冲区(Buffer)

    • 缓冲区是用于暂存数据的对象,它是 NIO 操作数据的基本单位。所有的数据读取和写入都是通过缓冲区进行的。
    • 缓冲区具有固定的容量,可以通过 put() 和 get() 等方法向其中写入或读取数据。
    • Java NIO 提供了不同类型的缓冲区,包括 ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer 和 DoubleBuffer,每种缓冲区用于不同类型的数据。

      (3) 选择器(Selector)

      • 选择器是 NIO 中用于多路复用的关键组件。它可以监听多个通道的事件,当一个或多个通道准备好进行读取或写入时,选择器将这些通道放入就绪集合(SelectionKey)中。
      • 选择器使得一个单独的线程可以有效地管理多个通道,提高了系统的性能和资源利用率。
      • 选择器是事件驱动模型的核心,它使得 NIO 可以实现非阻塞式的 I/O 操作。  

        2. 与传统 IO 的区别 

        (1)阻塞与非阻塞:

        • 传统的 IO 是阻塞式的,当一个 IO 操作开始后,线程会被阻塞,直到该操作完成。这意味着如果线程在进行 IO 操作时没有其他任务可执行,系统资源会被浪费。
        • NIO 使用非阻塞式 IO,意味着线程在进行 IO 操作时可以同时执行其他任务,从而提高了系统资源的利用率。

          (2)通道与流: 

          • 传统的 IO 使用字节流和字符流进行数据传输,而 NIO 使用通道(Channel)和缓冲区(Buffer)。
          • 通道可以双向传输数据,而流是单向的。通道提供了更多的控制和灵活性。

            (3)多路复用:

            • NIO 引入了选择器(Selector)的概念,可以同时监听多个通道的事件,当一个或多个通道准备好进行读取或写入时,选择器会通知程序。
            • 这使得一个线程可以有效地管理多个通道,处理并发连接和大量的 IO 操作,提高了系统的性能和可扩展性。

              3. NIO 的优势

              • 高并发处理:NIO 可以通过较少的线程处理更多的连接,提高了系统的性能和资源利用率。

              • 灵活的数据操作:NIO 提供了更灵活和高效的数据操作方式,可以更好地处理大量的 IO 操作。

                 使用NIO传输图片

                客户端代码

                import java.io.FileInputStream; // 导入文件输入流类,用于读取文件内容  
                import java.io.IOException;     // 导入I/O异常类,用于处理可能的文件读取和网络通信异常  
                import java.net.InetSocketAddress; // 导入网络地址类,用于指定服务器的IP地址和端口号  
                import java.nio.ByteBuffer;       // 导入ByteBuffer类,用于在通道中传输数据  
                import java.nio.channels.FileChannel; // 导入文件通道类,用于从文件中读取数据  
                import java.nio.channels.SocketChannel; // 导入Socket通道类,用于网络通信  
                  
                public class ImageClient {  
                  
                    // 主函数,程序的入口点  
                    public static void main(String[] args) throws IOException {  
                  
                        // 创建一个SocketChannel实例,并准备连接到服务器  
                        // SocketChannel是NIO(非阻塞I/O)中用于网络通信的通道  
                        SocketChannel socketChannel = SocketChannel.open();  
                  
                        // 连接到指定的服务器地址和端口,这里连接到本地机器(localhost)的8080端口  
                        // InetSocketAddress是一个网络地址的封装,包含了IP地址和端口号  
                        socketChannel.connect(new InetSocketAddress("localhost", 8080));  
                  
                        // 读取图片文件到ByteBuffer中  
                        // 使用FileInputStream来读取文件,然后通过getChannel()方法获取到文件的通道(FileChannel)  
                        FileInputStream fis = new FileInputStream("src/main/java/images/userAvatar.jpg");  
                        FileChannel fileChannel = fis.getChannel();  
                  
                        // 分配一个ByteBuffer,大小为1024字节,用于临时存储从文件中读取的数据  
                        // ByteBuffer是一个字节容器,可以通过它读取或写入数据到通道  
                        ByteBuffer buffer = ByteBuffer.allocate(1024);  
                  
                        // 循环读取文件内容,直到文件结束  
                        while (fileChannel.read(buffer) > 0) {  
                            // 调用flip()方法,将ByteBuffer从写模式切换到读模式  
                            // 在调用write()方法之前,必须先调用flip()方法  
                            buffer.flip();  
                  
                            // 将ByteBuffer中的数据写入到SocketChannel中,发送到服务器  
                            socketChannel.write(buffer);  
                  
                            // 调用clear()方法,清空ByteBuffer,准备下一次读取  
                            // clear()方法会将position设置为0,limit设置为capacity,为下一次读取或写入做准备  
                            buffer.clear();  
                        }  
                  
                        // 关闭FileInputStream和SocketChannel,释放资源  
                        fis.close();  
                        socketChannel.close();  
                  
                    }  
                }

                服务端代码

                import java.io.FileOutputStream; // 导入文件输出流类,用于将接收到的数据写入文件  
                import java.io.IOException;     // 导入I/O异常类,用于处理可能的文件写入和网络通信异常  
                import java.net.InetSocketAddress; // 导入网络地址类,用于指定服务器的IP地址和端口号  
                import java.nio.ByteBuffer;       // 导入ByteBuffer类,用于在通道中传输数据  
                import java.nio.channels.FileChannel; // 导入文件通道类,用于向文件中写入数据  
                import java.nio.channels.ServerSocketChannel; // 导入服务器套接字通道类,用于监听和接受连接  
                import java.nio.channels.SocketChannel; // 导入Socket通道类,用于网络通信  
                  
                public class ImageServer {  
                  
                    public static void main(String[] args) throws IOException {  
                        // 创建一个ServerSocketChannel实例,用于监听连接请求  
                        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  
                  
                        // 将ServerSocketChannel绑定到特定的端口,这里使用8080端口  
                        serverSocketChannel.bind(new InetSocketAddress(8080));  
                  
                        // 配置ServerSocketChannel为非阻塞模式,这允许服务器同时处理多个连接  
                        serverSocketChannel.configureBlocking(false);  
                  
                        // 无限循环,持续监听新的连接请求  
                        while (true) {  
                            // 尝试接受一个新的连接请求  
                            // 因为设置了非阻塞模式,如果当前没有连接请求,accept()会立即返回null  
                            SocketChannel socketChannel = serverSocketChannel.accept();  
                  
                            // 如果成功接受了一个连接请求,则进行以下操作  
                            if (socketChannel != null) {  
                                // 分配一个ByteBuffer,大小为1024字节,用于临时存储从SocketChannel中读取的数据  
                                ByteBuffer buffer = ByteBuffer.allocate(1024);  
                  
                                // 创建一个FileOutputStream,用于将接收到的数据写入到文件中  
                                // 这里假设文件名为"D:\\ltsServer\\text.jpg"  
                                FileOutputStream fos = new FileOutputStream("D:\\ltsServer\\text.jpg");  
                  
                                // 获取FileOutputStream对应的FileChannel  
                                FileChannel fileChannel = fos.getChannel();  
                  
                                // 读取SocketChannel中的数据,并写入到文件中,直到SocketChannel中没有更多数据可读  
                                int bytesRead;  
                                while ((bytesRead = socketChannel.read(buffer)) > 0) {  
                                    // 打印从SocketChannel中读取的字节数  
                                    System.out.println(bytesRead);  
                  
                                    // 调用flip()方法,将ByteBuffer从读模式切换到写模式  
                                    buffer.flip();  
                  
                                    // 将ByteBuffer中的数据写入到FileChannel中,即将数据写入到文件中  
                                    fileChannel.write(buffer);  
                  
                                    // 调用clear()方法,清空ByteBuffer,准备下一次读取  
                                    buffer.clear();  
                                }  
                  
                                // 关闭FileOutputStream和SocketChannel,释放资源  
                                fos.close();  
                                socketChannel.close();  
                            }  
                  
                            // 在实际应用中,对于非阻塞模式,通常需要使用选择器(Selector)来同时处理多个通道  
                            // 这里的代码只是简单示例,没有使用选择器  
                        }  
                    }  
                }

                这里再补充一段在服务端使用 选择器(Selector)监听通道的代码

                private static void start() throws IOException {
                        // 创建ServerSocketChannel,并设置为非阻塞模式
                        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
                        serverSocketChannel.configureBlocking(false);
                        serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
                        // 创建Selector,用于监听通道事件
                        Selector selector = Selector.open();
                        // 将ServerSocketChannel注册到Selector上,监听连接事件
                        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
                        // 不断循环,处理通道事件
                        while (true) {
                            selector.select(); // 阻塞,等待通道事件发生
                            for (SelectionKey key : selector.selectedKeys()) {
                                if (key.isAcceptable()) { // 客户端发起连接事件
                                    acceptClient(selector, serverSocketChannel);
                                } else if (key.isReadable()) { // 客户端发送数据事件
                                    readMessageFromClient(key);
                                }
                            }
                            selector.selectedKeys().clear(); // 清空已处理的事件集合
                        }
                    }

                 

                 

                 

转载请注明来自码农世界,本文标题:《NIO&使用NIO传输图片》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,65人围观)参与讨论

还没有评论,来说两句吧...

Top