You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C++和libcurl向ONVIF网络摄像机发送SOAP请求并获取响应?

问题:C++ 结合libcurl向ONVIF摄像机发送SOAP请求返回空响应

我尝试用C++和libcurl向ONVIF网络摄像机发送SOAP请求,实现代码如下:

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

struct MemoryStruct {
    char *memory;
    size_t size;
};

int main()
{
    std::string pstFld = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\"><SOAP-ENV:Body><tds:GetSystemDateAndTime/></SOAP-ENV:Body></SOAP-ENV:Envelope>";
    CURL *curl;
    CURLcode res;

    struct MemoryStruct chunk;
    chunk.memory = (char *) malloc(1);
    chunk.size = 0;

    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();

    if (curl)
    {
        struct curl_slist *slist = NULL;
        slist = curl_slist_append(slist, "Host: my_ip_address");
        slist = curl_slist_append(slist, "Content-Type: text/xml; charset=utf-8");
        std::string contentLength = "Content-Length: " + std::to_string(pstFld.size());
        slist = curl_slist_append(slist, contentLength.c_str());
        curl_easy_setopt(curl, CURLOPT_URL, "http://my_ip_address/onvif/device_service");
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(curl, CURLOPT_POST, true);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, pstFld);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");

        res = curl_easy_perform(curl);

        if (res != CURLE_OK)
        {
            fprintf(stderr, "Failed: %s\n", curl_easy_strerror(res));
        }
        else
        {
            printf("%lu bytes retrieved.\n", (unsigned long) chunk.size);
        }

        curl_easy_cleanup(curl);
        curl = NULL;
    }

    free(chunk.memory);
    curl_global_cleanup();

    return 0;
}

执行后收到错误:

* upload completely sent off: 6 out of 6 bytes
* Empty reply from server
Failed: Server returned nothing (no headers, no data)

注:用PHP CURL向同一设备发送相同SOAP请求能正常获取XML响应,但C++实现仅能发送请求却收不到响应。


解决方案

核心问题分析

代码存在几个关键问题导致无法接收响应:

  1. 未实现WriteMemoryCallback函数:设置了CURLOPT_WRITEFUNCTION但未提供具体实现,curl无法将服务器响应写入内存缓冲区。
  2. CURLOPT_POSTFIELDS参数错误:直接传入std::string对象而非其C风格字符串指针,可能导致curl无法正确读取POST内容长度。
  3. 手动设置Content-Length易出错:手动计算的长度可能与实际发送的字节数不符,建议交给curl自动处理。

修改后的完整代码

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

struct MemoryStruct {
    char *memory;
    size_t size;
};

// 实现WriteMemoryCallback函数,用于将响应写入内存缓冲区
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *)userp;

    char *ptr = (char *)realloc(mem->memory, mem->size + realsize + 1);
    if(!ptr) {
        // 内存分配失败
        fprintf(stderr, "realloc failed\n");
        return 0;
    }

    mem->memory = ptr;
    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0; // 确保字符串以NULL结尾

    return realsize;
}

int main()
{
    std::string pstFld = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\"><SOAP-ENV:Body><tds:GetSystemDateAndTime/></SOAP-ENV:Body></SOAP-ENV:Envelope>";
    CURL *curl;
    CURLcode res;

    struct MemoryStruct chunk;
    chunk.memory = (char *)malloc(1);
    chunk.size = 0;

    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();

    if (curl)
    {
        struct curl_slist *slist = NULL;
        slist = curl_slist_append(slist, "Host: my_ip_address"); // 替换为设备实际IP
        slist = curl_slist_append(slist, "Content-Type: text/xml; charset=utf-8");
        // 移除手动设置的Content-Length,由curl自动计算
        curl_easy_setopt(curl, CURLOPT_URL, "http://my_ip_address/onvif/device_service"); // 替换为设备实际地址
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(curl, CURLOPT_POST, true);
        // 传入std::string的C风格字符串指针
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, pstFld.c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");

        res = curl_easy_perform(curl);

        if (res != CURLE_OK)
        {
            fprintf(stderr, "Failed: %s\n", curl_easy_strerror(res));
        }
        else
        {
            printf("%lu bytes retrieved.\n", (unsigned long)chunk.size);
            // 打印响应内容
            if(chunk.size > 0) {
                printf("Response:\n%s\n", chunk.memory);
            }
        }

        // 清理HTTP头列表
        curl_slist_free_all(slist);
        curl_easy_cleanup(curl);
    }

    free(chunk.memory);
    curl_global_cleanup();

    return 0;
}

关键修改点说明

  • 添加WriteMemoryCallback实现:这是curl接收响应的核心函数,负责动态扩容缓冲区并写入响应数据。
  • 修正CURLOPT_POSTFIELDS参数:使用pstFld.c_str()获取C风格字符串指针,确保curl正确读取POST内容。
  • 移除手动Content-Length设置:curl会自动计算并设置该头,避免手动计算的误差。
  • 添加curl_slist_free_all(slist):释放HTTP头列表的内存,避免内存泄漏。
  • 新增响应内容打印:方便查看服务器返回的XML数据。

内容的提问来源于stack exchange,提问作者O Connor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:39:26