最近需要对接一个接口, 人家提供了两种调用方式, 第一种是基于 IE 浏览器的 Active, 第二种是动态链接库 dll. 我们公司的产品不支持 IE, 所以只能通过调用 dll 来完成了.
之前我已经用 Java 实现了这个代理, 但是感觉很笨重, 依赖于容器还有 JVM 一大堆, 这个代理要安装在客户端电脑上, 基于 Http 协议来调用, 然后透传参数调用 dll, 将返回的结果转换为 JSON.
如今我想用 Golang 来实现这个功能, 它不依赖特定的运行环境, 而且天然高并发, 适合做网络通信相关, 结合了 Java 的高效和 C 的高性能.
一, 功能拆解
http 模块
调用 dll
返回结果处理
功能大致上分为三步, 三步独立完成, 分步进行. 第一个很简单, 网上一搜就出来了, 也没遇到什么波折. 然后直接测试调用 dll, 这个耽误了不少时间, 不知道问题出在哪, 又不好测试, 打的包不在 dll 所在文件夹里, 每次还要复制过去测试.
二, 遇到的问题
1. 对于入参和出参的处理
void _stdcall PostAndRecvEx(IN char* pszPost, OUT char* pszRecv)
参数:
pszPost 输入 xml 信息
pszRecv 返回 xml 信息
返回值: 无
2. 程序异常退出
由于 dll 文件在指定的安装目录, 将 golang 生成的 exe 复制过去后才能找到它, 但是一运行却出现闪退, 根本不知道哪里出了问题.
3. 中文乱码
这里体现在两个地方, 一是输入参数无法被 dll 中的函数识别, 一直报错. 第二个是输出的 xml 中包含中文乱码.
4. 返回结果无法映射到结构体
要转成 JSON, 首先应该定义结构体, 我是利用返回结果直接测试的, 但是返回的 xml 无法映射到结构体对应的字段上, 怀疑是编码问题, 返回的 xml 指定了 GBK(encoding="gbk"). 由于是模拟的返回数据, 我直接修改了编码发现可以映射到指定字段. 网上查了下没什么好办法, 看到这个很妙就直接用了:
xmlDataRsp = strings.Replace(xmlDataRsp, "gbk", "UTF-8", -1)
三, 涉及知识点
1. 如何使用 http 模块写一个服务 (找个官方的示例)
- import (
- "fmt"
- "log"
- "net/http"
- )
- func handler(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
- }
- func main() {
- http.HandleFunc("/", handler)
- log.Fatal(http.ListenAndServe(":8080", nil))
- }
2. 如何调用 dll(这个也有几种方式)
- dll := syscall.NewLazyDLL("NISEC_SKSC.dll")
- proc := dll.NewProc("PostAndRecvEx")
3. 捕获异常 明确问题点
- defer func() {
- // 恢复程序的控制权
- err := recover()
- if err != nil {
- fmt.Println(err)
- }
- }()
- // 可能出问题的代码
4. 如何使用 cgo
- //#include <stdio.h>
- //#include <stdlib.h>
- import "C"
- import (
- "bytes"
- "strconv"
- "unsafe"
- )
- func main() {
- cs := C.CString("你好")
- defer C.free(unsafe.Pointer(cs))
- println(cs)
- }
一开始以为注释是没用的, 后来才知道编译器识别加注释的 c 代码 (通过 import "C")
5. 底层数据类型不一致的处理
使用 java 调用 dll 的时候我特地在后面加了结束符:
JNAOperateDll.instanceDll.PostAndRecvEx((xmlStr + "\0") .getBytes("gbk"), b);
用 golang 的时候, 我也一直不知道如何处理, 到底该用字符串加 "\0", 还是字节加 \ x00.
这就要了解 golang 和 c 对字符的处理, c 需要通过结束符来判断字符串何时结束, 而 golang 不需要, 它有长度标识.
6. xml 转 JSON
golang 要转 JSON, 需要定义结构体来搭桥, 所有属性都要有对应的字段. 网上找到一个不需要结构体的, 但是 star 数量不多, 而且没下载下来.
四, 总结
1. 做事情要知道事情的边界
像我们这个需求, 要和其他接口交互, 而且不是基于 http 协议, 我们公司的产品放弃了 IE 的支持, 所以只能通过调用 dll 来实现. 这就是边界, 无法突破的壁垒.
2. 在边界内力求做到最好
知道了事情的边界, 力求在边界内做到最好. 起初我就觉得用 Java 来实现不太好, 但是客户催得紧, 还要调试这些接口, 就迅速用 Java 实现了. 后来回过头我就想着如何优化它, 如何一键启动甚至开机自启, 如何设置成 Windows 服务, 但是这都改变不了它庞大且不利于传播的事实, 后来想起来 golang, 这玩意真是天生做这个的料.
3. 遇到问题不要轻易放弃
在写这个代理的过程中, 遇到了较多困难, 毕竟是不熟悉的语言, 虽然它的功能看起来很简单, 但是我一开始还是有点虚, 例如看到了指针, 我就怕遇到了这些环环相扣的陷进去了, 不过我还是鼓励自己不断去尝试, 边做边学.
4. 形成自己做事的方法
在做这个工具的时候, 我是分几步进行的, 大方向上没有变, 最后把代码合起来, 就是一个完整的功能. 我觉得这样拆分可以各个击破, 专注于一个个点, 而且可以将难度分化, 给人以信心.
这次我也没有一开始就去看 golang 的语法, 学习那些东西, 我是直接开干, 需要什么我就去查什么. 当你学会一种开发语言后, 需要借助于另一个语言完成某个功能, 这种方法我觉得很好, 不会陷入另一个语言的大坑, 针对目标在实践中学习, 高效而深刻.
来源: https://www.cnblogs.com/lucare/p/10587315.html