2

我使用 Sockets 开发了一个客户端-服务器聊天,效果很好,但是当我尝试使用 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,但它和以前一样:循环永远不会结束,所以输出部分永远不会被调用。

4

3 回答 3

2

-1 仅在套接字关闭或损坏时返回。您可以在发送压缩内容后关闭套接字,您的代码将开始工作。但我怀疑你想让套接字保持打开状态以获取更多(未来)聊天消息。因此,您需要一些其他方式让客户端知道离散消息何时已完全传输。就像帕特里克建议的那样,您可以在每个压缩有效负载之前传输消息长度。

不过,您也许可以利用 deflate 格式本身的某些东西。我认为它有一个流中的最后一个块标记。如果您使用的是 java.util.zip.Inflater ,请查看Inflater.finished()

于 2012-07-25T18:51:34.317 回答
1

在流关闭之前,读取函数不会返回 -1。您可以做的是计算应该从服务器发送到客户端的字节数,然后在客户端读取该字节数。

计算字节数就像在实际消息之前发送从 Zip.compress 函数返回的字节数组的长度一样简单,然后使用 readInt 函数获取该数字。

使用此算法可确保您在解压缩之前读取正确的字节数,因此即使客户端实际读取 0 个字节,它也会继续读取,直到收到所需的所有字节。你可以做一个streamIn.read(buf, 0, Math.min(bytesLeft, buf.length))只读取你想要的字节数。

于 2012-07-25T18:28:03.597 回答
0

您的问题是您使用流的方式。您必须发送一些元数据,以便您的客户知道期望的数据是什么。理想情况下,您正在创建一个协议/状态机来读取流。对于您的示例,作为一个快速而肮脏的解决方案,发送诸如数据大小或终止序列之类的东西。

解决方案示例:
服务器:在压缩数据之前发送“数据大小”
客户端:等待“数据大小”字节。现在循环直到读取等于或大于“数据大小”值。就像是:

while( streamIn.ready() && dataRead < dataExpected)
{
    dataRead += streamIn.read(buf);
}

当然你需要读取之前的dataExpected,使用类似的代码。

提示:如果您不介意丢失数据的可能性,您也可以使用 UDP。用数据报编程更容易......

于 2012-07-25T19:54:09.823 回答