输入流当中的read方法和readfully方法的区别与原理 -- 一直纠结的socket传输大图片解决的问题所在

在做一个通过 DataInputStream in, DataOutputStream   out 读入写出传输图片文件时 遇到了各种错误。终于在调试N久之后给弄出来了。

做一个总结,纪念一下:

1、起初通过自己的网络框架,利用gson图个方便,因此写了一个图片的类Picture,然后像通过设置一个byte数组的属性然后利用gson

     完美独好的 把类转换为json格式的字符串 传过去,然后在另外一边又利用gson直接生成这个类型,我就不必操心它怎么个传法了。

     可是现实是残酷的,世上还真没有那么好的事儿,一运行就各种bug。总结了一下大概是这样:

      a、使用dataInputstream和dataOutstream的readUTF 和 writeUTF 的时候对字符串的长度是有限制的,因此在使用这个传输一个

几十kb的图片的时候就会报错了。更别说我想传输几百kb的图片 = =!

b、使用gson的来读写object的时候同样也是这个问题,gson能解析的字符串的长度也是有限制的。要解析这个庞大的东东,

      想想也是不可能,还是算了吧 ~ ~!

        后来就改呗,既然上层一点的不能完成,我就在下层下工夫:

        于是就直接用 输入,输出流 对 byte数组 进行操作,然后返回这个byte数组就ok了。

         于是利用 out 先写出一个要求发送的数组的总长度,然后在另外一边先接受这个int之后在声明一个该大小的数组。

         eg:int dataSize = dis.readInt();
byte[] dataSource = new byte[dataSize];

可是遗憾的是java报了 heapOutofMemoryException。当传输图片长度过大了的话。

           难道就过不去了么?我查了网上资料说要改设置一个环境变量参数来更改heap的总大小。但是我这个是要在android

           手机上用的呢,那怎么改。于是换个思路。既然你不让申请这么打的数组。那我就来个动态增长的,

           利用ByteArrayOutputStream, 然后声明一个小一点的缓冲数组:

      byte[] temp = new byte[bufferSize]; // 8192 == 8K

             然后就利用 dis 读一下,bos就写一些。ok这样就不报堆内存溢出的问题了。但是。。。。

dis.read(temp);
bos.write(temp);

     写出的那边发送的数据是完整的,但是接受方老是会报EOFException,而且莫名其妙的会读着读着就又从头开始读。

             而重头开始读的话又会读一个int,而这不是真正我写的int呢。这是为什么呢? 我尝试了好多方法。起初想着,干脆就

             直接把这个输入流传个ImageIO让它直接读来生成图片。这么不就解决了。哎~灵光一闪觉得自己太笨了。于是改网络

             框架。改后之后测试运行发现直接“瘫痪”在那里,发送端送出去了数据,但是接受端死在了那里。

                  期间还遇到一个网上有些人提到的问题。就是

while((len = dis.read(buffer) != -1)){
// do something
}

这个while是永远跳不出去的,因为len一直都不为-1.就算读到的结尾也一样。而应该利用available来测量一下是否还有数据读入

while((len = dis.read(buffer) != -1)){
// do something
if(dis.available() <= 0)
break;
}

aha。这样不就解决了问题了么。

               可和我利用的方法一样,当发送端的数据还在缓冲区时或者在路上,available返回也是0,跳出了循环,开始下一轮读,而这不是

       你想要的。原因是多种多样的,网络延迟啥的。

               说到这里也基本定位了问题的所在,就是那个输入流在还没有完成我想给它完成读入数量的byte时它就提前返回到初始态开始另一轮

               了,所以查一查有没有可以让它完成后才返回的方法。终于查到了解决办法的资料:

以下是一个博客上的二者的区别解释:收藏一下: http://blog.csdn.net/yangjingyuan/article/details/6151234

DataInputStream类中的read(byte[] b)和readFully(byte[] b)读取消息到底有什么区别呢?

下面让我来分析一下:

   1.其实read(byte[] b)方法和readFully(byte []b)都是利用InputStream中read()

方法,每次读取的也是一个字节,只是读取字节数组的方式不同,查询jdk中源代码发现

    2. read(byte[] b)方法实质是读取流上的字节直到流上没有字节为止,如果当声明的字节数组长度大于流上的数据长度时就提前返回,而readFully(byte[] b)方法是读取流上指定长度的字节数组,也就是说如果声明了长度为len的字节数组,readFully(byte[] b)方法只有读取len长度个字节的时候才返回,否则阻塞等待,如果超时,则会抛出异常 EOFException。

         3.那么当发送了长度为len的字节,那么为什么用read方法用户收不全呢,揪其原因我们发现消息在网络中传输是没那么理想的,我们发的那部分字节数组在传送过程中可能在接受信息方的缓存当中或者在传输线路,极端情况下可能在发送方的缓存当中,这样就不在流上,所以read方法提前返回了,这样就造成了各种错误。


         于是稍稍做一个调整:

          byte[] temp = new byte[dataSize - off];
 dis.readFully(temp);
 bos.write(temp);

这样就没问题了。300kb的图片一样发送无误。 几M 的就没测试过了。^ ^

你可能感兴趣的:(框架,socket,网络,测试,buffer,byte)