2012-07-25 117 views
2

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

回答

1

直到流被关闭,read函数才会返回-1。你可以做的是计算应该从服务器发送到客户端的字节数,然后读取客户端的字节数。

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

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

2

-1仅在插座关闭或断开时才返回。您可以在发送压缩内容后关闭套接字,并且您的代码将开始工作。但我怀疑你想保持打开更多(未来)聊天消息的套接字。因此,您需要其他方式让客户知道离散消息何时完全传输。像Patrick建议的那样,您可以在每个压缩有效负载之前传输消息长度。尽管如此,你也许可以利用deflate格式中的某些东西。我认为它有一个最后一个block-in-stream标记。如果你使用java.util.zip.Inflater看一下Inflater.finished()

+0

感谢您的意见!现在我的Zip.compress()方法在输出byte []数组的开始处自动添加4个字节。这4个字节表示发送的压缩消息的长度。 – HBv6 2012-07-26 08:39:13

0

你的问题是你使用流的方式。您必须发送一些元数据,以便您的客户知道作为数据会发生什么。意识到你正在创建一个协议/状态机来读取流。对于你的例子,作为一个快速和肮脏的解决方案,发送像数据大小或终止序列之类的东西。

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

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

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

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

相关问题