核心原理
WebService 主要有两种标准:SOAP 和 RESTful。

-
SOAP (Simple Object Access Protocol):
- 特点:基于 XML,协议规范非常严格,功能强大(支持事务、安全等),但相对复杂和“重”。
- 适用场景:企业级应用,需要高可靠性、事务支持和复杂交互的场景。
- C 调用方式:需要生成客户端代理类,代理类会自动封装 SOAP 请求的 XML 结构,开发者只需像调用本地方法一样调用远程方法。
-
RESTful (Representational State Transfer):
- 特点:基于 HTTP 协议,使用 JSON 或 XML 作为数据格式,轻量、简单、易于理解,通常使用标准的 HTTP 方法(GET, POST, PUT, DELETE)。
- 适用场景:移动应用、Web 前后端分离、微服务架构。
- C 调用方式:C 语言需要手动或使用库来构造 HTTP 请求(如
POST请求,请求体为 JSON),并解析返回的 JSON 响应。
对于“Java WebService”这个经典术语,通常默认指的是基于 SOAP 的服务,本文将重点介绍 C 调用 Java SOAP WebService 的方法,并简要提及 RESTful 的调用。
主流工具和方法
在 C/C++ 环境中,调用 SOAP WebService 最主流的工具是 gSOAP。

gSOAP (强烈推荐)
gSOAP 是一个强大的开源 C/C++ 开发工具包,用于开发 WebService 客户端和服务端,它的核心优势在于:
- 自动代码生成:你可以提供一个 WSDL (Web Services Description Language) 文件,gSOAP 会自动为你生成 C/C++ 的客户端存根(Stub)和服务端骨架(Skeleton),存根文件就像一个本地 C/C++ 函数库,调用它就等于在调用远程的 WebService。
- 跨平台:支持 Windows, Linux, macOS, 嵌入式系统等。
- 功能全面:支持 SOAP 1.1/1.2, WSDL, XML 数据绑定等。
- 协议支持:除了 SOAP,也支持 XML-RPC 和 REST。
工作流程:
- 获取 Java WebService 的 WSDL 文件地址。
- 使用 gSOAP 的
wsdl2h工具解析 WSDL,生成头文件(.h)。 - 使用 gSOAP 的
soapcpp2工具处理头文件,生成客户端存根代码(.cpp,.h)。 - 在你的 C/C++ 项目中包含这些生成的文件,并像调用普通函数一样调用 WebService 方法。
详细步骤:使用 gSOAP 调用 Java SOAP WebService
假设我们有一个简单的 Java WebService,它提供一个根据用户名查询用户信息的功能。
第 1 步:准备 Java WebService (服务端)
这里我们用一个简单的 Java Spring Boot 项目作为示例,它会发布一个 SOAP WebService。

