1.背景
在重构的历史项目中,有一点是语言转换:从 PHP 转至 Goland ,在压缩资源的同时,享受语言实现级别进步带来的性能红利。
在功能点覆盖上存在白盒 SDK 加密场景,如 流量校验、对端数据传输…等。
-
原 PHP 实现中是通过扩展方式进行调用:将白盒 SDK 以
.so
文件的形式补充至php-config --extension-dir
,并添加extension=XXX.so
到php.ini
配置文件。
2.实现方式
在 Goland 中,也提供了像 Java、Python、C++ …中调用 动态库的原生能力,只是在调度过程实现上存在略微不同。
之前架构设计中提到的 千万级入口服务 —— 框架设计(一:组件模式) 组件也是通过 动态库的方式实现。
2.1.C 库 .so 文件生成
ar -x XXprotect_sec.a
g++ -shared *.o -o XXprotect_sec.so
有了 .so
文件之后,还需要有 C 库 的接口文件 .h
文件。.h
文件用来在 Goland 调度过程中进行二次封装调用。
2.2.C 库 .h 文件
在 Goland 中调用 C 库接口,允许存在中间处理环节,即二次封装。而进行的前提是基于 .h
文件提供的 C 库接口。
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* brief XX get version function
* return version string.
*/
typedef const void * (*FN10)(void);
#define XX_VERSION()
((FN10)gsecfv3[1])()
......
/**
* brief XX initialize function
* return return context if successful
*/
typedef void * (*FN11)(const char *, char *, char *);
#define XX_INIT(akey, encrypt_key, decrypt_key)
((FN11)gsecfv3[2])(akey, encrypt_key, decrypt_key)
2.3.Goland 调用实现
2.3.1 整体
先整体看:在 package
下方的依赖包引入中,import "C"
必须单起一行,且二次封装的接口务必以注释的方式在其上方编排;下方 Goland 函数中使用时,以 C 库开头直接调用即可。
需要注意的是:
- 注释中的
#include .h
行,不仅仅是 C 库的接口,也可以是 C 中的原生包。比如:#include<stdio.h>
… - Goland 中调用使用到的 C 库,务必在注释行引入。如果不引入,项目编译时就会报出
could not determine kind of name for C.XX...
错误。 - C 动态库编译的版本与 Goland 执行版本中的需兼容。否则会报出 :
c running gcc failed: exit status 1 //opt/compiler/gcc-8.2/lib64/libstdc++.so.6: undefined reference to...
错误。
package encrypt
/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -lXXprotect_sec -Wl,-rpath,./
#include "XXlib_v3.h" // C 库接口文件
const char *XX_version() {
return XX_VERSION();
}
void *XX_init(const char *akey, char *encrypt_key, char *decrypt_key) {
return XX_INIT(akey, encrypt_key, decrypt_key);
}
......
int XX_destory(void *context) {
return XX_DESTORY(context);
}
*/
import "C" // import “C” 必须单起一行,并且紧跟在注释行之后
import (
"context"
"reflect"
......
"unsafe"
)
func XXEncrypt(ctx context.Context, tp string, data string, ver string, iv string) (string, error) {
iv = iv + "x00"
// 打印白盒SDK版本号
log.Println(C.GoString(C.bdswb_version()))
......
return "", nil
}
2.3.2 注释块部分
接下来,看到注释块中,前两行标记了当前 C 库调用动态库 的位置及特征。使用的是 #cgo
cgo 详细介绍 方式。
在注释主体中,是对 C 库接口的二次封装,类似 Hook 方式,给予开发者适当的操作空间。
需要注意的是:
- 动态库在项目编译时,需要在 环境变量中引入。可覆盖、追加变量
LD_LIBRARY_PATH
,否则无法从位置路径中获取到.so
动态库。比如:export LD_LIBRARY_PATH=/root/codes/XXXso/path:$LD_LIBRARY_PATH
- 最好是在
~/.bash_profile
中变更,这样对每次终端都生效,不要忘记重启source ~/.bash_profile
。
2.3.3 逻辑实现部分
在 Goland 实现部分,特地写了 iv = iv + "x00"
代码行。这里是将 Goland 中的字符串转为 C 中的格式。这种写法是未引用 C 库原生函数的情况下进行。另一种方式是使用原生库,不过需要在 注释部分追加对应的包。
需要注意的是:
3.小结
整体下来看,Goland 中直调 C 库 操作是很流程化的。在此前提下,也给予了调用规则适当的灵活性。
但综上,是不建议初学者使用的。尤其是不充分了解、熟悉 Goland、C 语言特性 及 项目主体逻辑、C 库逻辑 的场景;
原文地址:https://blog.csdn.net/qq_34417408/article/details/133363243
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_22860.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!