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

我将从以下几个方面为您全面解析:
- WebService 的两种主要风格:首先必须了解,我们谈论的 WebService 主要是哪一种,因为它们的实现方式完全不同。
- C 语言实现 SOAP WebService:这是 C 语言在 WebService 领域最经典的应用,通常使用 gSOAP 工具包。
- C 语言实现 RESTful WebService:这是目前更主流的风格,C 语言可以通过多种库来实现客户端或服务器端。
- 总结与对比:帮助您根据项目需求选择合适的技术方案。
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 语言的客户端存根和服务端框架代码,极大地简化了开发。
(图片来源网络,侵删)
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,描述了一个加法服务。

第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 库。
