📹 第六章 SOAP及gSOAP实战
📄 一句话概括

Ubuntu 有现成的包,一条 apt install 就完事,不用自己编。 但装包版在 /usr、自编版在 /usr/local——同样的代码,一台编得过、一台编不过。

📝 本节和前面几节的关系

上一节在 CentOS 上自己编,这一节在 Ubuntu 上装包,两边的差别本身就值得记一下。 这一节的重点不是命令,是「路径不同导致的编译参数差异」,这是跨平台迁移时最常撞的墙。

一、两边的命令对照

要做的事 CentOS(yum) Ubuntu(apt)
装编译器 yum install gcc gcc-c++ apt install build-essential
装 gSOAP 自己编 apt install gsoap libgsoap-dev
装开发库 openssl-devel libssl-dev

第二行是最大差别:Ubuntu 直接有包,CentOS 官方源里没有,所以书里给两个系统各写一节。 但「有包」不等于「版本够新」:发行版的包跟着系统版本走, Ubuntu 20.04 带的是 2.8.107,而 ONVIF 项目常用 2.8.116(08.08.02)。 版本差一点通常没事,但生成 ONVIF 代码时某些类型可能对不上—— 这是「装包版」唯一要留心的地方。

二、包名分两个,少一个就编不过

包 里面有什么 只装一个的后果
gsoap soapcpp2、wsdl2h 两个可执行文件 —
libgsoap-dev soapH.h、libgsoap.so、头文件 编译时报「找不到 soapH.h」

这个「工具包 + 开发包」的拆分是 Debian 系的通例:命令行工具一个包,头文件和库另一个包。 只装 gsoap 你会得到一个能跑 soapcpp2、但编不出程序的机器—— 生成代码那一步成功了,编译那一步全红。 看到「soapH.h 找不到」先查这个,别去查 -I 路径。

三、路径不同,编译参数就得不同

| 项 | 自编(CentOS) | 装包(Ubuntu) | |---|---|---|---| | 安装位置 | /usr/local | /usr | | g++ 默认搜吗 | 头文件不搜、库不搜 | 都搜 | | 要加的参数 | -I/usr/local/include -L/usr/local/lib | 不用加 | | 卸载 | make uninstall(不一定有) | apt remove |

这一张表是本节的核心。 g++ 的默认搜索路径里没有 /usr/local/include 和 /usr/local/lib (严格说 gcc 的默认 prefix 是 /usr,/usr/local 要显式给)。 所以同一份代码:Ubuntu 上 g++ client.cpp ... 就过;CentOS 上必须加两个 -I/-L。 反过来,如果你在 Ubuntu 上加了 -I/usr/local/include,而那里正好有一份老版本的头文件, 就会「头文件是新版的、库是老版的」——链接期报一堆 undefined reference,查半天。

四、链接参数,第三行最常漏

g++ client.cpp soapC.cpp soapClient.cpp \
    -lgsoap++ -lssl -lcrypto -lpthread
参数 为什么 漏了报什么
-lgsoap++ C++ 版库(纯 C 用 -lgsoap) undefined reference to soap_...
-lssl -lcrypto 用了 HTTPS 就必须加 undefined reference to SSL_...
-lpthread gSOAP 内部用线程锁 undefined reference to pthread_...

第三行最容易被漏,因为在 Windows 上不需要(Windows 的线程 API 在 kernel32 里, 随 CRT 一起链)。从 Windows 迁到 Linux 的项目,第一个撞的就是它。 第一行也有个坑:C++ 项目要用 -lgsoap++,不是 -lgsoap。 用错了会「符号名对不上」—— 因为 C++ 的符号经过 name mangling,和 C 版库里的符号根本不是同一个名字。

五、动手 Demo:Ubuntu 上就一条命令,但坑在别处

本机是 Windows,没有 apt,所以这一节也跑不了 apt 命令。 Demo 做的是「路径与链接参数的自检」: 把两边的路径规则、包名、链接参数写进一张表,逐条标出「谁负责」—— 哪一条是 apt 装好的、哪一条要自己加。 这段代码在任何平台都能跑,它只是打印一张对照表并做几项路径判断。

import os, shutil

# ① 两个前缀下,关键文件在不在
for pre in ("/usr", "/usr/local"):
    for rel in ("bin/soapcpp2", "include/soapH.h",
                "lib/libgsoap.so", "lib/libgsoap++.so"):
        p = os.path.join(pre, rel)
        print(p, "有" if os.path.exists(p) else "没有")

# ② 装包版和自编版的编译参数差异,由「文件在哪」推出来
need_inc = os.path.exists("/usr/local/include/soapH.h") and \
           not os.path.exists("/usr/include/soapH.h")
