我有一个到服务器的TCP连接,由socket和stream实现。在会话期间,服务器可以发送任意数量的消息 - 我必须阅读并处理它们。
我创建了一个线程,它以无限循环检查和读取数据:
in = socket.getInputStream();
ByteArrayOutputStream baos = null;
byte[] buf = new byte[4096];
while(!isInterrupted()) {
baos = new ByteArrayOutputStream();
for(int s; ( s = in.read(buf)) != -1; ) {
baos.write(buf, 0, s);
if(in.available() <= 0 ) {
readChunk(baos.toByteArray());
}
}
}
但实际上,它效率不高 - 它会使CPU处于高负荷状态,并且某些字节与前一个答案结合在一起。
解决这种情况的最有效和最优雅的方法是什么?
答案 0 :(得分:6)
TCP不是面向消息的,而是面向流的。这意味着如果您发送两条消息AA和BB,则很有可能在不同的场合读取值AABB,A A B B,A ABB,AAB B,AA BB(其中空格表示不同的读取尝试次数)。
您需要自己处理邮件大小或邮件分隔符,因此不再需要in.available()。此外,您的代码将相同的数据至少复制3次到不同的缓冲区,并考虑在socket.getInputStream()上使用BufferedInputStream。
答案 1 :(得分:2)
删除available()调用。 InputStream.available()
不是对流结束的有效检查,它在文档中也是如此。它也永远不会返回负值。此外,readChunk()
方法应该是读取的方法。此外,TCP中没有消息,因此使用available()
或任何其他技术来识别它们是无效的。
修改强>
你在其他赞誉中说你有一个计数前缀。用那个。使用DataInputStream.readInt()
阅读,分配该大小的byte[]
数组,然后填入DataInputStream.readFully():
int len = din.readInt();
byte[] message = new byte[len];
din.readFully(message);
答案 2 :(得分:0)
根据您对该消息的说法,这是一种方法:
in = socket.getInputStream();
byte[] buff = new byte[4096];
int packLen=0;
int ret=0;
while(!isInterrupted()) {
int offset=0;
int bLeft=4;
// 99% of the times the read will return 4 bytes,
// but just in case, put it in a loop.
while (bLeft > 0) {
ret = in.read(buff, offset, bLeft);
if (ret > 0) {
bLeft-=ret;
offset+=ret;
}
else if (ret == 0) {
// socket has been closed
}
else {
// soket has an error
}
}
// convert the 4 bytes to an int, depends on the way it's was sent
// this method is used frecuently
packLen = (int)((buff[0] & 0xff) << 24) |
(int)((buff[1] & 0xff) << 16) |
(int)((buff[2] & 0xff) << 8) |
(int)(buff[3] & 0xff);
// if the 4 bytes of the CRC32 is not included in the length,
// increment the length
packLen+=4;
offset=4;
if (packLen > 4092)
{
// packet is too big, ignore it or do something else
packLen=4092;
}
bLeft=packLen;
// Noew loop until the whole mesage has been read
while (bLeft > 0) {
ret = in.read(buff, offset, bLeft);
if (ret > 0) {
bLeft-=ret;
offset+=ret;
}
else if (ret == 0) {
// socket has been closed
}
else {
// soket has an error
}
}
// the readChunk function must be change
// Need to pass the length of the message.
// Is not the buff.length anymore
readChunk(buff, packLen+4 /* +4 for the length of the message*/);
}
如果您需要Java CRC32类我可以给它,它符合PKZIP和以太网标准。
编辑: 注意: 如果数据包长度大于4096,则此方法不起作用。