从java套接字读取字节:获取ArrayIndexOutOfBounds

时间:2011-07-25 13:35:24

标签: java networking stream

我有这个非常奇怪的问题:我有一个小程序从套接字读取字节; 每当我调试时,程序运行正常;但每次我运行它(如直接运行它),我得到ArrayIndexOutOfBounds异常。是什么赋予了?我读的插座太快了吗?我错过了什么吗?

这是main():

public static void main(String[] args){

    TParser p = new TParser();

    p.init();

    p.readPacket(); 

    p.sendResponse();

    p.readPacket();

    p.sendResponse();

    p.shutdown();

}

方法init是我创建用于读写的套接字的地方; 下一个方法(readPacket)是问题开始出现的地方;我将整个缓冲区读取到一个私有字节数组,以便我可以自由地操作数据;例如,根据数据上的一些字节,我设置了一些属性:

public void readPacket(){       

    System.out.println("readPacket");
    readInternalPacket();
    setPacketInfo();
}

private void readInternalPacket(){
    System.out.println("readInternalPacket");
    try {           
        int available=dataIN.available();           
        packet= new byte[available];    
        dataIN.read(packet,0,available);

        dataPacketSize=available;

    }
    catch (Exception e) {
        e.printStackTrace();
    }
}


private void setPacketInfo() {

    System.out.println("setPacketInfo");
    System.out.println("packetLen: " +dataPacketSize);

    byte[] pkt= new byte[2];
    pkt[0]= packet[0];
    pkt[1]= packet[1];

    String type= toHex(pkt);
    System.out.println("packet type: "+type);
    if(type.equalsIgnoreCase("000F")){
        recordCount=0;
        packetIterator=0;
        packetType=Constants.PacketType.ACKPacket;
        readIMEI();
        validateDevice();

    }
}

它破坏的行是

pkt [1] =数据包[1]; (setPacketInfo)

意味着它当时只有1个字节...但是怎么可能,如果我调试它运行完美?我必须在插座上做一些健全检查吗? (dataIN的类型为DataInputStream)

我应该把方法放在不同的线程上吗?我已经一遍又一遍地重复这一点,甚至更换了我的内存模块(当我开始有这个奇怪的想法时)

...请帮帮我。

5 个答案:

答案 0 :(得分:4)

我不知道周围的代码,特别是dataIN的类,但我认为你的代码是这样的:

int available=dataIN.available();根本不等待数据,只返回有0个可用字节

所以您的数组大小为0,然后执行:

pkt[0]= packet[0]; pkt[1]= packet[1];超出范围。

我建议您至少循环直到available()返回您期望的2,但我无法确定这是正确的(*)还是正确的(**)方式因为我不知道dataIN的类实现。

注意:(*)如果available()可以例如,则不正确分别返回2个字节。 (**)如果dataIN本身提供等待的方法,则不是正确的方法。

答案 1 :(得分:1)

从套接字读取数据是不是一个异步过程,并且在数据包[]完全填满之前调用setPacketInfo()?如果是这种情况,它可能在调试时运行良好,但在真正使用不同机器上的套接字时非常糟糕。

您可以在setPacketInfo()方法中添加一些代码来检查packet []变量的长度。

 byte[] pkt= new byte[packet.length];
 for(int x = 0; x < packet.length; x++)
 {
      pkt[x]= packet[x];
 }

不确定为什么你甚至将packet []变量复制到pkt []?

答案 2 :(得分:0)

您永远不应该依赖dataIN.available(),而dataIN.read(packet,0,available);会返回一个整数,表示您收到的字节数。这并不总是与可用的值相同,也可能小于缓冲区的大小。

您应该阅读以下内容:

byte[] packet = new byte[1024];  //
dataPacketSize = dataIN.read(packet,0,packet.length);

你还应该将DataInputStream包装在BufferedInputStream中,并处理少于2个字节的情况,这样你就不会尝试处理你没有的字节接收。

答案 3 :(得分:0)

添加@eznme的答案。您需要从基础流中读取,直到没有更多待处理数据。这可能需要一次或多次读取,但当 available 方法返回0时,将指示流的结尾。我建议使用Apache IOUtils将输入流'复制'到{{ 3}},然后从那里获取byte []数组。

setPacketInfo 方法中,您应该在获取协议头字节之前检查数据缓冲区长度:

byte[] pkt= new byte[2];
if((packet != null) && (packet.length >= 2)) {
    pkt[0]= packet[0];
    pkt[1]= packet[1];
    // ...
}

当您从协议中读取零长度数据缓冲区时,这将消除您获得的超出范围的异常。

答案 4 :(得分:0)

您正在面向流的层上使用面向数据包的协议,而不传输实际数据包长度。由于碎片,接收数据的大小可能小于您发送的数据包。

因此我强烈建议在发送实际数据包之前发送数据包大小。在接收方,您可以使用DataInputStream并使用阻塞读取来检测传入的数据包:

private void readInternalPacket() {
    System.out.println("readInternalPacket");
    try {
        int packetSize = dataIN.readInt();
        packet = new byte[packetSize];
        dataIN.read(packet, 0, packetSize);
        dataPacketSize = packetSize;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

当然,您还必须修改发送方,在数据包之前发送数据包大小。