0 0

关于java 不能完全读取socket二进制流的问题(数据量越大问题就越严重)30

我在java中用socket读取server端发过来的二进制流,从包头前面11个字节中读取包体的字节总数,然后再循环读取包体的字节数,但当server端的数据字节比较大时就不能完全读过来 ,如从server端发送了 3万个字节,这时client端程序就会不能完全读出server端的字节,可能只能读2万多个; 当字节数少于2000 以下时,就基本没有出现过问题。
我把程序贴出来,这段程序有什么问题么?接收大量的数据该如何处理为好?
想了很久,没想明白是怎么回事,望各位不吝赐教!!!

public class SocketService {

private static Socket socket;
private static InputStream is;
private static OutputStream os;
private static BufferedInputStream br;

public static byte[] getReceiveBytes(Catalog catalog,byte[] b) {
byte[] receive_body_byte = {};
try {
socket = new Socket(catalog.getPrivateTcpIp(), Integer.parseInt(catalog.getPrivateTcpIPPort()));

is = new DataInputStream(socket.getInputStream());
os = new DataOutputStream(socket.getOutputStream());
os.write(b);
br = new BufferedInputStream(is);

// 包体长度 packlength。
String packlength = "";

                            // 先收11个字节,函数解析出数据包长度
byte[] encry_byte = new byte[11]; 
int sizeHead = br.read(encry_byte);
for (int m = 0; m < sizeHead; m++) {
if (m > 6) {
packlength = packlength + Util.byte2HexStr(encry_byte[m]);
}
}
receive_body_byte = new byte[Integer.parseInt(packlength, 16)];
                             // 接收剩下的字节
int size = br.read(receive_body_byte);
//打印接收到的字节
System.out.println("\n-------接收到的字节总数是-------------- "+receive_body_byte.length);

os.flush();
is.close();
os.close();
br.close();
} catch (Exception e) {
                        System.out.print("**********Socket异常!!!!*********"+e.getMessage());
                        return null;
}

return receive_body_byte;
}

========= 上面的程序不知哪里有问题,于是我就又优化了一下,代码如下,但遇到大数据还是不能全读出来===================
 
           public class SocketService {

private static Socket socket;
private static InputStream is;
private static OutputStream os;
private static BufferedInputStream br;

public static byte[] getReceiveBytes(Catalog catalog,byte[] b) {
byte[] receive_body_byte = {};
try {
socket = new Socket(catalog.getPrivateTcpIp(), Integer.parseInt(catalog.getPrivateTcpIPPort()));

is = new DataInputStream(socket.getInputStream());
os = new DataOutputStream(socket.getOutputStream());
os.write(b);
br = new BufferedInputStream(is);

// 包体长度 packlength。
String packlength = "";

                            // 先收11个字节,函数解析出数据包长度
byte[] encry_byte = new byte[11]; 
int sizeHead = br.read(encry_byte);
for (int m = 0; m < sizeHead; m++) {
if (m > 6) {
packlength = packlength + Util.byte2HexStr(encry_byte[m]);
}
}
receive_body_byte = new byte[Integer.parseInt(packlength, 16)];
                    int size = 0
            //  分次取server端的数据,即每次从流中取1024个字节,不足1024就一次取过来
          while(true){
int a = receive_body_byte.length/1024;

if(a>=1){
int last=0;
Thread.sleep(280);
for(int i=0;i<a;i++){
//Thread.sleep(50);
byte[] btmp = new byte[1024];
br.read(btmp);
getBytes(receive_body_byte,i*1024,btmp);
last = last + i*1024;
Thread.sleep(100);
}
//然后取剩余不足1024个的字节
int aa = receive_body_byte.length%1024;
byte[] bmp = new byte[aa];
//Thread.sleep(200);
if(aa!=0){
br.read(bmp);
System.out.println(" aa------------"+aa);
p(bmp);
Thread.sleep(100);
getBytes(receive_body_byte,a*1024,bmp);
}
break;
}else{

br.read(receive_body_byte);
break;
}
}
//打印接收到的字节
System.out.println("\n-------接收到的字节总数是-------------- "+receive_body_byte.length);

os.flush();
is.close();
os.close();
br.close();
} catch (Exception e) {
                        System.out.print("**********Socket异常!!!!*********"+e.getMessage());
                        return null;
}

return receive_body_byte;
}

问题补充:
westice 写道
这种不能完全读取数据的问题我在win32Socket编程里遇到过,它有个recv()函数,调用这个函数是指定了接受数据长度的len_1,但是它并不读取len_1个数据,而是返回一个数len_2,len_2就是实际接收的长度,所以要自己判断一下再封装一个函数。你在看看Api文档,有没有这种问题。

=================
  谢谢你,但在java的inputstream中 只有read()这样的读法。上面我优化的分次读其实也是一个取实际长度的读法。

问题补充:
javafound 写道
3W个字节不是一次性一个包通过网络的,有些己进入网络,有些数据还在发送方的网卡buffer中,read(byte[])可能会提前返回,你可以:
1.发送时分包分段.
2.读的时候调用readFully方法,它内部实现不等同read(byte[]).
3.干脆就一个字节一个字节的读.

==============================
 
1  我也研究了好久,在google上找了不少文章,所以你可以看到我上面一段优化的代码: 就是考虑到不是一次性能取过来的,同时网络延迟,缓冲区会被覆盖等等因素,我才一次取1000个左右的字节,然后再适当调整取包的时间(即 Thread.sleep(200))。即使是这样也还是20-30% 的失败率
2   java socket 的inputstream 中没有readFully这个方法啊

问题补充:
补充一点 我遇到的这个java socket的问题,是手机客户端的问题,也就是说这个类是在手机客户端接收server端的字节。