添加依赖 (pom.xml)
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring SOAP Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<!-- WSDL4J for WSDL generation -->
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
</dependencies>
创建服务接口和实现
// src/main/java/com/example/demo/User.java
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "user", propOrder = {"name", "age"})
public class User {
protected String name;
protected int age;
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
// src/main/java/com/example/demo/UserService.java
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public interface UserService {
@WebMethod
User getUserByName(String name);
}
// src/main/java/com/example/demo/UserServiceImpl.java
import org.springframework.stereotype.Service;
@Service
@WebService(endpointInterface = "com.example.demo.UserService")
public class UserServiceImpl implements UserService {
@Override
public User getUserByName(String name) {
System.out.println("C客户端请求查询用户: " + name);
User user = new User();
user.setName(name);
user.setAge(30); // 模拟返回数据
return user;
}
}
发布服务
// src/main/java/com/example/demo/SoapApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
@SpringBootApplication
@EnableWs
public class SoapApplication {
public static void main(String[] args) {
SpringApplication.run(SoapApplication.class, args);
}
@Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet() {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext applicationContext);
servlet.setTransformSchemaLocations(false);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
}
启动这个 Java 应用,你的 SOAP WebService 就运行在 http://localhost:8080/ws,访问 http://localhost:8080/ws/userservice.wsdl 即可看到 WSDL 文件。
第 2 步:使用 gSOAP 生成客户端代码
安装 gSOAP
从 gSOAP 官网 下载并解压,确保 wsdl2h 和 soapcpp2 可执行文件在你的系统 PATH 中。
生成客户端代码 打开终端,执行以下命令:
# 1. 从 WSDL 生成 C/C++ 头文件 (.h) # -j 选项告诉 wsdl2h 使用 gSOAP 的 stdsoap2.h 作为基础 wsdl2h -j -o user_service.h http://localhost:8080/ws/userservice.wsdl # 2. 使用 soapcpp2 处理头文件,生成客户端存根代码 # -c 选项生成 C 代码 # -x 选项忽略 SOAP 错误处理,简化示例 soapcpp2 -c -x user_service.h
执行后,你会得到一堆生成的文件,其中最重要的是:
soapC.c/soapC.h:序列化和反序列化函数,用于处理 XML 和 C 数据结构之间的转换。soapStub.h:客户端函数的声明。user_serviceSoapProxy.h:包含实际的客户端代理类user_serviceSoapProxy。
第 3 步:编写 C 客户端代码
创建一个 client.c 文件,调用生成的代理类。
// client.c
#include "user_serviceSoapProxy.h" // 包含代理类
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
// 1. 创建代理对象
struct soap soap;
soap_init(&soap);
// 2. 创建请求和响应对象
_ns1__getUserByName req;
_ns1__getUserByNameResponse res;
// 3. 设置请求参数
req.name = "John Doe";
// 4. 调用 WebService
// 参数: soap对象, 请求对象, 响应对象, 服务端URL
int result = soap_call___ns1__getUserByName(&soap, NULL, NULL, &req, &res);
// 5. 处理结果
if (result == SOAP_OK) {
if (res.return_ != NULL) {
printf("调用成功!\n");
printf("用户名: %s\n", res.return_->name);
printf("年龄: %d\n", res.return_->age);
} else {
printf("调用成功,但返回结果为空,\n");
}
} else {
// 打印错误信息
soap_print_fault(&soap, stderr);
}
// 6. 清理资源
soap_destroy(&soap); // 释放响应对象
soap_end(&soap); // 断开连接,释放内存
soap_done(&soap); // 销 soap 结构体
return 0;
}
第 4 步:编译和运行
编译
你需要链接 gSOAP 的库文件 (libgsoap++),编译命令如下(假设 gSOAP 库在 /path/to/gsoap):
# 将生成的 .c 文件和 gSOAP 的 stdsoap2.c 一起编译 gcc client.c soapC.c stdsoap2.c -o client -I/path/to/gsoap -L/path/to/gsoap -lgsoap++ # 如果找不到库,可能需要指定完整路径,例如在 Linux 上 # gcc client.c soapC.c stdsoap2.c -o client -I/usr/local/include -L/usr/local/lib -lgsoap++
运行 确保你的 Java 服务端正在运行。
./client
预期输出:
调用成功!
用户名: John Doe
年龄: 30
你的 Java 服务端控制台会打印:
C客户端请求查询用户: John Doe
简要介绍:C 调用 RESTful WebService
如果你的 Java WebService 是 RESTful 风格(使用 JSON),过程会完全不同,你不会使用 gSOAP,而是使用 C 语言的 HTTP 客户端库,如 libcurl。
步骤:
- 构造 HTTP 请求:使用
libcurl发送一个POST或GET请求到 Java 服务的 URL。 - 设置请求头:
Content-Type: application/json。 - 设置请求体:将你要发送的数据(一个 JSON 字符串)作为请求体。
- 获取响应:
libcurl会将服务器返回的 JSON 数据写入一个你提供的缓冲区。 - 解析 JSON:使用一个 C 语言的 JSON 解析库(如 cJSON 或 Jansson)来解析返回的 JSON 字符串,提取你需要的数据。
示例代码片段 (伪代码):
#include <curl/curl.h>
#include <cjson/cJSON.h>
// libcurl 的回调函数,用于接收数据
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
((char*)userp)[0] = '\0'; // 清空缓冲区
strncat((char*)userp, contents, size * nmemb);
return size * nmemb;
}
int main() {
CURL *curl;
CURLcode res;
char readBuffer[1024];
char json_request_body[256];
// 1. 构造 JSON 请求体
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "name", "Jane Doe");
strcpy(json_request_body, cJSON_Print(root));
cJSON_Delete(root);
// 2. 初始化 libcurl
curl = curl_easy_init();
if(curl) {
// 3. 设置 URL 和请求头
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8080/api/user");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_request_body);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// 4. 设置回调函数来接收响应
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, readBuffer);
// 5. 执行请求
res = curl_easy_perform(curl);
// 6. 检查错误并处理响应
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
} else {
printf("服务器响应: %s\n", readBuffer);
// 7. 使用 cJSON 解析响应
cJSON *response = cJSON_Parse(readBuffer);
if (response) {
cJSON *name = cJSON_GetObjectItem(response, "name");
cJSON *age = cJSON_GetObjectItem(response, "age");
if (name && age) {
printf("解析结果 - 用户名: %s, 年龄: %d\n", name->valuestring, age->valueint);
}
cJSON_Delete(response);
}
}
// 清理
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
}
return 0;
}
总结与对比
| 特性 | gSOAP (SOAP) | libcurl + cJSON (RESTful) |
|---|---|---|
| 适用协议 | SOAP 1.1/1.2 | REST/HTTP |
| 数据格式 | XML | JSON ( |
| 核心优势 | 自动代码生成,高度集成,处理复杂 XML 绑定 | 轻量,灵活,JSON 易于人阅读和调试 |
| 工作流程 | WSDL -> 代理代码 -> 调用本地函数 | 手动构造 HTTP 请求 -> 手动解析 JSON |
| 学习曲线 | 较陡峭,需要理解 gSOAP 工具链 | 较平缓,主要是学会使用 libcurl 和一个 JSON 库 |
| 依赖 | gSOAP 工具包和库 | libcurl, cJSON (或类似库) |
| 推荐场景 | 调用传统的、基于 WSDL 的企业级 Java WebService | 调用现代的 RESTful API,特别是微服务 |
最终建议:
- 如果你的 Java WebService 是标准的 SOAP 服务,gSOAP 是不二之选,虽然前期配置稍显繁琐,但一旦生成客户端代码,后续的开发和维护会非常简单。
- 如果你的 Java WebService 是 RESTful 风格,或者你无法使用 gSOAP,libcurl + cJSON 的组合是最佳实践,它非常灵活且是 C 语言生态中的标准方案。
