杰瑞科技汇

C如何调用Java的WebService?

核心原理

WebService 主要有两种标准:SOAPRESTful

C如何调用Java的WebService?-图1
(图片来源网络,侵删)
  1. SOAP (Simple Object Access Protocol)

    • 特点:基于 XML,协议规范非常严格,功能强大(支持事务、安全等),但相对复杂和“重”。
    • 适用场景:企业级应用,需要高可靠性、事务支持和复杂交互的场景。
    • C 调用方式:需要生成客户端代理类,代理类会自动封装 SOAP 请求的 XML 结构,开发者只需像调用本地方法一样调用远程方法。
  2. 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

C如何调用Java的WebService?-图2
(图片来源网络,侵删)

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。

工作流程

  1. 获取 Java WebService 的 WSDL 文件地址。
  2. 使用 gSOAP 的 wsdl2h 工具解析 WSDL,生成头文件(.h)。
  3. 使用 gSOAP 的 soapcpp2 工具处理头文件,生成客户端存根代码(.cpp, .h)。
  4. 在你的 C/C++ 项目中包含这些生成的文件,并像调用普通函数一样调用 WebService 方法。

详细步骤:使用 gSOAP 调用 Java SOAP WebService

假设我们有一个简单的 Java WebService,它提供一个根据用户名查询用户信息的功能。

第 1 步:准备 Java WebService (服务端)

这里我们用一个简单的 Java Spring Boot 项目作为示例,它会发布一个 SOAP WebService。

C如何调用Java的WebService?-图3
(图片来源网络,侵删)

添加依赖 (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 生成客户端代码

安装 gSOAPgSOAP 官网 下载并解压,确保 wsdl2hsoapcpp2 可执行文件在你的系统 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

步骤:

  1. 构造 HTTP 请求:使用 libcurl 发送一个 POSTGET 请求到 Java 服务的 URL。
  2. 设置请求头Content-Type: application/json
  3. 设置请求体:将你要发送的数据(一个 JSON 字符串)作为请求体。
  4. 获取响应libcurl 会将服务器返回的 JSON 数据写入一个你提供的缓冲区。
  5. 解析 JSON:使用一个 C 语言的 JSON 解析库(如 cJSONJansson)来解析返回的 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 语言生态中的标准方案。
分享:
扫描分享到社交APP
上一篇
下一篇