  我试着如下的方法进行改进,但接收的速度还是慢。成功率比以往高了一些,但在数据量比较大时,还是有个别时候不能全部接收完。

ByteArrayOutputStream bos= new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = 0;
while (-1 != (len = br.read(buff))) {
bos.write(buff, 0, len);
  }

问题补充:
enet_java 写道
类似的问题,我也遇到过,我使用如下代码以后,就没有这个问题了,供你参考,

// 读取实际的对象内容
                buffer = new byte[objContentleng];
                int nIdx = 0;
                int nTotalLen = buffer.length;
                int nReadLen = 0;
               
                while (nIdx < nTotalLen)
                {
                    nReadLen = bis.read(buffer, nIdx, nTotalLen - nIdx);
                   
                    if (nReadLen > 0)
                    {
                        nIdx = nIdx + nReadLen;
                    }
                    else
                    {
                        break;
                    }
                }


===============================
    呵呵,谢谢你,经过测试和验证,以及再次查看api后,你这段代码是正确的,server端的-1 不一定能得到,因为网络等因素。 你这个循环读字节是个好方法。
再次感谢!  马上结贴!

问题补充:
潜心修炼 写道
引用
W个字节不是一次性一个包通过网络的,有些己进入网络,有些数据还在发送方的网卡buffer中

确实是这个样子。
我提一个建议,你可以试一试jboss下面的Netty。Netty对于我们来说屏蔽了流的读取和异常处理。你所需要做的仅仅是对在客户端或者服务器端对接受或者发送的数据进行加码或者解码,不需要编写流的写入写出部分的代码



=================

jboss下面的Netty  这个还没有用过,有相关的例子吗?

问题补充:
scamperdog 写道
JDK API中的说法
read(byte[] buffer)从输入流读取一些字节,保存到buffer中,返回读取的字节数。方法阻塞直到输入数据可用,遇到文件尾(返回-1),或扔出异常。

换言之,该方法不一定读取buffer.length个字节,尤其是对网络输入流而言。针对你的代码,举一个极端的例子
receive_body_byte = new byte[Integer.parseInt(packlength, 16)[color=red]*1000[/color]]; 
int size = br.read(receive_body_byte); //这个方法难道就永远不返回了?




=============================
  谢谢你,你说的很对,1 该方法不一定读取buffer.length个字节,  2 考虑到网络连接,缓冲等,可能-1 都收不到。

问题补充:
lcj325 写道
很诧异,为什么不用ObjectInput(Output)Stream来处理~


========================
  谢谢你, 我用循环已经解决了

问题补充:
gh_aiyz 写道
兄弟,TCP的数据包并不保证一次传过来,所以你需要获取长度之后,不停的循环,直到读到足够的数据位置。


=================
  你和  enet_java (资深程序员)  的想法是一样的,他给的是正解。
2010年11月25日 11:07

14个答案 按时间排序 按投票排序

0 0

采纳的答案

类似的问题,我也遇到过,我使用如下代码以后,就没有这个问题了,供你参考,

// 读取实际的对象内容
                buffer = new byte[objContentleng];
                int nIdx = 0;
                int nTotalLen = buffer.length;
                int nReadLen = 0;
               
