C语言如何实现WebService开发?

99ANYc3cd6
预计阅读时长 33 分钟
位置: 首页 C语言 正文

这是一个非常经典且重要的话题,尤其是在企业级应用和嵌入式系统中,C 语言本身并不像 Java、C# 或 Python 那样有原生的、高层次的 WebService 框架,因此实现方式通常依赖于第三方库。

c语言 webservice
(图片来源网络,侵删)

我将从以下几个方面为您全面解析:

  1. WebService 的两种主要风格:首先必须了解,我们谈论的 WebService 主要是哪一种,因为它们的实现方式完全不同。
  2. C 语言实现 SOAP WebService:这是 C 语言在 WebService 领域最经典的应用,通常使用 gSOAP 工具包。
  3. C 语言实现 RESTful WebService:这是目前更主流的风格,C 语言可以通过多种库来实现客户端或服务器端。
  4. 总结与对比:帮助您根据项目需求选择合适的技术方案。

WebService 的两种主要风格

在深入 C 语言实现之前,必须区分两种核心的 WebService 架构风格:

a) SOAP (Simple Object Access Protocol)

  • 特点

    • 协议规范:它是一套完整的协议,不仅仅是 API 设计风格,它定义了消息格式、传输方式(HTTP/HTTPS)、以及处理错误等。
    • 基于 XML:所有的请求和响应都使用 XML 格式进行编码,格式非常严格和冗长。
    • 平台无关:通过 WSDL (Web Services Description Language) 文件来描述服务接口,客户端可以根据 WSDL 文件自动生成调用代码(这个过程叫 "Stub" 或 "Proxy" 生成)。
    • 重量级:因为 XML 和严格的规范,SOAP 消息通常比较大,处理起来也相对较慢。
    • 内置特性:天生支持事务、安全(WS-Security)、异步通信等企业级特性。
  • C 语言与 SOAP:C 语言实现 SOAP 主要依赖 gSOAP 工具包,它通过一个代码生成器,根据 WSDL 文件自动生成 C 语言的客户端存根和服务端框架代码,极大地简化了开发。

    c语言 webservice
    (图片来源网络,侵删)

b) REST (Representational State Transfer)

  • 特点

    • 架构风格:REST 不是一种协议,而是一种设计 Web API 的架构风格,它更轻量、更简单。
    • 基于 HTTP:充分利用 HTTP 协议的原生能力,如 GET(查询)、POST(创建)、PUT(更新)、DELETE(删除)等动词来操作资源。
    • 数据格式灵活:通常使用 JSON 作为数据交换格式(也可以是 XML、纯文本等),JSON 比 XML 更简洁,解析更快。
    • 无状态:服务器不保存客户端的状态,每个请求都包含处理该请求所需的所有信息。
    • 资源导向:一切皆资源,通过 URI (Uniform Resource Identifier) 来唯一标识。
  • C 语言与 REST:C 语言实现 REST 更灵活,没有像 gSOAP 那样的“银弹”工具,通常有两种方式:

    • 客户端:使用 HTTP 客户端库(如 libcurl)发送 HTTP 请求,并使用 JSON 解析库(如 cJSON, jansson)来处理返回的 JSON 数据。
    • 服务器端:使用一个 HTTP 服务器库(如 libmicrohttpd, mongoose)来接收请求,然后根据请求的 URI 和方法调用相应的 C 函数,并返回 JSON 响应。

C 语言实现 SOAP WebService (以 gSOAP 为例)

gSOAP 是 C/C++ 语言开发 SOAP WebService 和客户端的事实标准,它是一个功能强大的工具包,可以处理复杂的 XML 数据绑定和 WSDL 解析。

核心概念

  • wsdl2h:一个工具,读取 WSDL 文件,生成 C/C++ 的头文件(.h),其中包含了服务接口的 C 语言结构体定义。
  • soapcpp2:另一个工具,读取由 wsdl2h 生成的头文件,生成客户端存根(soapClient.c)和服务端框架(soapServer.c)代码。
  • 运行时库gsoap 库,提供底层的序列化、反序列化和网络通信功能。

实现步骤(以一个简单的计算器服务为例)

假设我们有一个 WSDL 文件 calc.wsdl,描述了一个加法服务。

c语言 webservice
(图片来源网络,侵删)

第1步:生成 C 语言头文件

