我使用套接字开发了一个客户端 - 服务器聊天,它工作得很好,但是当我尝试使用Deflate压缩传输数据时,它不起作用:输出是“空的”(实际上它不是空的,但我'我将在下面解释。)
压缩/解压缩部分100%正常工作(我已经测试过了),所以问题必须在传输/接收部分的其他地方。
我使用以下方法将消息从客户端发送到服务器:
// streamOut is an instance of DataOutputStream
// message is a String
if (zip) { // zip is a boolean variable: true means that compression is active
streamOut.write(Zip.compress(message)); // Zip.compress(String) returns a byte[] array of the compressed "message"
} else {
// if compression isn't active, the client sends the not compressed message to the server (and this works great)
streamOut.writeUTF(message);
}
streamOut.flush();
我使用以下其他方法从客户端收到消息:
// streamIn is an instace of DataInputStream
if (server.zip) { // same as before: true = compression is active
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[512];
int n;
while ((n = streamIn.read(buf)) > 0) {
bos.write(buf, 0, n);
}
byte[] output = bos.toByteArray();
System.out.println("output: " + Zip.decompress(output)); // Zip.decompress(byte[]) returns a String of decompressed byte[] array received
} else {
System.out.println("output: " + streamIn.readUTF()); // this works great
}
调试我的程序,我发现while循环永远不会结束,所以:
byte[] output = bos.toByteArray();
System.out.println("output: " + Zip.decompress(output));
永远不会打电话给。
如果我将这两行代码放在while循环中(在 bos.write()之后),那么一切正常(它打印从客户端发送的消息)!但我不认为这是解决方案,因为收到的byte []数组的大小可能不同。因此我假设问题出在接收部分(客户端实际上能够发送数据)。
所以我的问题变成了接收部分的while循环。我尝试过:
while ((n = streamIn.read(buf)) != -1) {
,即使条件!= 0,但它和以前一样:循环永远不会结束,所以输出部分永远不会被调用。
答案 0 :(得分:2)
-1。您可以在发送压缩内容后关闭套接字,并且您的代码将开始工作。但我怀疑你想保持套接字打开更多(未来)聊天消息。因此,您需要一些其他方式让客户端知道何时已完全传输离散消息。像Patrick建议的那样,您可以在每个压缩有效负载之前传输消息长度。
但是,你可能能够利用deflate格式本身。我认为它有一个最后一个块流标记。如果您正在使用java.util.zip.Inflater,请查看Inflater.finished()。
答案 1 :(得分:1)
在流关闭之前,read函数不会返回-1。你可以做的是计算应该从服务器发送到客户端的字节数,然后在客户端读取该字节数。
计算字节数就像在实际消息之前发送从Zip.compress函数返回的字节数组的长度一样简单,然后使用readInt函数来获取该数字。
使用此算法可确保在解压缩之前读取正确的字节数,因此即使客户端实际读取0个字节,它也会继续读取,直到它收到所需的所有字节为止。您可以执行streamIn.read(buf, 0, Math.min(bytesLeft, buf.length))
只读取所需的字节数。
答案 2 :(得分:0)
您的问题是您使用流的方式。您必须发送一些元数据,以便您的客户知道数据的期望。理想情况下,您正在创建一个协议/状态机来读取流。对于您的示例,作为一种快速而简单的解决方案,请发送类似数据大小或终止序列等内容。
解决方案示例:
服务器:在压缩数据之前发送“数据大小”
客户端:等待“数据大小”字节。现在循环直到读取等于或大于“数据大小”值。像:
while( streamIn.ready() && dataRead < dataExpected)
{
dataRead += streamIn.read(buf);
}
当然,您需要使用类似的代码读取之前的dataExpected。
提示:如果您不介意丢失数据,也可以使用UDP。使用数据报编程更容易......