最近在学习NIO 时遇到一个问题,使用transferTo()方法和transferFrom()方法做零拷贝复制文件时数据丢失。

我想要完成这样一个测试,将d盘中一个centos镜像文件(CentOS-8.1.1911-x86_64-dvd1.iso)通过网络通信和NIO传输到d盘的server文件夹下,代码如下

客户端

//客户端代码

try {

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));

FileChannel fileChannel = FileChannel.open(Paths.get("D:\\vm\\centos\\CentOS-8.1.1911-x86_64-dvd1.iso"), StandardOpenOption.READ);

fileChannel.transferTo(0,fileChannel.size(),socketChannel);

fileChannel.close();

socketChannel.close();

} catch (IOException e) {

e.printStackTrace();

}

服务端

SocketChannel acceptChannel = null;

FileChannel fileChannel = null;

try {

ServerSocketChannel socketChannel = ServerSocketChannel.open();

socketChannel.bind(new InetSocketAddress(InetAddress.getByName("127.0.0.1"),9999));

while (true) {

acceptChannel = socketChannel.accept();

long start = System.currentTimeMillis();

ByteBuffer buffer = ByteBuffer.allocate(1024);

fileChannel = FileChannel.open(Paths.get("d:/server/testt.iso"), StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE);

while(acceptChannel.read(buffer)!= -1) {

buffer.flip();

fileChannel.write(buffer);

buffer.re();

}

acceptChannel.close();

fileChannel.close();

System.out.println("写入时间是:" + (System.currentTimeMillis() - start));

}

} catch (IOException e) {

e.printStackTrace();

}

镜像文件的大小在7G左右,但是传输后的文件大小只有8M。查看了方法的API注释如下

/* An attempt is made to read up to count bytes starting at

* the given position in this channel's file and write them to the

* target channel. An invocation of this method may or may not transfer

* all of the requested bytes; whether or not it does so depends upon the

* natures and states of the channels. Fewer than the requested number of

* bytes are transferred if this channel's file contains fewer than

* count bytes starting at the given position, or if the

* target channel is non-blocking and it has fewer than count

* bytes free in its output buffer.

*/

public abstract long transferTo(long position, long count,

WritableByteChannel target)

throws IOException;

大概的意思是此方法尝试将参数count长度的字节从参数position的位置开始 从此channel连接的文件中写入目标channel中,

但是引用此方法并不一定会传输所有需要的字节数,取决于这些通道的性质和状态。如果当前管道连接的文件包含的数据小于参数给出的大小或者目标管道是非阻塞的并且目标管道的输出缓冲区的可用空间不足,会导致小于参数count的数据传输。

这个注释是很坑人的,看了一直在思考是否目标管道有什么问题,然后找了好久都没找到,实际上并非如此。

查看transferTo()方法的源码,在传输数据之前会做一系列验证,其中有一个验证是这样的

int var8 = (int)Math.min(var3, 2147483647L);

其中var是我们传入的参数count,而var8是该方法内部实际运输的字节长度,当参数长度大于2147483647时,取这个数,转换完之后大概在2G左右,所有终于找到了问题原因。而解决这个问题的方法就是循环传输数据

代码如下:

try {

long start = System.currentTimeMillis();

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));

FileChannel fileChannel = FileChannel.open(Paths.get("D:\\vm\\centos\\CentOS-8.1.1911-x86_64-dvd1.iso"), StandardOpenOption.READ);

System.out.println("文件大小" + fileChannel.size());

long size = fileChannel.size();

long postion = 0 ;

while (size > 0) {

long count = fileChannel.transferTo(postion, size, socketChannel);

postion += count;

size -= count;

}

fileChannel.close();

socketChannel.close();

System.out.println("客户端文件传输时间:" + (System.currentTimeMillis() - start));

------------------------------------------分割线---------------------------------------------------------------

追加一个问题,我上面写道我通过SocketChannel传输后的数据只有8M左右,但是上面的源码中最终看到是限制是2G,这是为啥呢?

后来继续看了一下源码,发现我transferTo到SocketChannel这个类的对象中,最终调用的方式是下面这个

private long transferToTrustedChannel(long var1, long var3, WritableByteChannel var5) throws IOException {

boolean var6 = var5 instanceof SelChImpl;

if (!(var5 instanceof FileChannelImpl) && !var6) {

return -4L;

} else {

long var7 = var3;

while(var7 > 0L) {

long var9 = Math.min(var7, 8388608L);

try {

MappedByteBuffer var11 = this.map(MapMode.READ_ONLY, var1, var9);

try {

int var12 = var5.write(var11);

assert var12 >= 0;

var7 -= (long)var12;

if (var6) {

break;

}

assert var12 > 0;

var1 += (long)var12;

} finally {

unmap(var11);

}

} catch (ClosedByInterruptException var20) {

assert !var5.isOpen();

try {

this.close();

} catch (Throwable var18) {

var20.addSuppressed(var18);

}

throw var20;

} catch (IOException var21) {

if (var7 != var3) {

break;

}

throw var21;

}

}

return var3 - var7;

}

}

