簡體   English   中英

通過 TCP 套接字發送和接收壓縮數據

[英]Sending and receiving compressed data over a TCP socket

在通過 TCP 套接字發送和接收壓縮數據方面需要幫助。

如果我不使用壓縮,代碼工作得很好,但是當我使用壓縮時會發生一些非常奇怪的事情..基本上,問題是 stream.Read() 操作被跳過,我不知道為什么..

我的代碼:

using (var client = new TcpClient())
{
    client.Connect("xxx.xxx.xx.xx", 6100);
    using (var stream = client.GetStream())
    {
        // SEND REQUEST
        byte[] bytesSent = Encoding.UTF8.GetBytes(xml);

        // send compressed bytes (if this is used, then stream.Read() below doesn't work.
        //var compressedBytes = bytesSent.ToStream().GZipCompress();
        //stream.Write(compressedBytes, 0, compressedBytes.Length);

        // send normal bytes (uncompressed)
        stream.Write(bytesSent, 0, bytesSent.Length);

        // GET RESPONSE
        byte[] bytesReceived = new byte[client.ReceiveBufferSize];
        // PROBLEM HERE: when using compression, this line just gets skipped over very quickly
        stream.Read(bytesReceived, 0, client.ReceiveBufferSize);

        //var decompressedBytes = bytesReceived.ToStream().GZipDecompress();
        //string response = Encoding.UTF8.GetString(decompressedBytes);

        string response = Encoding.UTF8.GetString(bytesReceived);

        Console.WriteLine(response);
    }
}

您會注意到上面的一些擴展方法。 這是代碼,以防您想知道那里是否有問題。

public static MemoryStream ToStream(this byte[] bytes)
{
    return new MemoryStream(bytes);
}


public static byte[] GZipCompress(this Stream stream)
{
    using (var memoryStream = new MemoryStream())
    {
        using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress))
        {
            stream.CopyTo(gZipStream);
        }
        return memoryStream.ToArray();
    }
}

public static byte[] GZipDecompress(this Stream stream)
{
    using (var memoryStream = new MemoryStream())
    {
        using (var gZipStream = new GZipStream(stream, CompressionMode.Decompress))
        {
            gZipStream.CopyTo(memoryStream);
        }
        return memoryStream.ToArray();
    }
}

這些擴展在以下方面工作得很好,所以我確定它們不是問題:

string original = "the quick brown fox jumped over the lazy dog";
byte[] compressedBytes = Encoding.UTF8.GetBytes(original).ToStream().GZipCompress();
byte[] decompressedBytes = compressedBytes.ToStream().GZipDecompress();
string result = Encoding.UTF8.GetString(decompressedBytes);
Console.WriteLine(result);

有誰知道為什么在壓縮發送的字節時跳過 Read() 操作?

編輯

在向他們展示上述示例代碼后,我收到了來自 API 提供商的消息。 他們有話要說:

乍一看,我猜標題丟失了。 輸入必須以 'c' 開頭,后跟輸入的長度(在我們的示例中為 sprintf(cLength,"c%09d",hres))。 我們需要這個,因為我們無法讀取,直到我們找到一個二進制 0 來識別結尾。

之前他們提供了一些C示例代碼,我不是 100% 完全理解,如下:

example in C:

#include <zlib.h>

uLongf hres;
char cLength[COMPRESS_HEADER_LEN + 1] = {'\0'};

n = read(socket,buffer,10);
// check if input is compressed
if(msg[0]=='c') {
     compressed = 1;
}

n = atoi(msg+1);
read.....


hres = 64000;
res = uncompress((Bytef *)msg,   &hres, (const Bytef*) 
buffer/*compressed*/, n);
if(res == Z_OK && hres > 0 ){
     msg[hres]=0; //original
}
else // errorhandling

hres = 64000;

if (compressed){
res = compress((Bytef *)buffer,   &hres, (const Bytef *)msg, strlen(msg));
     if(res == Z_OK && hres > 0 ) {
         sprintf(cLength,"c%09d",hres);
         write(socket,cLength,10);
         write(socket, buffer, hres);
     }
     else // errorhandling

makefile: add "-lz" to the libs

他們正在使用 zlib。 我不懷疑這有什么不同,但我確實嘗試過使用 zlib.net,但我仍然沒有得到任何回應。

有人能給我一個例子,說明我應該如何在 C# 中發送這個輸入長度?

編輯 2

為了回應@quantdev,這是我現在正在嘗試的長度前綴:

using (var client = new TcpClient())
{
    client.Connect("xxx.xxx.xx.xx", 6100);
    using (var stream = client.GetStream())
    {
        // SEND REQUEST
        byte[] bytes = Encoding.UTF8.GetBytes(xml);
        byte[] compressedBytes = ZLibCompressor.Compress(bytes);

        byte[] prefix = Encoding.UTF8.GetBytes("c" + compressedBytes.Length);

        byte[] bytesToSend = new byte[prefix.Length + compressedBytes.Length];
        Array.Copy(prefix, bytesToSend, prefix.Length);
        Array.Copy(compressedBytes, 0, bytesToSend, prefix.Length, compressedBytes.Length);

        stream.Write(bytesToSend, 0, bytesToSend.Length);

        // WAIT
        while (client.Available == 0)
        {
            Thread.Sleep(1000);
        }

        // GET RESPONSE
        byte[] bytesReceived = new byte[client.ReceiveBufferSize];
        stream.Read(bytesReceived, 0, client.ReceiveBufferSize);

        byte[] decompressedBytes = ZLibCompressor.DeCompress(bytesReceived);
        string response = Encoding.UTF8.GetString(decompressedBytes);

        Console.WriteLine(response);
    }
}

您需要檢查您在 TCP 流上進行的 Read() 調用返回值:它是有效讀取的字節數。

MSDN 說:

返回值

讀入緩沖區的總字節數。 如果當前沒有可用的字節數,則這可能小於請求的字節數,如果已到達流的末尾,則該值可能為零 (0)。

  • 如果套接字關閉,調用將立即返回 0(這可能是這里發生的情況)。
  • 如果不為 0,則必須檢查實際接收的字節數,如果小於client.ReceiveBufferSize ,則需要額外調用Read以檢索剩余字節。

在調用 read 之前,請檢查套接字上是否確實一些數據可用

while(client.Available == 0)
// wait ...

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.available%28v=vs.110%29.aspx

我想你可能有文件結尾左右。 您可以在讀取流之前嘗試設置流位置嗎

stream.position = 0;

http://msdn.microsoft.com/en-us/library/vstudio/system.io.stream.read

Encoding.UTF8.GetString 不應用於任意字節數組。 例如:壓縮的字節可能包含空字符,這在 UTF-8 編碼的文本中是不允許的,除非用作終止符。

如果您想打印接收到的字節以進行調試,也許您應該將它們打印為整數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM