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

我在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;
}

14个回答

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

// 读取实际的对象内容
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;
                }
            }

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

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

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

[quote]W个字节不是一次性一个包通过网络的,有些己进入网络,有些数据还在发送方的网卡buffer中[/quote]
确实是这个样子。
我提一个建议,你可以试一试jboss下面的Netty。Netty对于我们来说屏蔽了流的读取和异常处理。你所需要做的仅仅是对在客户端或者服务器端对接受或者发送的数据进行加码或者解码,不需要编写流的写入写出部分的代码

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

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

编辑功能都没有么?

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

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

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

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

共14条数据 1 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问