套路化编程:C++与C#之间的zlib(libz)压缩传输

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys

        因为需要压缩,因为一端是C++,所以用了zlib(libz),因为用了zlib,所以C#端也要用兼容zlib格式的方法。

目录

一、技术要点

1.1 如何传输原始长度

1.2 解压时准备的缓冲区不够怎么办

1.3 压缩格式是什么

二、代码

2.1 C++ zlib的包装类

2.2 C# DeflateStream解压缩

2.3 C# SharpZipLib压缩


一、技术要点

1.1 如何传输原始长度

        压缩库不会传递原始长度信息,必须自己想办法传递。我的办法是先传输长度,再传输压缩后的数据。

1.2 解压时准备的缓冲区不够怎么办

        zlib的解压缩返回码会指示缓冲区不足,加大之后再次尝试。C#上使用流,没有这个问题。

1.3 压缩格式是什么

        zlib的压缩格式是Deflate,C#的DeflateStream的压缩格式现在也是Deflate(.net某个版本以前不是),但是是Raw Deflate,也就是没有两字节的头部格式说明和四字节的尾部校验和。所以呢,zlib的压缩数据去掉前后两个和四个字节传递给C#的DeflateStream解压是没有问题的。

        但是如果要传送数据给zlib解压缩,C#的DeflateStream的数据要加上头尾才行,经过了一些尝试之后仍然失败,所以放弃了。改用第三方库SharpZipLib,使用方法和.net自带的DeflateStream几乎相同。

二、代码

2.1 C++ zlib的包装类

        这个代码位于我的git库的ctfc项目的src/function/myZIP.h。依赖的头文件都在同一位置,除了zlib.h。

//myZIP.h 数据压缩
//
// Copyright (c) ct  All rights reserved.
// 版权所有 ct 保留所有权利
//#pragma once#include "config.h"
#include "function.h"
#include "Buffer.h"
#include "zlib/zlib.h"namespace ns_my_std
{//数据压缩,压缩后数据自带解压缩以后长度,8字节,压缩方的字节序(zlib要求自己想办法传输原始长度)class CMyZip{public:typedef uint64_t T_LEN;private:static bool _UnCompress(char const * src, T_LEN srclen, CBuffer & output, long buf_override){T_LEN outsize;//压缩数据记录的解压缩后长度memmove(&outsize, src, sizeof(T_LEN));//thelog << "记录的长度 " << CMyTools::ToHex((char*)&outsize, sizeof(T_LEN)) << " : " << outsize <<" 输入长度 "<< srclen << endi;if (0 == outsize){output.setSize(0);return true;}if (!output.reserve(outsize * buf_override)){thelog << "内存不足" << ende;return false;}uLongf len = output.capacity();//thelog << "预设缓冲区 " << len << endi;int ret = uncompress((unsigned char *)output.getbuffer(), &len, (unsigned char *)src + sizeof(T_LEN), srclen - sizeof(T_LEN));if (0 != ret){if (Z_MEM_ERROR == ret){thelog << "解压缩失败 内存不足" << ende;return false;}else if (Z_STREAM_ERROR == ret){thelog << "解压缩失败 level错误" << ende;return false;}else if (Z_BUF_ERROR){thelog << "预设缓冲区不足  " << buf_override << " " << output.capacity() << " " << len << ende;return _UnCompress(src, srclen, output, buf_override + 1);}else{thelog << "解压缩失败 " << ret << ende;return false;}}if (len != outsize){thelog << "解压缩后长度与预期不一致 " << len << " " << outsize << ende;return false;}output.setSize(len);return true;}public://默认压缩级别static bool Compress(char const * src, T_LEN srclen, CBuffer & output){return Compress2(src, srclen, output, Z_DEFAULT_COMPRESSION);}//level 0-9 越大压缩率越高耗时越长static bool Compress2(char const * src, T_LEN srclen, CBuffer & output, int level){if (Z_DEFAULT_COMPRESSION == level);else if (level < 0)level = 0;else if (level > 9)level = 9;else;if (!output.reserve(compressBound(srclen) + sizeof(T_LEN))){thelog << "内存不足" << ende;return false;}uLongf len = output.capacity() - sizeof(T_LEN);if (0 != compress2((unsigned char *)output.getbuffer() + sizeof(T_LEN), &len, (unsigned char *)src, srclen, level)){thelog << "压缩失败" << ende;return false;}T_LEN tmp = srclen;memmove(output.getbuffer(), &tmp, sizeof(T_LEN));output.setSize(sizeof(T_LEN) + len);return true;}static bool UnCompress(char const * src, T_LEN srclen, CBuffer & output){return _UnCompress(src, srclen, output, 2);}public:static int CZip_test(int argc, char ** argv){CEasyFile file;CBuffer input;CBuffer output;CBuffer output2;if (!file.ReadFile("随便一个文件名", input)){thelog << "读文件失败" << ende;return __LINE__;}if (!Compress(input.data(), input.size(), output))return __LINE__;thelog << input.size() << " " << output.size() << " " << (100 - output.size() * 100 / (input.size() ? input.size() : 1)) << "%" << endi;if (!UnCompress(output.data(), output.size(), output2))return __LINE__;thelog << input.size() << " 解压缩后 " << output2.size() << endi;if (!input.Compare(output2))return __LINE__;for (long i = 0; i < 10; ++i){if (!Compress2(input.data(), input.size(), output, i))return __LINE__;thelog << i << " " << input.size() << " " << output.size() << " " << (100 - output.size() * 100 / (input.size() ? input.size() : 1)) << "%" << endi;}return 0;}};
}

        这个代码很简单,自带测试。

        除了里面使用了zlib来压缩和解压缩之外,做一点额外工作:

        压缩后数据的前八个字节是原始数据长度,发送方的字节序

        按道理应该用网络字节序,这样比较有通用性,不过目前用到的设备字节序都相同,所以还没有做。

        注意不同CPU上数据类型的差异,用作传输的部分的长度和字节序都是必须一致的。

