使用套接字传输/接收压缩数据:如何正确接收客户端发送的数据
我开发了一个使用套接字的客户端 - 服务器聊天工具,它工作的很好,但是当我尝试使用Deflate压缩传输数据时, t工作:输出是“空的”(实际上它不是空的,但我会在下面解释)。使用套接字传输/接收压缩数据:如何正确接收客户端发送的数据
压缩/解压缩部分是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,但它和以前一样:循环永远不会结束,所以输出部分永远不会被调用。
直到流被关闭,read函数才会返回-1。你可以做的是计算应该从服务器发送到客户端的字节数,然后读取客户端的字节数。
计算字节数与在实际消息之前发送Zip.compress函数返回的字节数组的长度一样简单,然后使用readInt函数获取该数字。
使用此算法可确保在解压缩之前读取正确的字节数,因此即使客户端实际读取0字节,它也会继续读取,直到它接收到所需的所有字节。你可以做一个streamIn.read(buf, 0, Math.min(bytesLeft, buf.length))
只读取你想要的字节数。
-1仅在插座关闭或断开时才返回。您可以在发送压缩内容后关闭套接字,并且您的代码将开始工作。但我怀疑你想保持打开更多(未来)聊天消息的套接字。因此,您需要其他方式让客户知道离散消息何时完全传输。像Patrick建议的那样,您可以在每个压缩有效负载之前传输消息长度。尽管如此,你也许可以利用deflate格式中的某些东西。我认为它有一个最后一个block-in-stream标记。如果你使用java.util.zip.Inflater看一下Inflater.finished()。
你的问题是你使用流的方式。您必须发送一些元数据,以便您的客户知道作为数据会发生什么。意识到你正在创建一个协议/状态机来读取流。对于你的例子,作为一个快速和肮脏的解决方案,发送像数据大小或终止序列之类的东西。
解决方案示例:
服务器:在压缩数据之前发送“数据大小”
客户端:等待“数据大小”字节。现在循环直到读取等于或大于“数据大小”值。喜欢的东西:
while(streamIn.ready() && dataRead < dataExpected)
{
dataRead += streamIn.read(buf);
}
当然,你需要前阅读dataExpected,用类似的代码。
提示:如果您不介意有可能丢失数据,也可以使用UDP。它更容易编程与数据报...
感谢您的意见!现在我的Zip.compress()方法在输出byte []数组的开始处自动添加4个字节。这4个字节表示发送的压缩消息的长度。 – HBv6 2012-07-26 08:39:13