wsdl2h -o calc.h calc.wsdl

这会生成一个 calc.h 文件,里面定义了服务所需的数据结构和函数原型。

第2步:生成客户端和服务端代码

soapcpp2 -j -x -CL calc.h
  • -j: 生成基于文件的后台持久化代码(可选)。
  • -x: 禁用 XML 实例文件生成。
  • -CL: 生成纯 C 语言代码(而不是 C++)。

执行后,你会得到一堆文件,最重要的是:

  • soapClient.c: 客户端调用逻辑。
  • soapServer.c: 服务端处理逻辑。
  • calc.c / calc.nsmap: 你需要在这里实现具体的业务逻辑。

第3步:实现服务端业务逻辑

编辑 calc.c 文件,找到 ns__add 函数(这个名字是根据 WSDL 中的命名空间和操作名生成的),并填入你的代码。

// calc.c
#include "soapH.h" // 包含 gSOAP 生成的头文件
#include "calc.nsmap" // 包含命名空间映射
// 实现加法函数
int ns__add(struct soap *soap, int a, int b, int *result) {
    *result = a + b;
    printf("Server: Adding %d + %d = %d\n", a, b, *result);
    return SOAP_OK; // 返回成功
}
// 实现其他函数...
int ns__sub(struct soap *soap, int a, int b, int *result) { /* ... */ }

第4步:编写服务端主程序

创建一个 server.c 文件,启动服务。

// server.c
#include "soapH.h"
#include "calc.nsmap"
int main(int argc, char **argv) {
    struct soap soap;
    soap_init(&soap);
    // 监听在 8080 端口
    if (soap_bind(&soap, "localhost", 8080, 100) < 0) {
        soap_print_fault(&soap, stderr);
        exit(1);
    }
    printf("Soap server running on port 8080...\n");
    for (;;) {
        if (soap_accept(&soap) < 0) {
            soap_print_fault(&soap, stderr);
            break;
        }
        printf("Connection accepted\n");
        soap_serve(&soap); // 处理请求
        soap_end(&soap);   // 清理
    }
    soap_done(&soap); // 关 soap
    return 0;
}

第5步:编写客户端主程序

创建一个 client.c 文件,调用服务。

// client.c
#include "soapH.h"
#include "calc.nsmap"
int main(int argc, char **argv) {
    struct soap soap;
    soap_init(&soap);
    int result;
    // 调用远程的 add 方法
    if (soap_call_ns__add(&soap, "http://localhost:8080", "ns1", 2, 3, &result) == SOAP_OK) {
        printf("Client: 2 + 3 = %d\n", result);
    } else {
        soap_print_fault(&soap, stderr);
    }
    soap_done(&soap);
    return 0;
}

第6步:编译和运行

你需要安装 gSOAP 库,然后链接它进行编译。

# 编译服务端
gcc server.c calc.c soapC.c soapServer.c -o server -lgsoap++
# 编译客户端
gcc client.c soapC.c soapClient.c -o client -lgsoap++
# 运行
./server &
./client

输出: 服务端: Server: Adding 2 + 3 = 5 客户端: Client: 2 + 3 = 5


C 语言实现 RESTful WebService

REST 的实现更“手动”,因为它不依赖代码生成器。

实现一个 REST 客户端

目标是调用一个公共的 REST API,http://api.example.com/users/123

所需库:

  • libcurl: 用于发送 HTTP 请求。
  • cJSON: 用于解析和生成 JSON。

示例代码:

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include <cJSON.h>
// libcurl 的回调函数,用于接收服务器返回的数据
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    ((char *)userp)[size * nmemb] = 0;
    strcat((char *)userp, contents);
    return size * nmemb;
}
int main(void) {
    CURL *curl;
    CURLcode res;
    char readbuffer[128];
    char *json_response = malloc(128 * sizeof(char)); // 假设响应不超过128字节
    curl = curl_easy_init();
    if (curl) {
        // 设置目标 URL
        curl_easy_setopt(curl, CURLOPT_URL, "http://api.example.com/users/123");
        // 设置回调函数来处理响应
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)readbuffer);
        // 执行请求
        res = curl_easy_perform(curl);
        // 检查错误
        if (res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        } else {
            printf("Raw Response: %s\n", readbuffer);
            // 使用 cJSON 解析 JSON
            cJSON *json = cJSON_Parse(readbuffer);
            if (json) {
                cJSON *name = cJSON_GetObjectItem(json, "name");
                cJSON *email = cJSON_GetObjectItem(json, "email");
                if (name && email) {
                    printf("User Name: %s\n", name->valuestring);
                    printf("User Email: %s\n", email->valuestring);
                }
                cJSON_Delete(json); // 释放 cJSON 对象
            }
        }
        // 清理
        curl_easy_cleanup(curl);
    }
    free(json_response);
    return 0;
}

