hessiancpp编译和使用(C++版)
个人博客:戳我,戳我
许下的承诺
前两篇博客Hessian通信案例(java)和Hessian源码分析(java)介绍了Java版的hessian的使用以及源码分析。当时也说过打算写一下C++版的hessian的使用和源码分析,现在就是兑现承诺的时候了。其实我项目中实际用到的是C++版的hessian,java版的hessian是我最初接触用来理解hessian,并进行联调测试的部分。
hessian的官网上也提供了C++版的hessian的实现。只不过当时在网上找到的说法是hessiancpp的活跃度比较低,而且编译出问题很多,但是还是决定自己亲自一试,毕竟”绝知此事要躬行”嘛,嘿嘿!当然也有其他版本的C++实现,比如hessianorb项目。
Hessiancpp编译
现在是回过头来写,故早先编译碰到的问题我已经没办法复现,此处就只记录正确的编译步骤和方法。
下载hessiancpp
到官网下载源码包:https://sourceforge.net/projects/hessiancpp/,我自己当时的版本是hessiancpp-hessaincpp-1.1.0.tar.gz。
解压编译
解压上面下载的tar.gz压缩包
tar zxvf hessiancpp-hessaincpp-1.1.0.tar.gz
然后进入顶层目录hessiancpp:
cd hessaincpp
你可以先查看此目录下的Makefile文件,然后试着执行:
make all
结果,不出所料,编译出错,错误如下:
In file included from hessian_proxy.cpp:19:0:
hessian_proxy.h:31:19: 致命错误:ssl++.h:没有那个文件或目录
编译中断。
make: * [hessian_proxy.o] 错误 1
可见缺少了一个叫做ssl++.h的头文件,我们再仔细查看Makefile文件,可以看到第一行代码是:
SSLPP=../sslpp
显然需要另外一个库。这个库就是提供http代理的功能。网上有人碰到这个问题后,选择了libcurl重写一个http代理,据说还可行。
言归正传,我们需要一个叫做sslpp的库。
下载hessian-sslpp
去github下载: https://github.com/ksturner/hessian/tree/master/sslpp
常规操作,解压,然后进入顶层目录,然后查看Makefile文件,然后尝试执行make all编译。果然又出错了。
查看INSTALL文件:
SSLPP was developed on a Fedora Core 2 x86_64 system, using
- GCC 3.3.3
- OpenSSL 0.9.7a
- BOOST 1.31.0
- shared library
可以看到编译SSLPP需要的依赖。我碰到的编译错误是没有安装BOOST库。那么就去安装吧:
yum install boost boost-devel boost-doc
完事之后再试着执行make all ,make install看看。如果没有错误,那么基本就是可以了,如果碰到了错误,那么没办法,只能一步步解决。
这里主要是需要编译生成的libsslpp.so这个动态库。
继续编译hessiancpp
回到之前对hessiancpp的编译,修改下Makefile文件中SSLPP这个宏的值,根据sslpp的编译修改。然后执行:
make all
正常情况就不会有问题了,编译成功。在当前目录下生成了一个libhessian.so和一个main可执行程序和main_dyn可执行程序。
大功告成!!!
使用hessiancpp
首先要明白,hessiancpp只是实现了hessian的客户端,具体就是实现了hessian的序列化和反序列算法以及使用sslpp作为一个http代理客户端。故如果要使用hessiancpp,还需要配合一个hessian服务端,这里就用之前博文中介绍过的java 版hessian server作为服务端。
启动hessian server
假设服务端提供了两个接口函数,具体请看下图:
函数功能都是返回”Hello, world,my name is nick!”。
需要注意的是,此时这个服务端的地址为:* http://[IP]:8080/hessian_server/ServerMachineTest *
然后,启动服务端。
修改客户端代码
回到hessiancpp目录下,修改main.cpp。主要修改的代码是:
......
cout << "starting" << endl;
hessian_proxy proxy("192.168.242.188:8080", "/hessian_server/ServerMachineTest", false);
try
{ Object* hello_ret = proxy.call("hello",0);dump(hello_ret);Integer arg1(42);String arg2("hahaha");Object* hello2_ret = proxy.call("hello_2",2,&arg1,&arg2);dump(hello2_ret);.....
此处稍微对上面的代码做一点解释,具体解释在后面的代码分析。首先根据url构造一个http代理proxy,然后执行call函数,类似java版hessain里边的invoke函数,call函数的参数就是接口方法名以及接口函数的参数个数,以及参数本身。 dump()函数是已经实现好的,主要功能是打印输出。
编译,执行,可以看到结果如下:
至此,完成了c++版的hessian客户端和java版的hessian服务端的通信。
hessiancpp源码分析
hessiancpp目录下可以看到的文件有hessian_input.* hessian_output.* hessain_proxy.* wrappers.* zlibdec.* 等,大概可以猜测到hessian_input.cpp和hessian_output.cpp分别是接收处理和发送处理的代码,也即反序列化和序列化的代码,hessian_proxy.cpp是http代理的代码,负责发送和接收hessian报文。剩下的cpp文件具体再研究。
单步调试
为了进行代码分析,最好的办法是单步跟踪:
gdb ./main
进入到call函数中:
Object* hessian_proxy::call(const string& method, int argc, ...) throw(io_exception, http_exception) {
va_list ap;
int narg = 0;// result, connection, hessian output
Object* result = NULL;
sslpp::http_connection con(_hostspec, _use_ssl);
hessian_output hout;// create method call
string mc = hout.start_call(method);// add parameters
va_start(ap, argc);
while (narg++ < argc) {Object* param = va_arg(ap, Object*);hout.set_parameter(mc, param);
}
// clean up vararg
va_end(ap);
// finish method call
hout.complete_call(mc);// call
_num_calls++;
_bytes_out += mc.length();
string raw_reply = con.POST(_url, mc, HESSIAN_HTTP_CONTENT_TYPE, HESSIAN_HTTP_USER_AGENT);
string hessian_reply = con.parse_reply(raw_reply);
_bytes_in += hessian_reply.length();
_call_size_map.insert(std::make_pair(method, hessian_reply.length()));
// test for compressed answer
unsigned short header = ((unsigned short)hessian_reply[1]) << 8;
header += ((unsigned short)hessian_reply[0]);
if (header == GZIP_MAGICK) {
// decompresszlibdec zdec;try {hessian_reply = zdec.decompress(hessian_reply);}catch (zlib_exception& e) {throw io_exception(e.what());}
}
// create a string_input_stream around the reply; note use of auto_ptr
auto_ptr<input_stream> sis(new string_input_stream(hessian_reply));
// read reply
hessian_input hin(sis);
hin.start_reply();
result = hin.get_result();
hin.complete_reply();
return result;
}
其中关于序列化的代码为:
.....
// create method call
string mc = hout.start_call(method);// add parameters
va_start(ap, argc);
while (narg++ < argc) {Object* param = va_arg(ap, Object*);hout.set_parameter(mc, param);
}
// clean up vararg
va_end(ap);
// finish method call
hout.complete_call(mc);
start_call(),set_parameter(),complete_call()三个函数完成了hessian的序列化。具体序列化的过程如下:
string hessian_output::start_call(const string& method_name) {
string mc("c");
mc.append(1, (char)1);
mc.append(1, (char)0);
return write_ascii_string(mc, method_name, 'm');
}
其实比较简单,字符’c’可能代表”call”或者”client”,然后是版本号’1’,然后追加了一个字符’0’,然后利用write_ascii_string()函数序列化接口函数名,如上面的”hello”。
然后:
string& hessian_output::set_parameter(string& call, Object* object) {return write_object(call, object);
}
然后进入write_object函数:
string& hessian_output::write_object(string& call, Object* object) {const char* cls = object->classname2();if (strcmp(cls, "Binary") == 0) {return write_binary(call, dynamic_cast<Binary*>(object));}if (strcmp(cls, "Boolean") == 0) {return write_boolean(call, dynamic_cast<Boolean*>(object));}if (strcmp(cls, "Date") == 0) {return write_date(call, dynamic_cast<Date*>(object));}if (strcmp(cls, "Double") == 0) {return write_double(call, dynamic_cast<Double*>(object));}if (strcmp(cls, "Fault") == 0) {return write_fault(call, dynamic_cast<Fault*>(object));}if (strcmp(cls, "Integer") == 0) {return write_integer(call, dynamic_cast<Integer*>(object));}if (strcmp(cls, "List") == 0) {return write_list(call, dynamic_cast<List*>(object));}if (strcmp(cls, "Long") == 0) {return write_long(call, dynamic_cast<Long*>(object));}if (strcmp(cls, "Map") == 0) {return write_map(call, dynamic_cast<Map*>(object));}if (strcmp(cls, "Null") == 0) {return write_null(call, NULL);
}
if (strcmp(cls, "Ref") == 0) {return write_ref(call, dynamic_cast<Ref*>(object));}if (strcmp(cls, "Remote") == 0) {return write_remote(call, dynamic_cast<Remote*>(object));}if (strcmp(cls, "String") == 0) {return write_string(call, dynamic_cast<String*>(object));}if (strcmp(cls, "Xml") == 0) {return write_xml(call, dynamic_cast<Xml*>(object));}// throw exception, should not get here, reallythrow io_exception(string("hessian_output::write_object(): unknown object class ").append(object->classname()));
}
这是这一步的核心代码。可见,hessian支持基本的几种序列化类型,根据不同的对象类型,调用不同的序列化函数。
此处关键的就是Object这个类,定义在wrappers.h头文件中,Object是基类,后面派生了几种基本的子类:Binary,Boolean,Date,Double,Integer,Long,Map,String…,各个子类里有关于这种类型的对象的具体序列化和反序列化方法。
回到call()函数,通过:
string raw_reply = con.POST(_url, mc, HESSIAN_HTTP_CONTENT_TYPE, HESSIAN_HTTP_USER_AGENT);
客户端把序列化后的hessian报文通过http发送给服务端,然后等待服务端的应答。
string hessian_reply = con.parse_reply(raw_reply);
_bytes_in += hessian_reply.length();
_call_size_map.insert(std::make_pair(method, hessian_reply.length()));
服务端的应答就保存在hessian_reply这个string中,接下来就是反序列化:
...hessian_input hin(sis);hin.start_reply();result = hin.get_result();hin.complete_reply();
同理,单步跟踪后核心函数式get_result()函数:
Object* hessian_input::get_result() throw(io_exception) {return read_object();
}Object* hessian_input::read_object() throw(io_exception) {int tag = read();return read_object(tag);
}Object* hessian_input::read_object(int tag) throw(io_exception) {switch (tag) {case 'b':case 'B': return new Binary(read_bytes(tag));case 'T':case 'F': return new Boolean(read_boolean(tag));case 'd': return new Date(read_date(tag));case 'D': return new Double(read_double(tag));case 'f': return new Fault(read_fault(tag));case 'I': return new Integer(read_int(tag));case 'V': return new List(read_list(tag));case 'L': return new Long(read_long(tag));case 'M': return new Map(read_map(tag));case 'N': return new Null();case 'R': return new Ref(read_ref(tag));case 's':case 'S': return new String(read_string(tag));case 'x':case 'X': return new Xml(read_xml(tag));default:throw io_exception(string("hessian_input::readObject(): tag ").append(1, (char) tag).append(" cannot be handled")); }
}
反序列化的原理是根据不同的tag值调用相应的类型的反序列函数。
hessian报文
上述c++客户端序列化接口函数和其参数的结果如下:
从服务端返回来的hessian报文如下:
可见,hessian报文有很多不可见的二进制字符!
完了
上面就把c++版的hessaincpp的编译以及使用,以及源码分析都介绍了一遍,由于我现在是回过头来写这篇博客,会觉得很多地方简单,然后可能会觉得某些步骤或者代码分析不重要,就忽略了一部分。实际过程中,如果你碰到hessian,我的博客仅当参考,还需你自己探索。毕竟“绝知此事要躬行!”O(∩_∩)O
Blog:
rebootcat.com (默认)
email: linuxcode2niki@gmail.com
2016-11-22 于杭州
By 史矛革
相关文章:
美国AI博士一针见血:Python这样学最容易成为高手!
我见过市面上很多的 Python 讲解教程和书籍,他们大都这样讲 Python 的:先从 Python 的发展历史开始,介绍 Python 的基本语法规则,Python 的 list, dict, tuple 等数据结构,然后再介绍字符串处理和正则表达式࿰…

win7操作系统在哪显示隐藏文件夹
win7操作系统在哪显示隐藏文件夹 打开计算机--组织--文件夹和搜索选项--查看--把 “隐藏受保护的操作系统文件”前面的钩去掉,选中“显示隐藏的文件、文件夹和驱动器”--确定

ASP.NET MVC4中调用WEB API的四个方法
当今的软件开发中,设计软件的服务并将其通过网络对外发布,让各种客户端去使用服务已经是十分普遍的做法。就.NET而言,目前提供了Remoting,WebService和WCF服务,这都能开发出功能十分强大的服务。然而,越来越多的互联网…

使用docker制作hexo镜像
个人博客:戳我,戳我 背景 这段时间一直在折腾我的博客,由于之前出现过一次电脑硬盘完全挂掉的情况,为了避免重新搭建博客系统,一直打算搞一个方便点的环境,能进行多机迁移之类的。正好,Docker完…
3D目标检测深度学习方法数据预处理综述
作者 | 蒋天元来源 | 3D视觉工坊(ID: QYong_2014)这一篇的内容主要要讲一点在深度学习的3D目标检测网络中,我们都采用了哪些数据预处理的方法,主要讲两个方面的知识,第一个是representation,第二个数据预处…

NTLM协议认证
第一篇blog,发现这是个记录学习过程的好地方。从基础的开始吧。 NTLM: 基本知识telnet的一种验证身份方式,即Windows NT LAN Manager (NTLM); NTLM 是为没有加入到域中的计算机(如独立服务器和工作组)提供的…

新盒模型移动端的排版
这里采用的是新盒模型来进行排版: <div class"mytest"> <header></header> <section></section> <footer></footer> </div> 在CSS样式里添加如下样式 html,body{ height: 100%; } .mytest{ …

微信跳一跳高分辅助踩坑
旧博文,搬到 csdn 原文:http://rebootcat.com/2018/01/08/wechat_jump_hack/ 最近挺火的微信跳一跳 最近新版微信的『跳一跳』小程序着实火了一把,也把小程序这个概念再次推波助澜了一波,看来以后小程序这个入口会有大作为。 张小…
“编程能力差,90%的人会输在这点上!”谷歌开发:其实都是在瞎努力
这是一个很难让人心平气和的年代。疫情之下,很多人的都在面临着:失业、降薪、找不到工作、随时被裁等风险。但是:有心的人早已上路超车,做个人能力的升级——提高自己的不可替代性。李开复曾提出过“五秒钟准则”:一项…

64位win7安装IIS7时不能浏览asp的问题
64位win7高级家庭版安装IIS7,安装完成后只能浏览静态页,找了很多的教程都没有解决,最后在一个博客里看到说64位系统下ASP是不支持的ODB读取ACC的数据库的,因此需要开启32位应用程序的支持。 方法是: Internet 信息服务…

0525 项目回顾7.0
一、sprint总结 当谈到团队,我开始真的不知道团队是怎么样的,怎么样进行工作的,要该怎么出力团队的关系,有时候会涉及到个人问题,是不是该考虑进来,但是很多时候是不能的,每一个人作为团队的一份…

辩证看待 iostat
旧博文,搬到 csdn 原文:http://rebootcat.com/2018/01/16/using-iostat-dialectically/ 前言 经常做系统分析会接触到很多有用的工具,比如 iostat,它是用来分析磁盘性能、系统 I/O 的利器。 本文将重点介绍 iostat 命令的使用,并…
搞机器学习,Python和R哪个更合适?
【编者按】如果你正想构建一个机器学习项目,但却纠结于如何选择编程语言,这篇文章将是你所需要的。这篇文章不仅帮助你理解Python和R这两种语言的区别,还有助于你了解各个语言多方面的优势。作者 | Manav Jain译者 | Joe,编辑 | 夕…

Java安装方法
第1章 Java简介及开发环境搭建 实验1 JDK的下载、安装与配置 【实验目的】 (1)熟悉JDK工具包的下载及安装过程。 (2)掌握JAVA_HOME、CLASSPATH及Path的设置内容。 (3)掌握Java程序运行原理及Javac、Java命…

Hash函数的安全性
我们为了保证消息的完整性,引进了散列函数,那么散列函数会对安全正造成什么影响呢?这是需要好好研究一番的问题。 三个概念: 1.如果y<>x,且h(x)h(y),则…

一键安装python3环境
旧博文,搬到 csdn 原文:http://rebootcat.com/2018/04/15/python3_in_a_box/ 一键安装python3环境 由于现在逐步转移到 python3 进行开发,但是很多机器并没有预装 python3 环境,所以需要安装。 所以分享一个我常用的,…
认知智能再突破,阿里 18 篇论文入选 AI 顶会 KDD
作者 | 马超责编 | 屠敏头图 | CSDN 下载自东方 IC出品 | CSDN(ID:CSDNnews)近日,国际知识发现与数据挖掘协会KDD在官网(https://www.kdd.org/kdd2020)公布其2020年度的论文收录结果,笔者看到阿里共有18篇论文入选&…

python采集cpu信息
旧博文,搬到 csdn 原文:http://rebootcat.com/2018/05/20/analyze_cpu/ python脚本采集cpu 经常要做一些 linux 系统上的性能分析或者采集 cpu/mem/bandwidth 上报到监控系统。 分享一个我平常常用到的 cpu 采集脚本,原理是分析 /proc/stat…

Pretty Login便携版:Windows 7登录界面修改器
Pretty Login是由chnable开发的一个美化小工具,用来辅助修改Widnows 7登陆界面的背景图片,除此之外,它也能定制欢迎界面上的文本、按钮样式,如设置阴影、半透明效果。 由于Windows 7限制登录背景图片的大小不超过255KB,…
来了来了!趋势预测算法大PK!
作者 | 王哲责编 | Carol头图 | CSDN 付费下载自视觉中国趋势预测在很多应用场景中都会起到至关重要的作用,比如淘宝商家会考虑库存量应该保持在多少才能够满足客户需求,商场希望得知假期会迎来多大的客流量以安排系列活动,机场想要预测五一黄…

hdu 5713(状态压缩DP)
要进行两次dp, 第一个,dp[i],1<i<(1<<n) 其中用i的二进制形式表示已选择的点。 dp[i] 用来保存i中的点构成一个连通块,边集多少种可能。 转移方程: save[0] 1;//这里用save[i]表示dp[i]for(int i1;i<(1<<n)…

nginx特定的 404页面利于seo
要求:访问http://www.qq.com/123 url保持不变 显示的结果为指定的404页面curl -I http://www.qq.com/123 返回的状态码为404 准备一 404.php页面在最底部加上:<?phpheader(HTTP/1.1 404 Not Found);header(Status: 404 Not Found);?>然后ngin…

python采集bandwidth信息
旧博文,搬到 csdn 原文:http://rebootcat.com/2018/05/21/analyze_bandwidth/ python脚本采集bandwidth 经常要做一些 linux 系统上的性能分析或者采集 cpu/mem/bandwidth 上报到监控系统。 分享一个我平常常用到的 bandwidth 采集脚本,原理…
零基础搭建个性化精准营销 AI 应用,这次手把手教你!
百万学AI系列AI 应用开发大师课已经直播两期了,在前两期的内容中,大家在入门级任务《猫狗分类器》中上手 TensorFlow 开发,通过离线 SDK 在 Android 手机中完成人脸识别应用的部署。在这两个任务中,能成功安装开发环境,…

C++数据类型简析
C语言的基本数据类型有如下四种: 整型,说明符为int;字符型,说明符为char;浮点型(又称实型),说明符为float(单精度),double(双精度&…
浅谈几种区块链网络攻击以及防御方案之51#37攻击
旧博文,搬到 csdn 原文:http://rebootcat.com/2020/04/11/network_attack_of_blockchain_51_attack/ 写在前面的话 自比特币诞生到现在,比特币(网络)经历过大大小小非常多次的攻击,尤其在比特币诞生之初的…
全球Python调查报告:Python 2正在消亡,PyCharm比VS Code更受欢迎
作者 | 唐小引题图 | 自东方 IC出品 | AI科技大本营(ID:rgznai100)一年一度的 Python 开发者调查报告终于发布了。该报告由 Python 软件基金会和 JetBrains 联合发起,已经持续三年,并且参与调查的开发者人数正在逐年上…

中兴V880使用手记之五——刷入recovery
Recovery是安卓系统的一个特殊工作模式,也就是一个刷机的工程界面,给安卓手机刷入Recovery相当于给系统安装了一个dos界面,可以在未开机的状态下,实现系统更新、还原出厂设置、清除手机缓存等。通过一些第三方工具,可以…

windows 系统常用操作
1、所有端口使用情况 netstat -ano 2、查询xxxx端口pid netstat -aon|findstr "xxxx" 3、根据端口Pid查详情 tasklist|findstr "pkd" 4、根据进程pid kill 进程 taskkill /pid pid -f 转载于:https://www.cnblogs.com/vitre/p/5549344.html
浅谈几种区块链网络攻击以及防御方案之日蚀攻击
旧博文,搬到 csdn 原文:http://rebootcat.com/2020/04/12/network_attack_of_blockchain_eclipse_attack/ 写在前面的话 自比特币诞生到现在,比特币(网络)经历过大大小小非常多次的攻击,尤其在比特币诞生之…