从 java 套接字读取字节:获取 ArrayIndexOutOfBounds
我遇到了这个非常奇怪的问题:我有一个小程序可以从套接字读取字节; 每当我调试时,程序都运行良好;但每次我运行它(就像直接运行它),我都会收到 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]= packet[1] 行
; (setPacketInfo)
意味着它当时只有 1 个字节...但是那怎么可能,如果我调试它时它运行得完美呢?我必须对套接字进行一些健全性检查吗? (dataIN 的类型为 DataInputStream)
我应该将方法放在单独的线程上吗?我一遍又一遍地检查这个问题,甚至更换了我的内存模块(当我开始对此有奇怪的想法时)
......请帮助我。
am having this very strange problem: i have a small program that reads bytes off a socket;
whenever i am debugging, the program runs fine; but every time i run it (like straight up run it), i get the ArrayIndexOutOfBounds exception. what gives? am i reading it too fast for the socket? am i missing something?
here is the main():
public static void main(String[] args){
TParser p = new TParser();
p.init();
p.readPacket();
p.sendResponse();
p.readPacket();
p.sendResponse();
p.shutdown();
}
The method init is where i create the Sockets for reading and writing;
The next method (readPacket) is where problems start to arise; i read the entire buffer to a private byte array so i can manipulate the data freely; for instance, depending on some bytes on the data i set some properties:
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();
}
}
The line where it breaks is the line
pkt[1]= packet[1]; (setPacketInfo)
meaning it only has 1 byte at that time... but how can that be, if whe i debug it it runs perfectly? is there some sanity check i must do on the socket? (dataIN is of type DataInputStream)
should i put methods on separate threads? ive gone over this over and over, even replaced my memory modules (when i started having weird ideas on this)
...please help me.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我不知道周围的代码,尤其是
dataIN
的类,但我认为您的代码是这样做的:int available=dataIN.available();
根本不等待数据,仅返回有 0 个字节可用,因此您的数组大小为 0,然后执行:
pkt[0]= packet[0]; pkt[1]= packet[1]; 超出范围。
我建议您至少循环直到
available()
返回您期望的2
,但我无法确定这是正确的 (* ) 或正确的 (* * ) 的方式来做到这一点,因为我不知道 dataIN 的类实现。注意:(*) 如果
available()
可以单独返回 2 个字节,则这是不正确的。 (** ) 如果dataIN
本身提供等待方法,则不是正确的方法。I dont know the surrounding code, especially the class of
dataIN
but I think your code does this:int available=dataIN.available();
does not wait for data at all, just returns that there are 0 bytes availableso your array is of size 0 and you then do:
pkt[0]= packet[0]; pkt[1]= packet[1];
which is out of bounds.I would recommend that you at least loop until the
available()
returns the2
you expect, but i cannot be sure that that is the correct (* ) or right (** ) way to do it because i dont knowdataIN
's class-implementation.Notes: (* ) it is not correct if it is possible for
available()
to e.g. return the 2 bytes separately. (** ) it is not the right way to do it ifdataIN
itself provides methods that wait.难道从套接字读取数据是一个异步过程,并且在 packet[] 完全填满之前调用 setPacketInfo() 吗?如果是这种情况,它可能在调试时运行得很好,但当它真正在不同机器上使用套接字时运行得很糟糕。
您可以在 setPacketInfo() 方法中添加一些代码来检查 packet[] 变量的长度。
不太确定为什么你甚至将 packet[] 变量复制到 pkt[] 中?
Can it be that reading the data from the socket is an asynchronous process and the setPacketInfo() is called before your packet[] is completely filled? If this is the case, it's possible it runs great when debugging, but terrible when it really uses sockets on different machines.
You can add some code to the setPacketInfo() method to check the length of the packet[] variable.
not really sure though why you even copy the packet[] variable into pkt[]?
您在面向流的层上使用面向数据包的协议,而不传输真实的数据包长度。由于碎片,接收到的数据的大小可能小于您发送的数据包。
因此我强烈建议在发送实际数据包之前发送数据包大小。在接收方,您可以使用 DataInputStream 并使用阻塞读取来检测传入数据包:
当然,您还必须修改发送方,在数据包之前发送数据包大小。
You are using a packet oriented protocol on a stream oriented layer without transmitting the real packet length. because of fragmentation the size of received data can be smaller than the packet you sent.
Therefore I strongly recommend to send the data packet size before sending the actual packet. On the receiver side you could use a DataInputStream and use blocking read for detecting an incoming packet:
Of course you have to modify the sender side as well, sending the packet size before the packet.
添加到@eznme 的答案。您需要从基础流中读取数据,直到不再有待处理的数据。这可能需要一次或多次读取,但当 available 方法返回 0 时,将指示流结束。我建议使用 Apache IOUtils 将输入流“复制”到 ByteArrayOutputStream,然后从中获取 byte[] 数组。
在您的 setPacketInfo 方法中,您应该在获取协议头字节之前检查数据缓冲区长度:
这将消除您在读取零长度数据缓冲区时遇到的越界异常你的协议。
To add to the answer from @eznme. You need to read from your underlying stream until there is no more pending data. This may required one or more reads, but an end of stream will be indicated when the available method returns 0. I would recommend using Apache IOUtils to 'copy' the input stream to a ByteArrayOutputStream, then getting the byte[] array from that.
In your setPacketInfo method you should do a check on your data buffer length before getting your protocol header bytes:
That will get rid of the out of bound exceptions you are getting when you read zero-length data buffers from your protocol.
您永远不应该依赖
dataIN.available()
,并且dataIN.read(packet,0,available);
返回一个整数,表示您收到了多少字节。该值并不总是与可用值相同,并且它也可能小于缓冲区的大小。您应该这样阅读:
您还应该将
DataInputStream
包装在BufferedInputStream
中,并注意获得少于 2 个字节的情况,这样您就不会'不要尝试处理您尚未收到的字节。You should never rely on
dataIN.available()
, anddataIN.read(packet,0,available);
returns an integer that says how many bytes you received. That's not always the same value as what available says, and it can also be less than the size of the buffer.This is how you should read:
You should also wrap your
DataInputStream
in aBufferedInputStream
, and take care of the case where you get less than 2 bytes, so that you don't try to process bytes that you haven't received.