当前位置: 首页 > news >正文

第一章Netty,NIO零拷贝详细实现代码

基于前文对 ‌NIO 零拷贝原理‌(sendfile 与 mmap)及‌高并发场景‌的讨论,以下是两种核心零拷贝技术的详细 Java 实现代码。

先看一段传统IO模型的文件拷贝代码:

packagecom.example.demo;importjava.io.File;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.net.Socket;publicclassFileNioTest{publicstaticvoidmain(String[]args)throwsIOException{Filef=newFile("netty.txt");RandomAccessFilefile=newRandomAccessFile(f,"r");byte[]buf=newbyte[(int)f.length()];intread=file.read(buf);Socketsocket=newSocket();socket.getOutputStream().write(buf);}}

传统IO工作流程:



一、 基于 transferTo 的文件网络传输(推荐)

这是最纯粹的零拷贝,适用于‌静态资源服务器‌或‌大文件上传/下载‌。数据直接在内核态从 Page Cache 传输到 Socket Buffer,不经过用户态。

1. 服务端代码(发送文件)

importjava.io.FileInputStream;importjava.net.InetSocketAddress;importjava.nio.channels.FileChannel;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;publicclassZeroCopyServer{publicstaticvoidmain(String[]args)throwsException{// 1. 创建 ServerSocketChannelServerSocketChannelserverSocketChannel=ServerSocketChannel.open();serverSocketChannel.bind(newInetSocketAddress(8080));System.out.println("Server started on port 8080...");while(true){// 2. 接受连接SocketChannelsocketChannel=serverSocketChannel.accept();// 3. 获取文件通道FileChannelfileChannel=newFileInputStream("large_file.dat").getChannel();// 4. 【核心】零拷贝传输// transferTo 底层调用 sendfile,数据不经过用户空间longtransferred=fileChannel.transferTo(0,fileChannel.size(),socketChannel);System.out.println("Total bytes transferred: "+transferred);// 5. 关闭资源fileChannel.close();socketChannel.close();}}}

2. 客户端代码(接收文件)

importjava.io.FileOutputStream;importjava.net.InetSocketAddress;importjava.nio.channels.FileChannel;importjava.nio.channels.SocketChannel;publicclassZeroCopyClient{publicstaticvoidmain(String[]args)throwsException{// 1. 连接服务器SocketChannelsocketChannel=SocketChannel.open();socketChannel.connect(newInetSocketAddress("localhost",8080));// 2. 获取文件输出通道FileChannelfileChannel=newFileOutputStream("received_file.dat").getChannel();// 3. 读取数据并写入文件// 注意:客户端接收通常仍涉及一次从内核到用户空间的拷贝(除非使用 DirectBuffer + write)// 但服务端发送已实现零拷贝,整体性能显著提升longbytesRead=fileChannel.transferFrom(socketChannel,0,Long.MAX_VALUE);System.out.println("Total bytes received: "+bytesRead);// 4. 关闭资源fileChannel.close();socketChannel.close();}}

二、 基于 MappedByteBuffer 的大文件随机读写

适用于‌数据库索引‌、‌日志分析‌等需要频繁随机访问大文件的场景。通过内存映射,避免传统 read/write 的系统调用开销。

1. 写入大文件示例

importjava.io.RandomAccessFile;importjava.nio.MappedByteBuffer;importjava.nio.channels.FileChannel;publicclassMappedFileWriter{publicstaticvoidmain(String[]args)throwsException{StringfilePath="mapped_large_file.dat";longfileSize=1024*1024*100;// 100MB// 1. 创建 RandomAccessFile 和 FileChannelRandomAccessFileraf=newRandomAccessFile(filePath,"rw");FileChannelchannel=raf.getChannel();// 2. 【核心】内存映射// MapMode.READ_WRITE: 读写模式// position: 0, size: fileSizeMappedByteBuffermappedBuffer=channel.map(FileChannel.MapMode.READ_WRITE,0,fileSize);// 3. 直接操作内存,如同操作数组for(inti=0;i<1024;i++){mappedBuffer.putInt(i*4,i);// 在偏移量 i*4 处写入整数 i}// 4. 强制刷新到磁盘(可选,确保数据持久化)mappedBuffer.force();System.out.println("Data written to mapped file.");// 5. 关闭资源channel.close();raf.close();}}

2. 读取大文件示例

importjava.io.RandomAccessFile;importjava.nio.MappedByteBuffer;importjava.nio.channels.FileChannel;publicclassMappedFileReader{publicstaticvoidmain(String[]args)throwsException{StringfilePath="mapped_large_file.dat";// 1. 创建 RandomAccessFile 和 FileChannelRandomAccessFileraf=newRandomAccessFile(filePath,"r");FileChannelchannel=raf.getChannel();// 2. 内存映射(只读模式)MappedByteBuffermappedBuffer=channel.map(FileChannel.MapMode.READ_ONLY,0,channel.size());// 3. 随机读取数据intvalue=mappedBuffer.getInt(0);// 读取偏移量 0 处的整数System.out.println("Value at offset 0: "+value);intvalueAt1024=mappedBuffer.getInt(1024*4);System.out.println("Value at offset 4096: "+valueAt1024);// 4. 关闭资源channel.close();raf.close();}}

三、 关键注意事项

‌资源关闭‌:FileChannel 和 SocketChannel 必须显式关闭,否则会导致文件描述符泄漏。
‌内存映射释放‌:MappedByteBuffer 占用的堆外内存不会立即被 GC 回收。若需手动释放,可借助 sun.misc.Cleaner(非官方 API,需谨慎使用)或通过反射调用 clean() 方法。
‌小文件性能‌:对于极小文件(< 1KB),零拷贝的系统调用开销可能高于传统 I/O,此时传统 read/write 可能更优。
‌平台差异‌:transferTo 在 Linux 下性能最佳(完整零拷贝),在 Windows 下可能退化为部分拷贝实现。

http://www.jsqmd.com/news/1116540/

相关文章:

  • 收放板机的“多面手”——一台设备如何适配多种板件规格
  • NAFNet图像恢复终极指南:如何用AI让模糊照片变清晰
  • 即触 AI PPT:从文档到PPT,用这个工具就够了
  • AI智能体与本地大模型集成:Hermes+Codex自动化工作流部署指南
  • ICM-42605运动追踪芯片在工业自动化中的应用
  • 6DoF IMU原理与应用:从硬件选型到数据融合
  • ICM-42605六轴IMU与PIC18F2515实现高精度姿态追踪
  • 企业AI搭建,别再当冤大头了
  • 不止优质板材,精细化施工才是机房防静电的核心关键
  • 双基站ISAC系统架构设计与6G感知通信技术解析
  • STM32L152ZD与MC74HC165A的工业级开关量采集方案
  • MySQL 读写分离延迟:读库不是主库的实时镜像
  • 如何快速安装和使用 Liberation Fonts:开源字体的完整指南 [特殊字符]
  • 互联网大厂 Java 求职面试:从 Spring Boot 到微服务架构的探索
  • E-Hentai下载器终极指南:5分钟学会漫画批量下载技巧
  • STM32F767ZG与KMX63传感器融合开发指南
  • TC78H653FTG与PIC18F45K80驱动直流有刷电机方案详解
  • 基于TC78H660FTG的高效直流电机驱动系统设计
  • Spring Boot测试自动配置:从原理到实战的完整指南
  • 【NASA级代码可信性认证实践】:AI审查如何通过ISO/IEC 25010质量模型验证?
  • AI自动化办公时代:个人办公提效的数字员工全分类,可操控电脑、跨软件自动干活
  • 告别命令行繁琐操作:Semaphore让Ansible与Terraform运维效率提升10倍的可视化平台
  • 高校生常用的AI论文工具是哪款?
  • 4-20mA电流环接收器设计与PIC24FJ128GA310实现
  • ICM-42688-P运动传感器与PIC18F4455微控制器的工业应用解析
  • 为什么选择easy-checker?openEuler社区开发者的效率提升秘诀
  • GitHubDesktop2Chinese:3分钟实现GitHub Desktop中文汉化的完整指南
  • ICM-42688-P与PIC18LF45K22在工业自动化中的应用
  • 面向高标准农田巡检:基于YOLO的无人机智能分析系统落地实战
  • Python爬虫经典案例第46篇:学术论文爬取——arXiv论文预印本采集实战