草庐IT

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

coder 2023-09-19 原文

在通过 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 提供商的消息。他们是这样说的:

at a first glance I guess the header is missing. The input must start with a 'c' followed by the length of the input (sprintf(cLength,"c%09d",hres) in our example). We need this because we can't read until we find a binary 0 to recognize the end.

之前他们提供了一些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);
    }
}

最佳答案

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

MSDN 说:

Return Value

The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.

  • 如果套接字已关闭,调用将立即返回 0(这可能是此处发生的情况)。
  • 如果不为 0,则您必须检查您实际收到了多少字节,如果它小于 client.ReceiveBufferSize,您将需要额外调用 Read 以检索剩余的字节。

在调用 read 之前,检查一些数据实际上是 available在套接字上:

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

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

关于c# - 通过 TCP 套接字发送和接收压缩数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24051610/

有关c# - 通过 TCP 套接字发送和接收压缩数据的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  4. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  5. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  6. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

    我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

  7. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

    我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

  8. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  9. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  10. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

随机推荐