實現基於SOAP協議的跨語言通信:C++ (gSOAP) 作為服務端,PHP (SoapClient) 作為客戶端。網上流傳的示例多因版本差異、WSDL處理、命名空間映射等問題無法直接運行,本文提供經調試可用的完整方案。
"https://127.0.0.1:8888",
'uri' => "urn:add",
'connection_timeout' => 3
));
var_dump($soap); // 調試:查看可用方法
// 調用遠程方法,參數需關聯數組形式
$params = array('num1' => 6, 'num2' => 2);
$result = $soap->add($params);
var_dump($result);
echo "Sum: " . $result->sum; // 輸出: Sum: 8
} catch (SoapFault $e) {
echo "SOAP Fault: " . $e->getMessage();
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
?>
add.h:服務接口定義soapH.h / soapStub.h:gSOAP生成的存根代碼add.nsmap:命名空間映射表add.wsdl:WSDL描述文件(需放置在可讀路徑)#include "soapH.h"
#include <windows.h>
#include "add.nsmap"
#define BACKLOG 100
DWORD WINAPI process_request(LPVOID*);
int http_get(struct soap*);
int main(int argc, char** argv) {
struct soap soap;
soap_init(&soap);
if (argc < 2) {
// 單線程模式(調試用)
soap_serve(&soap);
soap_destroy(&soap);
soap_end(&soap);
} else {
// 多線程服務器模式
soap.send_timeout = 60;
soap.recv_timeout = 60;
soap.accept_timeout = 3600;
soap.max_keep_alive = 100;
soap.fget = http_get; // 掛載HTTP GET處理函數(用於WSDL分發)
int port = atoi(argv[1]);
SOAP_SOCKET m = soap_bind(&soap, NULL, port, BACKLOG);
if (!soap_valid_socket(m)) exit(1);
printf("Server listening on port %d\n", port);
for (;;) {
SOAP_SOCKET s = soap_accept(&soap);
if (!soap_valid_socket(s)) {
if (soap.errnum) soap_print_fault(&soap, stderr);
break;
}
// 為每個連接創建線程處理
struct soap* tsoap = soap_copy(&soap);
DWORD tid;
HANDLE hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)process_request, tsoap, 0, &tid);
if (!hThread) { printf("Thread creation failed\n"); exit(-1); }
}
}
soap_done(&soap);
return 0;
}
// 線程處理函數
DWORD WINAPI process_request(LPVOID* arg) {
struct soap* tsoap = (struct soap*)arg;
soap_serve(tsoap);
soap_destroy(tsoap);
soap_end(tsoap);
soap_done(tsoap);
free(tsoap);
return 0;
}
// 服務方法實現:加法運算
int ns__add(struct soap* soap, int num1, int num2, int* sum) {
*sum = num1 + num2;
return SOAP_OK;
}
// HTTP GET處理:分發WSDL文件
int http_get(struct soap* soap) {
char* s = strchr(soap->path, '?');
if (!s || strcmp(s, "?wsdl")) return SOAP_GET_METHOD;
FILE* fd = fopen("./add.wsdl", "rb");
if (!fd) return 404;
soap->http_content = "text/xml";
soap_response(soap, SOAP_FILE);
for (;;) {
size_t r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), fd);
if (!r) break;
if (soap_send_raw(soap, soap->tmpbuf, r)) break;
}
fclose(fd);
soap_end_send(soap);
return SOAP_OK;
}
struct Namespace namespaces[] = {
{"SOAP-ENV", "https://schemas.xmlsoap.org/soap/envelope/"},
{"SOAP-ENC", "https://schemas.xmlsoap.org/soap/encoding/"},
{"xsi", "https://www.w3.org/2001/XMLSchema-instance"},
{"xsd", "https://www.w3.org/2001/XMLSchema"},
{"ns", "urn:add"}, // 服務命名空間,與PHP uri參數對應
{NULL, NULL}
};
//gsoap ns service name: add //gsoap ns service style: rpc //gsoap ns service encoding: encoded //gsoap ns service location: https://127.0.0.1:8888 //gsoap ns schema namespace: urn:add int ns__add(int num1, int num2, int* sum);
gSOAP服務端需自行處理 ?wsdl 請求,http_get 函數讀取本地 add.wsdl 文件並以 text/xml 內容類型返回,這是PHP SoapClient 獲取服務描述的關鍵。
soap_accept 接受連接soap_copy 創建獨立上下文CreateThread 運行 process_requestsoap_serve 處理完整SOAP交互| 問題 | 原因 | 解決 |
|---|---|---|
| PHP報「Could not connect to host」 | 服務端未啟動/端口阻塞/防火牆 | 檢查服務進程、netstat -an、防火牆規則 |
| WSDL解析失敗 | WSDL文件路徑錯誤/命名空間不匹配 | 確保http_get正確讀取add.wsdl,nsmap與PHP uri一致 |
| 參數傳遞錯誤 | PHP關聯數組鍵名與WSDL定義不符 | 對照WSDL message part名稱調整$params鍵名 |
| 中文亂碼 | 編碼不一致 | 確保WSDL聲明UTF-8,PHP/服務端統一編碼 |
add.h 接口定義soapcpp2 -c -j add.h 生成存根代碼ns__add 等服務方法libgsoap.a / gsoap++.libserver.exeadd.wsdl 放置於可執行文件同級目錄server.exe 8888gSOAP提供了高性能、標準合規的C++ Web Service實現。關鍵在於:正確的命名空間映射、WSDL文件動態分發、線程安全的上下文管理、與PHP SoapClient的參數格式對接。本方案經實測可在Windows/Linux雙平台穩定運行,適合企業級異構系統集成場景。