编译:

gcc client_rest.c -o client_rest -lcurl -lcjson

实现一个 REST 服务器

所需库:

  • libmicrohttpd: 一个轻量级的 HTTP 服务器库。
  • cJSON: 用于构建 JSON 响应。

示例代码(极简版):

#include <microhttpd.h>
#include <stdio.h>
#include <string.h>
#include <cJSON.h>
// 处理 HTTP 请求的回调函数
int answer_to_connection(void *cls,
                         struct MHD_Connection *connection,
                         const char *url,
                         const char *method,
                         const char *version,
                         const char *upload_data,
                         size_t *upload_data_size,
                         void **con_cls) {
    const char *page = "{\"message\": \"Hello from C REST Server!\"}";
    struct MHD_Response *response;
    int ret;
    // 只处理 GET 请求
    if (strcmp(method, "GET") != 0) {
        return MHD_NO; // 不支持此方法
    }
    // 创建 HTTP 响应
    response = MHD_create_response_from_buffer(strlen(page), (void *)page, MHD_RESPMEM_PERSISTENT);
    // 设置响应头
    MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "application/json");
    // 返回响应
    ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
    MHD_destroy_response(response);
    return ret;
}
int main(void) {
    struct MHD_Daemon *daemon;
    // 启动一个 HTTP 服务器,监听 8888 端口
    daemon = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 8888, NULL, NULL,
                              &answer_to_connection, NULL, MHD_OPTION_END);
    if (NULL == daemon) {
        return 1;
    }
    printf("REST server running on port 8888...\n");
    getchar(); // 按任意键停止服务器
    MHD_stop_daemon(daemon);
    return 0;
}

编译:

gcc server_rest.c -o server_rest -lmicrohttpd -lcjson

总结与对比

特性 SOAP (gSOAP) REST (libcurl + libmicrohttpd + cJSON)
风格 协议规范 架构风格
数据格式 XML (严格) JSON (灵活)
核心工具 gSOAP 工具链 (wsdl2h, soapcpp2) 多个独立库组合
代码生成 ,自动生成客户端/服务端框架 ,手动编写所有逻辑
开发效率 高(有 WSDL 的情况下) 低,需要手动处理 HTTP 和 JSON
性能 较低(XML 解析开销大) 较高(JSON 解析快,HTTP 直接)
学习曲线 gSOAP 本身有一定学习成本 需要分别学习多个库
适用场景 企业级应用、需要强类型和契约、需要 WS-* 安全/事务等特性 移动 App 后端、微服务、IoT 设备、API 优先的项目
C 语言定位 非常成熟和主流的方案 灵活、轻量的方案,是现代 C 语言开发 REST API 的常用选择

如何选择?

  • 选择 SOAP (gSOAP)

    • 你的项目已经基于 SOAP 和 WSDL 构建。
    • 你需要与现有的 Java/.NET SOAP 服务进行互操作。
    • 你的应用对事务、安全等企业级特性有硬性要求。
    • 你希望最大限度地减少手动编写网络和序列化代码的工作量。
  • 选择 REST (libcurl/libmicrohttpd + cJSON)

    • 你正在构建一个新的、现代化的 API。
    • 性能是一个关键考量因素。
    • 你的客户端主要是移动应用或 JavaScript 浏览器,它们天生就擅长处理 JSON。
    • 你需要一个更轻量级、更简单的解决方案。
    • 你在嵌入式设备上开发,对资源占用敏感。

在 C 语言的世界里,gSOAP 是 SOAP 的不二之选,而 REST 的实现则更加灵活和多样化,通常依赖于开发者组合使用成熟的 HTTP 和 JSON 库。

-- 展开阅读全文 --
头像
MATLAB如何高效转化为C语言?
« 上一篇 02-12
dede5.7函数方法手册
下一篇 » 02-12

相关文章

取消
微信二维码
支付宝二维码

目录[+]