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

C# 通过TCP套接字发送和接收压缩数据,c#,sockets,tcp,compression,C#,Sockets,Tcp,Compression,需要通过TCP套接字发送和接收压缩数据的帮助 如果我不使用压缩,代码可以很好地工作,但是当我使用压缩时,会发生一些非常奇怪的事情。。基本上,问题是stream.Read()操作被跳过,我不知道为什么 我的代码: using (var client = new TcpClient()) { client.Connect("xxx.xxx.xx.xx", 6100); using (var stream = client.GetStream()) { // S

需要通过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提供者展示了上面的示例代码之后,我收到了一条来自API提供者的消息。他们说:

乍一看,我想标题不见了。输入必须启动 输入的长度后面跟一个“c” (在我们的示例中,sprintf(cLength,“c%09d”,hres)。我们需要这个是因为 在找到二进制0以识别结尾之前,我们无法读取

他们之前在
C
中提供了一些示例代码,我不完全理解,如下所示:

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
您需要检查:它是有效读取的字节数

MSDN说:

返回值

读入缓冲区的总字节数。如果不需要那么多字节,则该值可以小于请求的字节数 当前可用,如果流的结尾已被删除,则为零(0) 达到

  • 如果套接字关闭,调用将立即返回0(这里可能会发生这种情况)
  • 如果不是0,则必须检查实际接收的字节数,如果小于
    client.ReceiveBufferSize
    ,则需要额外调用
    Read
    来检索剩余字节
在调用read之前,请检查套接字上是否确实存在某些数据:

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

我想你大概有文件的结尾了。您能在读取流之前尝试设置流位置吗

stream.position = 0;

Encoding.UTF8.GetString不应在任意字节数组上使用。 e、 g:压缩字节可能包含空字符,UTF-8编码文本中不允许包含空字符,除非用作终止符


如果要打印接收到的字节以进行调试,可能只需将它们打印为整数。

跳过是什么意思?调用后接收到的字节长度是多少?我所说的跳过是指它在任何时候都只执行那一行,然后移动到下一行。如果我不使用压缩,应用程序将在
stream.Read()
处等待大约60秒,直到有数据后再继续。在继续之前,您应该验证Read()调用的返回值。另外,您考虑过异步客户机吗?谢谢@quantdev,但是使用
while(client.Available==0)
似乎只会让它进入无限循环。我等了大约3分钟才放弃……是的,因为没有人向你的插座发送任何东西。这就是您需要检查它的原因:)(而且为了避免这些轮询循环,请使用异步Sockeets)谢谢-如果数据被压缩,API提供程序似乎没有发送任何内容,而且我似乎需要在请求中发送一些输入长度。请参阅上面的主要帖子中的我的编辑,如果您能提供帮助,那就太好了。@Matt,在bytessenterray前面加上'c'字符,后跟
bytesSent
(在前面加上前缀)的长度(作为无符号长)。这基本上就是他们问你的问题。顺便说一句,我对你的第一个问题的第一个回答最终是正确的,不是吗关于你的第一个答案,我不确定它是否正确,因为没有压缩,它工作得很好。。发生的情况是(因为它是同步的),它只是等待一个响应(通常大约60秒),然后继续。我不需要特别告诉它等待。但无论如何,我已经实施了这个建议。。但就像我说的,我不知道它是否正确,因为当我用压缩数据测试它时,我并没有得到任何响应,最终会永远等待。稍后我也将实现异步套接字,但我想先让一个简单的示例开始工作。问题在前一行。正如我也说过的,如果数据没有被压缩,代码工作得非常好。
stream.position = 0;