与上面的代码类似,通过long var9 = Math.min(var7, 8388608L); 这一句代码,将数据大小再一次限制了,所有最终单次传输的数据是8M左右。而解决方案与上面相同,循环写入数据就行了。问题到此完美解决了,终于可以安心的睡个好觉了。。

transferto方法的应用_使用NIO中的transferTo方法传输数据精度丢失的问题相关推荐

  1. java 重写方法 访问权限_为何Java中子类重写方法的访问权限不能低于父类中权限(内含里氏替换原则)...

    为何Java中子类重写方法的访问权限不能低于父类中权限 因为 向上转型及Java程序设计维护的原因 例: 假设一个父类A 拥有的方法public void setXXX(){}可以被其他任意对象调用这 ...

  2. python方法测试怀孕_在Python中测试私有方法(例外)

    在阅读了关于在Python中测试私有方法的内容之后,特别是在How do I unit test the methods in a method object?处引用了接受的答案,看来最好只测试公共接 ...

  3. python中构造方法和析构方法的区别_类的构造方法、析构方法、实例方法

    类的构造方法.析构方法.实例方法 上一个小节我们学习了 Python 的类属性和实例属性的知识点,这个小节我们还是会学习关于类的知识.这节课我们会学习类的构造方法.析构方法和实例方法. 1. 实例方法 ...

  4. python脚本怎么使用_在Python中使用next()方法操作文件的教程

    next()方法当一个文件被用作迭代器,典型例子是在一个循环中被使用,next()方法被反复调用.此方法返回下一个输入行,或引发StopIteration异常EOF时被命中. 与其它文件的方法,如Re ...

  5. java 中如何实现多进程_在Java中可以使用哪些方法来实现Java的多进程运行模式?...

    在Java中我们可以使用两种方法来实现这种要求.最简单的方法就是通过Runtime中的exec方法执行java classname.如果执行成功,这个方法返回一个Process对象,如果执行失败,将抛 ...

  6. java 调用祖父方法_在Java中调用祖父母方法:您不能

    java 调用祖父方法 在文章保护的重点中,我详细介绍了"受保护"如何扩展"包私有"访问. 我在那儿写道: 你能做的是 覆盖子类中的方法或 使用关键字super ...

  7. 本周数据与上周对比应如何表达_互联网运营中的数据分析方法

    在大数据分析和产品.运营优化方面,大数据分析方法是其核心,那么如何做好数据分析呢,今天我们来讲讲互联网运营中的数据分析方法. 1.细分分析 细分分析是分析的基础,单一维度下的指标数据的信息价值很低. ...

  8. vue 给标签添加data属性_在Vue中获取自定义属性方法:data-id的实例

    获取自定义属性的方法: 第一步:首先在标签上绑定上@click="getDateId(item.id)",并将属性值传到绑定的事件里面 第二步:在标签上继续绑定:date-id = ...

  9. python中构造方法和析构方法的区别_基于Python构造方法与析构方法的研究

    基于 Python 构造方法与析构方法的研究 林观德 [期刊名称] < <现代职业教育> > [年 ( 卷 ), 期] 2019(000)018 [摘要] Python 语言是 ...

最新文章

  1. springMVC之Interceptor拦截器
  2. 浅谈计算机硬件维护 论文,浅谈计算机硬件维护的论文(2)
  3. C++设计模式--命令模式(Command)
  4. VTK:Utilities之DataAnimationSubclass
  5. android 禁止Viewpager左右滑动功能
  6. Spark技术内幕: Task向Executor提交的源代码解析
  7. 马大为院士:科研人也得养家, 非升即走压力下,不得不做短平快的研究
  8. 人类遗传变异神库 | ClinVar数据库详解
  9. leetcode 删除链表的倒数第N个节点
  10. pages文件服务器地址,Pages怎么设置目录 Pages如何设置目录
  11. 压测学习总结(2)——Jmeter 基本知识入门
  12. SSIS [大容量插入任务] 找不到文件错误
  13. 草根最容易逆袭的地方就是互联网
  14. WPS Office 2012兼容全部Office格式,为办公带来了很多便利
  15. 巴特沃斯(Butterworth)滤波器 (1)
  16. 图书管理系统(C SQL)
  17. Excel 2010 SQL应用117 分组统计之GROUP BY 与First
  18. 计算机在材料化学中的应用大纲,材料化学-《材料研究方法》课程教学大纲
  19. 2017IT在线教育机构汇总
  20. BZOJ 4239 巴士走读

热门文章

  1. winpe下安装xp系统,提示找不到硬盘驱动器解决方案
  2. 小白学习freemark的过程(代码全贴+详细介绍)
  3. cocoscreater预制体prefab全攻略
  4. android计步器!五步搞定Android开发环境部署,附大厂真题面经
  5. 最新更新的 CVPR 2022 论文66篇
  6. Redis各版本特性汇总
  7. head注入(X-Forwarded-For)
  8. jquery隐藏和显示tr标签
  9. ROS联合webots实战案例(四)webots中使用激光雷达
  10. Winrar的几个常用功能以及一些使用技巧