通过TCP套接字发送和接收压缩数据

本文关键字:压缩 数据 TCP 套接字 通过 | 更新日期: 2023-09-27 18:06:53

需要帮助通过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);

有没有人知道为什么读取()操作被跳过时发送的字节被压缩?

编辑

在向API提供程序展示了上面的示例代码之后,我收到了一条来自API提供程序的消息。他们是这么说的:

乍一看,我猜标题不见了。输入必须开始用'c'后跟输入的长度(在我们的示例中,sprintf(length,"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套接字发送和接收压缩数据

您需要检查您在TCP流上进行的Read()调用的返回值:它是有效读取的字节数。

MSDN显示:

读取到缓冲区的总字节数。如果没有那么多字节,这个值可以小于请求的字节数当前可用,如果流的末尾为0,则为零(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不应该在任意字节数组上使用。例如:压缩字节可能包含NULL字符,这在UTF-8编码的文本中是不允许的,除非用作结束符。

如果你想打印接收到的字节用于调试,也许你应该把它们打印成整数。