                while (nIdx < nTotalLen)
                {
                    nReadLen = bis.read(buffer, nIdx, nTotalLen - nIdx);
                   
                    if (nReadLen > 0)
                    {
                        nIdx = nIdx + nReadLen;
                    }
                    else
                    {
                        break;
                    }
                }

2010年11月25日 16:40
0 0

首先要理解什么是Socket数据流,而不是数据段。

那个readFully在DataInputStream里

2010年12月01日 06:54
0 0

TCP应该会做流量控制,不至于是应用层没有读完就会往应用层填鸭。

2010年11月30日 19:05
0 0

说一下我的理解:既然你知道jdk的read(byte[])方法实际读取的字节数可能小于byte.length,那你的代码在逻辑上就确实都有那么点问题,就拿你取报文头字节的代码来说:

引用

// 包体长度 packlength。
String packlength = "";
// 先收11个字节,函数解析出数据包长度
byte[] encry_byte = new byte[11]; 
int sizeHead = br.read(encry_byte);
for (int m = 0; m < sizeHead; m++) {
if (m > 6) {
   packlength = packlength + Util.byte2HexStr(encry_byte[m]);
}
}


这段代码保证不了就读取了字节流的前11个字节吧,所以这里你应该设一个期望值11,反复的读直到读到11个字节了为止,当然因为要读的数据还很少,一般不会出现读不满11字节的情况存在,但是严谨上处理是应该这样的,那同样的道理,对后面报文体的内容的处理也应该是如此,就不多说了。
个人意见,仅供参考。

2010年11月30日 15:03
0 0

什么叫: 可能只能读2万多个?
读多少个字节你不知道啊?
你可以用循环读字节,看看它为什么没有读完就返回了。
自己项目里,我开一个socket同步N个文件到服务器上。

2010年11月29日 15:54
0 0

很诧异,为什么不用ObjectInput(Output)Stream来处理~
===========
汗,真有人用这个ObjectInput(Output)Stream?你是真不知道这里面的陷阱?
第一,所有参与方(服务器和客户端)都必须使用java,而且,所有人的java版本必须一致。。。

2010年11月29日 02:13
0 0

兄弟,TCP的数据包并不保证一次传过来,所以你需要获取长度之后,不停的循环,直到读到足够的数据位置。

2010年11月29日 02:10
0 0

很诧异,为什么不用ObjectInput(Output)Stream来处理~

2010年11月27日 19:52
0 0

编辑功能都没有么?

2010年11月26日 18:48
0 0

JDK API中的说法
read(byte[] buffer)从输入流读取一些字节,保存到buffer中,返回读取的字节数。方法阻塞直到输入数据可用,遇到文件尾(返回-1),或扔出异常。

换言之,该方法不一定读取buffer.length个字节,尤其是对网络输入流而言。针对你的代码,举一个极端的例子

receive_body_byte = new byte[Integer.parseInt(packlength, 16)[color=red]*1000[/color]]; 
int size = br.read(receive_body_byte); //这个方法难道就永远不返回了?

2010年11月26日 18:47
0 0

引用
W个字节不是一次性一个包通过网络的,有些己进入网络,有些数据还在发送方的网卡buffer中

确实是这个样子。
我提一个建议,你可以试一试jboss下面的Netty。Netty对于我们来说屏蔽了流的读取和异常处理。你所需要做的仅仅是对在客户端或者服务器端对接受或者发送的数据进行加码或者解码,不需要编写流的写入写出部分的代码

2010年11月25日 16:47
0 0

如下:
InputStream ins=socket.getInputStream();
DataInputStream ds=new DataInputStream(ins);
byte[] data=new byte[1W];
ds.readFully(data);

2010年11月25日 14:57
0 0

3W个字节不是一次性一个包通过网络的,有些己进入网络,有些数据还在发送方的网卡buffer中,read(byte[])可能会提前返回,你可以:
1.发送时分包分段.
2.读的时候调用readFully方法,它内部实现不等同read(byte[]).
3.干脆就一个字节一个字节的读.

2010年11月25日 12:41
0 0

这种不能完全读取数据的问题我在win32Socket编程里遇到过,它有个recv()函数,调用这个函数是指定了接受数据长度的len_1,但是它并不读取len_1个数据,而是返回一个数len_2,len_2就是实际接收的长度,所以要自己判断一下再封装一个函数。你在看看Api文档,有没有这种问题。

2010年11月25日 11:37

相关推荐

    java源码包---java 源码 大量 实例

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    JAVA上百实例源码以及开源项目

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    JAVA上百实例源码以及开源项目源代码

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    Java源码包100个设计实例.zip

    Java二进制IO类与文件复制操作实例.rar Java从压缩包中提取文件.rar Java从网络取得文件.rar Java仓库管理系统,Access数据库.rar Java仿Vista界面风格的登录窗口.rar Java仿千千静听音乐播放器源代码.rar Java企业...

    java源码包4

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    java源码包3

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    java源码包2

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java二进制IO类与文件复制操作实例 16个目标文件 内容索引:Java源码,初学实例,二进制,文件复制 Java二进制IO类与文件复制操作实例,好像是一本书的例子,源代码有的是独立运行的,与同目录下的其它代码文件互不联系...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Java二进制IO类与文件复制操作实例 16个目标文件 内容索引:Java源码,初学实例,二进制,文件复制 Java二进制IO类与文件复制操作实例,好像是一本书的例子,源代码有的是独立运行的,与同目录下的其它代码文件互不联系...

    fstrm:C中的帧流实现

    帧流是一种轻量级的二进制干净协议,它允许以最小的成帧开销(每个数据帧仅四个字节)传输任意编码的数据有效载荷序列。 帧流未指定数据帧的编码格式,并且可以与产生字节序列的任何数据序列化格式一起使用,例如,...

    你必须知道的495个C语言问题

    这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制结构域的对齐方式? 2.14 为什么sizeof返回的值大于结构大小的期望值,是不是尾部有填充? 2.15 如何确定域在结构中的字节偏移...

    《你必须知道的495个C语言问题》

    这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制结构域的对齐方式? 27  2.14 为什么sizeof返回的值大于结构大小的期望值,是不是尾部有填充? 28 2.15 如何确定域在结构中的...

    C语言FAQ 常见问题列表

    o 3.10 我的编译器在结构中留下了空洞, 这导致空间浪费而且无法与外部数据文件进行 "二进制" 读写。能否关掉填充, 或者控制结构域的对齐方式? o 3.11 为什么 sizeof 返回的值大于结构的期望值, 是不是尾部有填充? ...

    python cookbook(第3版)

    5.9 读取二进制数据到可变缓冲区中 5.10 内存映射的二进制文件 5.11 文件路径名的操作 5.12 测试文件是否存在 5.13 获取文件夹中的文件列表 5.14 忽略文件名编码 5.15 打印不合法的文件名 5.16 增加或改变已...

    Android C++高级编程:使用NDK_Onur Cinar, 于红PDF电子书下载 带书签目录 完整版

    6.2.1 二进制兼容性 136 6.2.2 提供了什么 136 6.2.3 缺什么 137 6.3 内存管理 137 6.3.1 内存分配 137 6.3.2 C语言的动态内存管理 138 6.3.3 C++的动态内存管理 139 6.4 标准文件I/O 141 6.4.1 标准流 141 ...

    你必须知道的495个C语言问题(PDF)

    部数据文件进行”二进制” 读写。能否关掉填充, 或者控制结构域 的对齐方式? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.11 为什么sizeof 返回的值大于结构的期望值, 是不是尾部有填充? . . ...

    8583报文解析框架Simple8583.zip

    6)通过Socket将数据发送并接受响应(读取前两个字节长度,根据长度获取其剩余报文),根据IsoPackage解析报文域,解析得到BitMap后根据BitMap对数据域进行解析,并将值都放入到对应的field中 7)将数据都放在Map...

Global site tag (gtag.js) - Google Analytics