print("要加 -I/usr/local/include 吗:", need_inc)

实跑输出:

--------------------------------------------------------------------
Ubuntu 上就一条命令,但坑在别处
--------------------------------------------------------------------
**Ubuntu 有现成的包,不用自己编。**
  这一节对照两边的差别,再说清「装包版」和「自编版」的区别。

**两边的命令对照:**

  要做的事          CentOS(yum)                 Ubuntu(apt)
  ----------------------------------------------------------------------------------
  装编译器          `yum install gcc gcc-c++`     `apt install build-essential`
  装 gSOAP          自己编                        `apt install gsoap libgsoap-dev`
  装开发库          `openssl-devel`               `libssl-dev`

  **第二行是最大差别**:Ubuntu 直接有包,**CentOS 要自己编**。
  **这也是为什么书里给两个系统各写一节。**

  **注意包名分两个**:`gsoap` 是工具(`soapcpp2`/`wsdl2h`),`libgsoap-dev` 是开发库。
  **只装前者,编译时会报「找不到 soapH.h」。**

**自编版和装包版的三个区别:**

  项              自编(CentOS)                装包(Ubuntu)
  --------------------------------------------------------------------------
  版本            你下载的那个                  跟着发行版走,可能偏旧
  安装位置        `/usr/local`                  `/usr`
  卸载            `make uninstall`(不一定有)  `apt remove`

  **第二行会影响到编译参数**:自编版在 `/usr/local`,**`g++` 默认不搜这个目录的头文件**,
  要加 `-I/usr/local/include`。
  **而 Ubuntu 装包版在 `/usr`,默认就搜**。
  所以同样的代码在两台机器上**一个编得过、一个编不过。**

**Linux 上编 gSOAP 程序,链接参数一般长这样:**

  `g++ client.cpp soapC.cpp soapClient.cpp -lgsoap++ -lssl -lcrypto -lpthread`

  参数                为什么
  ------------------------------------------------------
  `-lgsoap++`         C++ 版库,纯 C 用 `-lgsoap`
  `-lssl -lcrypto`    用了 HTTPS 就必须加
  `-lpthread`         gSOAP 内部用线程锁

  **第三行最常被漏**,报错是 `undefined reference to pthread_...`。
  **在 Windows 上不需要,所以从 Windows 迁过来的项目必踩。**

本机(Windows)这一整张表全是「没有」——因为 /usr 这个前缀在 Windows 上不存在。 所以这段代码在本机只验证了「路径判断逻辑」本身,没验证过真实的 Ubuntu 环境。 「要加 -I 吗」这一行是推出来的、不是查文档的: 如果头文件只在 /usr/local/include 而不在 /usr/include,那默认路径就找不到,必须加。 这个推理链比背结论可靠——换台机器、换个 prefix,逻辑照样成立。 正文里那张「链接参数」表是本机完全无法验证的部分(没有 g++)。 它来自 gSOAP 的构建要求,不是我实测的——这一类知识只能标注来源,不能假装跑过。

30 秒实验

  1. 在任意一台 Ubuntu 上 apt install gsoap libgsoap-dev,再跑这段探针,看四个文件在哪。
  2. 故意只装 gsoap 不装 libgsoap-dev,编译一个最小程序,看报错是不是「找不到 soapH.h」。
  3. 把 -lpthread 去掉,看 undefined reference to pthread_create 长什么样。

常见误区

  1. 以为装 gsoap 就齐了。 libgsoap-dev 是另一个包,缺它编不过。
  2. 以为自编版和装包版只是版本不同。 安装前缀不同(/usr/local vs /usr), 会导致编译参数必须不同。
  3. 以为 C++ 项目链接 -lgsoap 也行。 要用 -lgsoap++, C 和 C++ 的符号名经过 name mangling 之后对不上。
自测三问(点开对答案) 1. Ubuntu 上装 gSOAP 要装几个包?分别装什么? —— **两个**:`gsoap`(工具:`soapcpp2`/`wsdl2h`)和 `libgsoap-dev`(头文件 + 库)。 2. 为什么自编版编程序要加 `-I/usr/local/include`? —— 因为 **`g++` 的默认搜索路径里不含 `/usr/local/include`**,**自编版装在那里**。 装包版在 `/usr`,默认就搜。 3. `-lpthread` 为什么在 Linux 上不能漏、Windows 上不用加? —— gSOAP 内部用线程锁,**Linux 上 `pthread` 是单独的库**; **Windows 的线程 API 在 `kernel32` 里,随 CRT 一起链。**

下一节回到 Windows:在 VS 里用 gSOAP 开发客户端,看「生成的桩被调用」这件事的全貌。

相关笔记