我有这个非常奇怪的问题:我有一个小程序从套接字读取字节; 每当我调试时,程序运行正常;但每次我运行它(如直接运行它),我得到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)
我应该把方法放在不同的线程上吗?我已经一遍又一遍地重复这一点,甚至更换了我的内存模块(当我开始有这个奇怪的想法时)
...请帮帮我。
答案 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();
}
}
当然,您还必须修改发送方,在数据包之前发送数据包大小。