2.2 C# DeflateStream解压缩

        因为DeflateStream是.net自带的,能用尽量用。

		static public string UnCompressMesssage(string text){try{byte[] zipdata = Convert.FromBase64String(text);MemoryStream zipstream = new MemoryStream(zipdata.Skip(10).Take(zipdata.Length - 14).ToArray());//前面8字节原始长度(C++代码额外增加的),DeflateStream格式前面少2个字节,后面少4个字节DeflateStream deflateStream = new DeflateStream(zipstream, CompressionMode.Decompress);MemoryStream resultMemoryStream = new MemoryStream();deflateStream.CopyTo(resultMemoryStream);text = System.Text.Encoding.UTF8.GetString(resultMemoryStream.ToArray());deflateStream.Close();}catch (Exception ex){text = ex.ToString();//如果出错返回的是异常信息,换成别的吧}return text;}

        这个代码针对UTF-8的字符串进行操作,如果数据不是UTF-8,可以使用base64编码后再操作。输入数据是经过了base64编码的压缩数据(咳咳,又损失了一部分压缩效果,因为传输过程还有些别的要求),输出是还原的字符串。

2.3 C# SharpZipLib压缩

        代码和使用DeflateStream差不多,除了构造函数少一个参数(因为压缩和解压缩是两个类,不需要用参数来区分)。

using ICSharpCode.SharpZipLib.Zip.Compression.Streams;static public string CompressMesssage(string text){try{MemoryStream uncompressed = new MemoryStream(Encoding.UTF8.GetBytes(text));MemoryStream resultMemoryStream = new MemoryStream();DeflaterOutputStream deflateStream = new DeflaterOutputStream(resultMemoryStream);//由于最终无法成功添加头尾信息,所以放弃使用DeflateStream,改为SharpZipLibuncompressed.CopyTo(deflateStream);deflateStream.Close();byte[] tmp = new byte[8 + resultMemoryStream.ToArray().Length];BitConverter.GetBytes((long)text.Length).CopyTo(tmp, 0);//必须是8位整数resultMemoryStream.ToArray().CopyTo(tmp, 8);text = Convert.ToBase64String(tmp);}catch (Exception ex){text = ex.ToString();//这个出错处理要改一改}return text;}

        输入是字符串,输出是经过base64编码的压缩数据(之前有八个字节的原始长度)。

(这里是结束)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/759696.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu20.04安装Pycharm

下载pycharm安装包 https://www.jetbrains.com/pycharm/download/#sectionlinux 使用社区版点击download 下载好的pycharm如图所示&#xff0c;右键解压&#xff1a; 打开终端&#xff0c;输入cd命令&#xff0c;进入刚刚解压文件夹下的bin文件夹&#xff0c;命令行是cd 文…

深度优先搜索(DFS)与广度优先搜索(BFS)在 Java 中的应用

引言&#xff1a; 在算法和数据结构中&#xff0c;深度优先搜索&#xff08;Depth First Search&#xff0c;DFS&#xff09;和广度优先搜索&#xff08;Breadth First Search&#xff0c;BFS&#xff09;是两种常用的图遍历算法。它们在解决图相关问题时非常有用&#xff0c;可…

19.WEB渗透测试--抓包技术(下)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;18.WEB渗透测试--抓包技术&#xff08;上&#xff09;-CSDN博客 Burp含义和内容参考&…

CCDP.01.使用NotePad++辅助部署OpenStack的说明

前言 对于象OpenStack&#xff08;OS&#xff09;这样的复杂分布式系统&#xff08;云计算平台&#xff09;&#xff0c;一次部署通过是需要相当的Linux基础、网络基础、分布式系统基础、云计算基础的。这里类比在开发大型复杂系统常常采用的“防御式编程”方法论&#xff0c;探…

LoadBalancerCacheManager not available, returning delegate without caching

警告&#xff1a;LoadBalancerCacheManager not available, returning delegate without caching 背景&#xff1a;更换了redis集群 解决方案&#xff1a; 重启gateway网关服务 也就是重启引用下面这个包的服务 <dependency><groupId>org.springframework.cloud…

写一个简单的 C++ 日志库 - cllogger(3)- CRT

通过上一篇 《写一个简单的 C 日志库 - cllogger&#xff08;2&#xff09;- 日期时间》我们已经掌握了如何通过 std::chrono 提供的日期时间工具转换时间参数为指定格式的字符串。 现在我们可以把各个参数信息拼装为 Entry 实例&#xff0c;交给 OutputMessage() void cllog…

视频无水印批量下载软件|抖音视频提取工具

视频无水印批量下载软件 在当今社交媒体充斥着大量优质视频内容的时代&#xff0c;很多用户都希望能够轻松下载自己喜爱的视频进行收藏或分享。为了满足用户的需求&#xff0c;我们特别推出了一款专业的视频无水印批量下载软件&#xff0c;让您可以方便快捷地获取喜爱的视频内容…

最新梨花带雨网页音乐播放器二开优化修复美化版全开源版本源码下载

最新梨花带雨网页音乐播放器二开优化修复美化版全开源版本源码下载 梨花带雨播放器基于thinkphp6开发的XPlayerHTML5网页播放器前台控制面板,支持多音乐平台音乐解析。二开内容:修复播放器接口问题,把接口本地化,但是集成外链播放器接口就不本地化了,我花钱找人写的理解下…

[C语言]——内存函数

目录 一.memcpy使用和模拟实现&#xff08;内存拷贝&#xff09; 二.memmove 使用和模拟实现 三.memset 函数的使用&#xff08;内存设置&#xff09; 四.memcmp 函数的使用 C语言中规定&#xff1a; memcpy拷贝的就是不重叠的内存memmove拷贝的就是重叠的内存但是在VS202…

C++总结

数据类型 基本的内置类型 修饰符类型 C 允许在 char、int 和 double 数据类型前放置修饰符。 修饰符是用于改变变量类型的行为的关键字&#xff0c;它更能满足各种情境的需求。 类型限定符 函数 以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的&am…

relative、fixed、absolute 和 static 四种定位有什么区别?

1. relative、fixed、absolute 和 static 四种定位 1. Relative&#xff08;相对定位&#xff09;&#xff1a; 相对定位通过设置 position: relative; 来实现。相对定位相对于元素在文档流中的初始位置进行定位。可以使用 top、right、bottom 和 left 属性来相对于原始位置进…

使用 Dify 和 AWS Bedrock 玩转 Anthropic Claude 3

本篇文章&#xff0c;聊聊怎么比较稳定的使用 Anthropic Claude 3&#xff0c;以及基于目前表现非常好的模型&#xff0c;来做一些有趣的 AI Native 小工具。 写在前面 在实际体验了半个多月&#xff0c;月初上线的 Anthropic Claude Pro 后&#xff0c;发现 Claude 3 系列模…

C语言中的联合和枚举(未完)

1、联合体 联合体类型的声明 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以不同的类型。但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫&#xff1a;共⽤体。因为所有变量公用…

在任何 Mac 上恢复永久删除照片的 5 种简单方法

Mac 为业余和专业摄影师提供了很多东西&#xff0c;从令人印象深刻的硬件到广泛的照片管理和编辑应用程序。它还提供了多种恢复丢失照片的方法&#xff0c;我们在本文中介绍了其中的五种方法&#xff0c;以帮助您避免潜在的灾难性情况。 Mac 上删除的照片去了哪里&#xff1f;…

如何快速搭建一个完整的vue2+element-ui的项目-二

技术细节-继续配置 提示&#xff1a;你以为这样就完了吗,其实还有很多东西需要我们自己手写的 例如&#xff1a; element-ui的配置样式重置配置src使用的配置elinst配置axios异步请求的二次封转配置语言国际化配置(这个看需求,我这里就不用配置了)vuex的配置mixins的配置开发环…

【Linux第三课-基础开发工具的使用】yum、vim、gcc/g++编译器、gdb、Make/Makefile编写、进度条程序、git命令行简单操作

目录 yum - 软件包管理器快速认识yum快速使用yumyum搜索yum安装yum卸载 yum的周边 - yum的整个生态问题 vim快速介绍vimvim的模式命令模式插入模式低行模式 常见模式 -- 命令、低行命令模式 -- 光标的移动命令模式 -- 复制粘贴、剪贴、删除命令模式 -- 小写/大写替换模式命令模…

TCP协议 及 重要机制

目录 1.TCP 协议报文格式 1.1.端口号 1.2 首部长度 和 选项 1.3 保留位 1.4 检验和 1.5 32位序号和确认序号 2. TCP的重要机制 2.1 确认应答 2.2 超时重传 2.3 连接管理 2.3.1 三次握手 2.3.2 四次挥手 2.4 滑动窗口 2.5 流量控制 2.6 拥塞控制 2.7 延时应答 2…

JAVA EE (计算机是如何工作的)

学前注意事项 出去面试的时候java岗位不需要懂前端&#xff08;会少量讲解&#xff09; 但是我们做项目的时候多少回用到一些前端的东西 1.什么是计算机 1.1前情提要 不仅仅只有电脑是计算机 计算机还不仅仅是电脑手机和平板 路由器 智能洗衣机 刷脸打卡机都可以说是计算…

Ubuntu18.04桌面版设置静态IP地址

引用: Ubuntu配置静态IP_ubuntu配置静态ip地址-CSDN博客 正文 默认Unbuntu 18.04 Desktop桌面版使用 netplan 管理网卡网络地址。使用Unbuntu 18.04 桌面版配置&#xff0c;可以通过桌面上的设置图标配置网卡的静态IP地址。 点击桌面右上角下拉框&#xff0c;点击“设置”按…

Codeforces\ Round\ 930(C.Bitwise Operation Wizard)

C o d e f o r c e s R o u n d 930 ( C . B i t w i s e O p e r a t i o n W i z a r d ) \Huge{Codeforces\ Round\ 930(C.Bitwise Operation Wizard)} Codeforces Round 930(C.BitwiseOperationWizard) 文章目录 题意思路注意 标程 题目链接&#xff1a;[B.Bitwise Operati…