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

时间:2014-06-05 04:24:06

标签: c# sockets tcp compression

需要有关通过TCP套接字发送和接收压缩数据的帮助。

如果我不使用压缩,代码可以正常工作,但是当我使用压缩时会发生一些非常奇怪的事情。基本上,问题是stream.Read()操作被跳过了,我不会&#39不知道为什么......

我的代码:

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);
    }
}

3 个答案:

答案 0 :(得分:1)

您需要检查return value of the Read() calls you are making on the TCP stream:它是有效读取的字节数。

MSDN说:

  

返回值

     

读入缓冲区的总字节数。如果没有多个字节,这可能小于请求的字节数   当前可用,如果流结束,则为零(0)   达到。

  • 如果套接字关闭,则呼叫将立即返回0(这可能是此处发生的情况)。
  • 如果不是0,那么必须检查您实际接收的字节数,如果小于client.ReceiveBufferSize,则需要额外调用Read来检索剩余的字节。

在您致电阅读之前,请检查套接字上的某些数据是否为available

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

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

答案 1 :(得分:1)

我想你可能有文件结尾了。您可以在读取流之前尝试设置流位置

stream.position = 0;

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

答案 2 :(得分:0)

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

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