2012-07-25 115 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。它更容易編程與數據報...

相關問題