SOAP

SOAP is a simple XML based protocol to let applications exchange information over HTTP. SOAP是一个简单的基于XML的协议,它让应用程序跨HTTP进行信息交换。

Or more simply: SOAP is a protocol for accessing a Web Service. 或者更简单的:SOAP是用于访问网络服务的协议。


What You Should Already Know 你应该已经了解的:

Before you study SOAP you should have a basic understanding of XML and XML Namespaces. 在你学习SOAP 之前,你应该能基本理解以下XML和XML命名空间。 If you want to study these subjects first, please read our XML Tutorial. 如果你想继续看下去,请先访问XML 教程.


What is SOAP? 什么是SOAP?

  • SOAP stands for Simple Object Access Protocol SOAP代表Simple Object Access Protocol(简单对象访问协议)
  • SOAP is a communication protocol  SOAP是通讯协议
  • SOAP is for communication between applications SOAP用于应用程序之间的通讯
  • SOAP is a format for sending messages SOAP是一种信息发送的格式
  • SOAP is designed to communicate via Internet SOAP是通过因特网来通讯的
  • SOAP is platform independent SOAP是独立的平台
  • SOAP is language independent SOAP是独立的语言
  • SOAP is based on XML SOAP是建立在XML基础之上的
  • SOAP is simple and extensible SOAP是简单并可以扩展的
  • SOAP allows you to get around firewalls SOAP允许透过防火墙传播
  • SOAP will be developed as a W3C standard SOAP会以W3C标准为标准而发展起来。

Why SOAP? 为什么使用SOAP?

It is important for application development to allow Internet communication between programs. 对应用程序而言,允许程序之间的因特网通讯是非常重要的。

Today's applications communicate using Remote Procedure Calls (RPC) between objects like DCOM and CORBA, but HTTP was not designed for this. RPC represents a compatibility and security problem; firewalls and proxy servers will normally block this kind of traffic. 如今的应用软件都是通过对象之间的RPC(远程过程调用,如:DCOM和CORBA)来实现通讯的,但是HTTP却不是这样的。RPC表现为兼容性和安全问题。一般情况下,防火墙和代理服务器会阻塞流量。

A better way to communicate between applications is over HTTP, because HTTP is supported by all Internet browsers and servers. SOAP was created to accomplish this. 因为所有的因特网浏览器都支持HTTP,所以在应用程序之间进行通讯还有一个更好的办法,就是跨HTTP。SOAP就是基于这个理论而被创建出来的。

SOAP provides a way to communicate between applications running on different operating systems, with different technologies and programming languages. SOAP提供了一条途径使在不同操作系统上运行的应用程序间实现通讯(通过不同技术和程序设计语言)。


Microsoft and SOAP 微软和SOAP

SOAP is a key element of Microsoft's .NET architecture for future Internet application development. 在未来因特网应用程序发展进程中,SOAP是微软.NET体系结构的主要元素。


SOAP 1.1 was Proposed to W3C 向W3C提议SOAP1.1

UserLand, Ariba, Commerce One, Compaq, Developmentor, HP, IBM, IONA, Lotus, Microsoft, and SAP proposed to W3C, in May 2000, the SOAP Internet protocol that they hope will revolutionize application development by connecting graphic user interface desktop applications to powerful Internet servers using the standards of the Internet: HTTP and XML. 在2000年5月,UserLand, Ariba, Commerce One, Compaq, Developmentor, HP, IBM, IONA, Lotus, Microsoft和 SAP对W3C做出提议:他们所设想的SOAP英特网协议将通过使用HTTP以及XML两个网络标准把图形用户界面桌面应用程序(graphic user interface desktop applications)连接到强大的英特网服务中。


W3C is Working on SOAP 1.2 W3C将在SOAP 1.2上运行

The first public Working Draft on SOAP was published from W3C in December 2001. To read more about the SOAP activities at W3C please visit our W3C tutorial. 2001年12月,SOAP上的第一份公共工作草案将在W3C上公布。如想了解更多SOAP在W3C上的活动,请访问W3C 教程。

/****************************************************************************** * Copyright (c) 2018-2018 TP-Link Systems Inc. * * Filename: soap.c * Version: 1.0 * Description: soap 处理函数 * Author: liyijie<liyijie@tp-link.com.cn> * Date: 2018-12-03 ******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <arpa/inet.h> #include "onvif_passthrough.h" #include "soap_global.h" #include "soap_parse.h" #include "soap_pack.h" #define SOAP_TAG_NUM 256 typedef struct _SOAP_TAG_HANDLE { char tag[LEN_TAG]; S32 (*handle)(SOAP_CONTEXT *soap); }SOAP_TAG_HANDLE; LOCAL SOAP_TAG_HANDLE g_soap_tag_handle[SOAP_TAG_NUM]; /****************************************************************************** * 函数名称: new_soap() * 函数描述: 申请soap结构体 * 输 入: N/A * 输 出: N/A * 返 回 值: soap结构体地址 ******************************************************************************/ SOAP_CONTEXT *new_soap() { SOAP_CONTEXT *soap = NULL; soap = (SOAP_CONTEXT *)ONVIF_MALLOC(sizeof(SOAP_CONTEXT)); if (soap == NULL) { ONVIF_ERROR("malloc g_soap_for_discv failed"); return NULL; } memset(soap, 0, sizeof(SOAP_CONTEXT)); if (OK != onvif_create_buf(&soap->xml_buf, ONVIF_DISCV_BUF_LEN)) { ONVIF_FREE(soap); return NULL; } return soap; } /****************************************************************************** * 函数名称: free_soap() * 函数描述: 释放soap结构体 * 输 入: soap -- soap结构体地址 * 输 出: N/A * 返 回 值: N/A ******************************************************************************/ void free_soap(SOAP_CONTEXT *soap) { if (soap == NULL) { return; } if (soap->fault != NULL) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); ONVIF_FREE(soap->fault); } onvif_free_buf(&soap->xml_buf); ONVIF_FREE(soap); return; } /****************************************************************************** * 函数名称: soap_init() * 函数描述: 初始化soap结构体 * 输 入: soap -- soap结构体地址 * 输 出: N/A * 返 回 值: N/A ******************************************************************************/ void soap_init(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return; } soap->error = OK; soap->in_ip = 0; soap->ifindex = -1; soap->has_header = FALSE; memset(&soap->header, 0, sizeof(SOAP_ENV_HEADER)); if (soap->fault != NULL) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); ONVIF_FREE(soap->fault); soap->fault = NULL; } soap->tag[0] = '\0'; soap->request_begin = NULL; soap->request_end = NULL; soap->types[0] = '\0'; soap->scopes_item[0] = '\0'; soap->scopes_matchby[0] = '\0'; if (soap->xml_buf.start == NULL || (soap->xml_buf.end - soap->xml_buf.start) > ONVIF_DISCV_BUF_LEN) { if (OK != onvif_create_buf(&soap->xml_buf, ONVIF_DISCV_BUF_LEN)) { soap->xml_buf.start = NULL; soap->xml_buf.last = NULL; soap->xml_buf.end = NULL; } } soap->xml_buf.last = soap->xml_buf.start; return; } /****************************************************************************** * 函数名称: soap_out_env_fault() * 函数描述: 组装错误信息报文内容 * 输 入: xml_buf -- 存放输出xml字串的内存地址 * fault_data -- 错误信息 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_env_fault(ONVIF_BUF *xml_buf, SOAP_ENV_FAULT *fault_data) { char attr[LEN_ANY] = {0}; if (xml_buf == NULL) { ONVIF_WARN("xml_buf == NULL."); return ERROR; } /* SOAP-ENV:Fault begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, G_SOAP_ENV_FAULT_STR, NULL)); /* SOAP-ENV:Code begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Code", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_value, NULL)); if (fault_data && fault_data->soap_env_subcode_1) { /* SOAP-ENV:Subcode begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Subcode", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_subcode_1, NULL)); if (fault_data->soap_env_subcode_2) { /* SOAP-ENV:Subcode begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Subcode", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_subcode_2, NULL)); /* SOAP-ENV:Subcode end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Subcode")); } /* SOAP-ENV:Subcode end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Subcode")); } /* SOAP-ENV:Code end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Code")); /* SOAP-ENV:Reason begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Reason", NULL)); /* SOAP-ENV:Text */ snprintf(attr, sizeof(attr), "xml:lang=\"en\""); SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Text", fault_data->soap_env_reason, attr)); /* SOAP-ENV:Reason end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Reason")); /* SOAP-ENV:Fault end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, G_SOAP_ENV_FAULT_STR)); return OK; } /****************************************************************************** * 函数名称: soap_fault() * 函数描述: 填充错误信息结构体内容 * 输 入: soap -- soap结构体地址 * code -- 错误码 * subcode1 -- 子错误码1 * subcode2 -- 子错误码2 * reason -- 错误原因 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ S32 soap_fault(SOAP_CONTEXT *soap, char *code, char *subcode1, char *subcode2, char *reason) { ONVIF_TRACE("Onvif_fault\n"); if (soap == NULL || code == NULL || subcode1 == NULL) { ONVIF_TRACE("soap == NULL || code == NULL || subcode1 == NULL."); return ERROR; } if (soap->fault) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); } else { soap->fault = ONVIF_MALLOC(sizeof(SOAP_ENV_FAULT)); } if (NULL == soap->fault) { ONVIF_WARN("ONVIF_MALLOC fault failed."); return ERROR; } memset(soap->fault, 0, sizeof(SOAP_ENV_FAULT)); soap->fault->soap_env_value = ONVIF_STRDUP(code); if (NULL == soap->fault->soap_env_value) { ONVIF_WARN("ONVIF_STRDUP soap_env_value failed."); return ERROR; } soap->fault->soap_env_subcode_1 = ONVIF_STRDUP(subcode1); if (NULL == soap->fault->soap_env_subcode_1) { ONVIF_WARN("ONVIF_STRDUP soap_env_subcode_1 failed."); return ERROR; } if (subcode2) { soap->fault->soap_env_subcode_2 = ONVIF_STRDUP(subcode2); if (NULL == soap->fault->soap_env_subcode_2) { ONVIF_WARN("ONVIF_STRDUP soap_env_subcode_2 failed."); return ERROR; } } if (reason) { soap->fault->soap_env_reason = ONVIF_STRDUP(reason); if (NULL == soap->fault->soap_env_reason) { ONVIF_WARN("ONVIF_STRDUP soap_env_reason failed."); return ERROR; } } return OK; } /****************************************************************************** * 函数名称: soap_generate_fault() * 函数描述: 组装错误报文xml内容 * 输 入: soap -- soap结构体地址 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_generate_fault(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return ERROR; } //soap_set_fault(soap); if (soap->fault == NULL) { return ERROR; } if (soap->error < 200 && soap->error != SOAP_FAULT) { soap->has_header = FALSE; } return soap_generate_xml((p_out_fun)(soap_out_env_fault), soap, soap->fault); } /****************************************************************************** * 函数名称: soap_serve_request() * 函数描述: soap请求处理函数入口 * 输 入: soap -- soap结构体地址 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ S32 soap_serve_request(SOAP_CONTEXT *soap) { U32 index = 0; PASSTHROUGH_RET ret = 0; if (soap == NULL || soap->tag[0] == '\0') { return ERROR; } ret = onvif_serve_passthrough(soap); if (PASSTHROUGH_ERROR == ret) { return ERROR; } else if (PASSTHROUGH_SOAP_FAULT == ret) { SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } else if (PASSTHROUGH_NOT_MATCH == ret) { for (index = 0; index < SOAP_TAG_NUM; index++) { if (NULL == g_soap_tag_handle[index].handle) { SOAP_IF_FAIL_RET(soap_fault(soap, "SOAP-ENV:Receiver", "ter:ActionNotSupported", NULL, NULL)); soap->error = SOAP_FAULT; SOAP_IF_FAIL_RET(soap_generate_fault(soap)); break; } if (soap_match_tag(soap->tag, g_soap_tag_handle[index].tag)) { if (g_soap_tag_handle[index].handle(soap)) { if (soap->fault == NULL) { soap_fault(soap, "SOAP-ENV:Sender", "ter:InvalidArgVal", NULL, "error"); soap->error = SOAP_FAULT; } SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } break; } } if (index >= SOAP_TAG_NUM) { SOAP_IF_FAIL_RET(soap_fault(soap, "SOAP-ENV:Receiver", "ter:ActionNotSupported", NULL, NULL)); soap->error = SOAP_FAULT; SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } } /* UDP直接回复,HTTP在上一层回复 */ if (soap->use_udp == TRUE && soap->xml_buf.start != NULL && soap->xml_buf.start != soap->xml_buf.last) { return onvif_send_udp_packet(soap->sock, soap->in_ip, soap->sin_port, soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); } return OK; } LOCAL S32 soap_env_fault_handle(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return ERROR; } ONVIF_TRACE("SOAP-ENV:Fault handle."); /* do nothing and no response */ return OK; } /****************************************************************************** * 函数名称: soap_tag_handle_add() * 函数描述: 注册soap请求处理函数 * 输 入: tag -- 请求tag * handle -- 处理函数 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ void soap_tag_handle_add(char *tag, void *handle) { U32 index = 0; if ((NULL == tag) || (strlen(tag) >= 64) || (NULL == handle)) { ONVIF_ERROR("soap handle add error, invalid arg."); return; } while ((index < SOAP_TAG_NUM) && (NULL != g_soap_tag_handle[index].handle)) { index++; } if (index >= SOAP_TAG_NUM) { ONVIF_ERROR("soap handle add error, max support %d tags.", SOAP_TAG_NUM); return; } snprintf(g_soap_tag_handle[index].tag, sizeof(g_soap_tag_handle[index].tag), "%s", tag); g_soap_tag_handle[index].handle = handle; return; } /****************************************************************************** * 函数名称: soap_tag_handle_init() * 函数描述: 初始化soap请求处理函数 * 输 入: N/A * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ void soap_tag_handle_init() { U32 index = 0; for (index = 0; index < SOAP_TAG_NUM; index++) { memset(g_soap_tag_handle[index].tag, 0, sizeof(g_soap_tag_handle[index].tag)); g_soap_tag_handle[index].handle = NULL; } soap_tag_handle_add("SOAP-ENV:Fault", soap_env_fault_handle); return; } ——+能不能详细地一行一行一个一个变量一个一个函数地给我解释下代码
最新发布
09-02
<think> 我们一行一行解释代码,但由于代码量较大,我们将分函数进行详细解释。每个函数的功能、输入输出、变量含义都会说明。 ### 1. 头文件和宏定义 ```c /****************************************************************************** * Copyright (c) 2018-2018 TP-Link Systems Inc. * * Filename: soap.c * Version: 1.0 * Description: soap 处理函数 * Author: liyijie<liyijie@tp-link.com.cn> * Date: 2018-12-03 ******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <arpa/inet.h> #include "onvif_passthrough.h" #include "soap_global.h" #include "soap_parse.h" #include "soap_pack.h" #define SOAP_TAG_NUM 256 // 定义标签处理函数的最大数量 ``` - 头文件:包含标准C库、网络操作等头文件,以及项目相关的头文件。 - `SOAP_TAG_NUM`:定义最多可以注册256个标签处理函数。 ### 2. 结构体定义 ```c typedef struct _SOAP_TAG_HANDLE { char tag[LEN_TAG]; // 标签字符串,长度由LEN_TAG定义(例如64) S32 (*handle)(SOAP_CONTEXT *soap); // 处理函数指针,参数为SOAP_CONTEXT指针,返回整型(S32) } SOAP_TAG_HANDLE; LOCAL SOAP_TAG_HANDLE g_soap_tag_handle[SOAP_TAG_NUM]; // 全局标签处理函数数组 ``` - `SOAP_TAG_HANDLE`:结构体,包含一个标签字符串和一个处理函数指针。 - `g_soap_tag_handle`:全局数组,用于存储注册的标签处理函数。 ### 3. `new_soap` 函数 ```c SOAP_CONTEXT *new_soap() { SOAP_CONTEXT *soap = NULL; soap = (SOAP_CONTEXT *)ONVIF_MALLOC(sizeof(SOAP_CONTEXT)); // 分配SOAP_CONTEXT结构体内存 if (soap == NULL) { ONVIF_ERROR("malloc g_soap_for_discv failed"); return NULL; } memset(soap, 0, sizeof(SOAP_CONTEXT)); // 初始化为0 // 创建XML缓冲区,大小为ONVIF_DISCV_BUF_LEN(可能是4096等) if (OK != onvif_create_buf(&soap->xml_buf, ONVIF_DISCV_BUF_LEN)) { ONVIF_FREE(soap); // 如果创建失败,释放soap内存 return NULL; } return soap; // 返回soap结构体指针 } ``` - 功能:创建并初始化一个SOAP_CONTEXT结构体,分配内存并设置XML缓冲区。 - 输入:无。 - 输出:成功返回SOAP_CONTEXT指针,失败返回NULL。 - 变量: - `soap`:指向SOAP_CONTEXT结构体的指针。 ### 4. `free_soap` 函数 ```c void free_soap(SOAP_CONTEXT *soap) { if (soap == NULL) { return; } // 释放fault结构体内部成员 if (soap->fault != NULL) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); ONVIF_FREE(soap->fault); // 释放fault结构体本身 } onvif_free_buf(&soap->xml_buf); // 释放XML缓冲区 ONVIF_FREE(soap); // 释放soap结构体 return; } ``` - 功能:释放SOAP_CONTEXT结构体及其内部所有动态分配的内存。 - 输入:`soap` - 要释放的SOAP_CONTEXT指针。 - 输出:无。 ### 5. `soap_init` 函数 ```c void soap_init(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return; } // 重置部分字段 soap->error = OK; soap->in_ip = 0; soap->ifindex = -1; soap->has_header = FALSE; memset(&soap->header, 0, sizeof(SOAP_ENV_HEADER)); // 清零header // 释放并重置fault if (soap->fault != NULL) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); ONVIF_FREE(soap->fault); soap->fault = NULL; } // 清空标签和请求指针 soap->tag[0] = '\0'; soap->request_begin = NULL; soap->request_end = NULL; // 清空类型和范围相关字段 soap->types[0] = '\0'; soap->scopes_item[0] = '\0'; soap->scopes_matchby[0] = '\0'; // 检查XML缓冲区是否存在或是否过大,如果是则重新创建 if (soap->xml_buf.start == NULL || (soap->xml_buf.end - soap->xml_buf.start) > ONVIF_DISCV_BUF_LEN) { // 尝试创建新的缓冲区 if (OK != onvif_create_buf(&soap->xml_buf, ONVIF_DISCV_BUF_LEN)) { // 创建失败,将缓冲区指针置为NULL soap->xml_buf.start = NULL; soap->xml_buf.last = NULL; soap->xml_buf.end = NULL; } } // 重置缓冲区写指针到开始位置 soap->xml_buf.last = soap->xml_buf.start; return; } ``` - 功能:重置SOAP_CONTEXT结构体,用于复用该结构体处理新的请求。 - 输入:`soap` - 要初始化的SOAP_CONTEXT指针。 - 输出:无。 ### 6. `soap_out_env_fault` 函数 ```c LOCAL S32 soap_out_env_fault(ONVIF_BUF *xml_buf, SOAP_ENV_FAULT *fault_data) { char attr[LEN_ANY] = {0}; if (xml_buf == NULL) { ONVIF_WARN("xml_buf == NULL."); return ERROR; } /* SOAP-ENV:Fault begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, G_SOAP_ENV_FAULT_STR, NULL)); /* SOAP-ENV:Code begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Code", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_value, NULL)); // 如果有子错误码1 if (fault_data && fault_data->soap_env_subcode_1) { /* SOAP-ENV:Subcode begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Subcode", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_subcode_1, NULL)); // 如果有子错误码2 if (fault_data->soap_env_subcode_2) { /* SOAP-ENV:Subcode begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Subcode", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_subcode_2, NULL)); /* SOAP-ENV:Subcode end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Subcode")); } /* SOAP-ENV:Subcode end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Subcode")); } /* SOAP-ENV:Code end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Code")); /* SOAP-ENV:Reason begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Reason", NULL)); /* SOAP-ENV:Text */ snprintf(attr, sizeof(attr), "xml:lang=\"en\""); // 设置语言属性为英文 SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Text", fault_data->soap_env_reason, attr)); /* SOAP-ENV:Reason end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Reason")); /* SOAP-ENV:Fault end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, G_SOAP_ENV_FAULT_STR)); return OK; } ``` - 功能:生成SOAP错误报文(XML格式)并写入缓冲区。 - 输入: - `xml_buf`:XML缓冲区。 - `fault_data`:错误信息结构体。 - 输出:成功返回OK,失败返回ERROR。 - 变量: - `attr`:临时存储XML属性。 ### 7. `soap_fault` 函数 ```c S32 soap_fault(SOAP_CONTEXT *soap, char *code, char *subcode1, char *subcode2, char *reason) { ONVIF_TRACE("Onvif_fault\n"); if (soap == NULL || code == NULL || subcode1 == NULL) { ONVIF_TRACE("soap == NULL || code == NULL || subcode1 == NULL."); return ERROR; } // 如果已有fault,先释放其内部成员 if (soap->fault) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); } else { // 否则分配新的fault结构体 soap->fault = ONVIF_MALLOC(sizeof(SOAP_ENV_FAULT)); } if (NULL == soap->fault) { ONVIF_WARN("ONVIF_MALLOC fault failed."); return ERROR; } memset(soap->fault, 0, sizeof(SOAP_ENV_FAULT)); // 清零 // 复制错误码 soap->fault->soap_env_value = ONVIF_STRDUP(code); if (NULL == soap->fault->soap_env_value) { ONVIF_WARN("ONVIF_STRDUP soap_env_value failed."); return ERROR; } // 复制子错误码1 soap->fault->soap_env_subcode_1 = ONVIF_STRDUP(subcode1); if (NULL == soap->fault->soap_env_subcode_1) { ONVIF_WARN("ONVIF_STRDUP soap_env_subcode_1 failed."); return ERROR; } // 如果有子错误码2,复制 if (subcode2) { soap->fault->soap_env_subcode_2 = ONVIF_STRDUP(subcode2); if (NULL == soap->fault->soap_env_subcode_2) { ONVIF_WARN("ONVIF_STRDUP soap_env_subcode_2 failed."); return ERROR; } } // 如果有原因文本,复制 if (reason) { soap->fault->soap_env_reason = ONVIF_STRDUP(reason); if (NULL == soap->fault->soap_env_reason) { ONVIF_WARN("ONVIF_STRDUP soap_env_reason failed."); return ERROR; } } return OK; } ``` - 功能:填充SOAP错误信息到soap->fault结构体中。 - 输入: - `soap`:SOAP上下文。 - `code`:主错误码。 - `subcode1`:子错误码1。 - `subcode2`:子错误码2(可选)。 - `reason`:错误原因文本(可选)。 - 输出:成功返回OK,失败返回ERROR。 ### 8. `soap_generate_fault` 函数 ```c LOCAL S32 soap_generate_fault(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return ERROR; } if (soap->fault == NULL) { return ERROR; } // 如果不是SOAP_FAULT错误且错误码小于200,则标记为没有头部 if (soap->error < 200 && soap->error != SOAP_FAULT) { soap->has_header = FALSE; } // 调用soap_generate_xml生成错误报文,使用soap_out_env_fault作为输出函数 return soap_generate_xml((p_out_fun)(soap_out_env_fault), soap, soap->fault); } ``` - 功能:生成SOAP错误报文,并存储到soapXML缓冲区中。 - 输入:`soap` - SOAP上下文。 - 输出:成功返回OK,失败返回ERROR。 ### 9. `soap_serve_request` 函数 ```c S32 soap_serve_request(SOAP_CONTEXT *soap) { U32 index = 0; PASSTHROUGH_RET ret = 0; if (soap == NULL || soap->tag[0] == '\0') // 检查soap和标签是否有效 { return ERROR; } // 先尝试透传处理 ret = onvif_serve_passthrough(soap); // 处理透传返回结果 if (PASSTHROUGH_ERROR == ret) { return ERROR; } else if (PASSTHROUGH_SOAP_FAULT == ret) // 需要返回SOAP错误 { SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } else if (PASSTHROUGH_NOT_MATCH == ret) // 透传未处理,需要本地处理 { // 在全局标签处理函数数组中查找匹配的标签 for (index = 0; index < SOAP_TAG_NUM; index++) { // 遇到空槽位,说明后面没有注册的函数了,退出循环 if (NULL == g_soap_tag_handle[index].handle) { // 没有找到处理函数,生成错误 SOAP_IF_FAIL_RET(soap_fault(soap, "SOAP-ENV:Receiver", "ter:ActionNotSupported", NULL, NULL)); soap->error = SOAP_FAULT; SOAP_IF_FAIL_RET(soap_generate_fault(soap)); break; } // 如果当前标签匹配 if (soap_match_tag(soap->tag, g_soap_tag_handle[index].tag)) { // 调用处理函数 if (g_soap_tag_handle[index].handle(soap)) { // 如果处理函数返回非0(错误),并且没有设置fault,则设置一个默认错误 if (soap->fault == NULL) { soap_fault(soap, "SOAP-ENV:Sender", "ter:InvalidArgVal", NULL, "error"); soap->error = SOAP_FAULT; } // 生成错误报文 SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } break; // 跳出循环 } } // 如果遍历完数组都没有找到匹配的标签 if (index >= SOAP_TAG_NUM) { SOAP_IF_FAIL_RET(soap_fault(soap, "SOAP-ENV:Receiver", "ter:ActionNotSupported", NULL, NULL)); soap->error = SOAP_FAULT; SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } } // 如果是UDP传输,并且缓冲区有数据,则直接发送UDP响应 if (soap->use_udp == TRUE && soap->xml_buf.start != NULL && soap->xml_buf.start != soap->xml_buf.last) { return onvif_send_udp_packet(soap->sock, soap->in_ip, soap->sin_port, soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); } return OK; } ``` - 功能:处理SOAP请求的入口函数,根据标签分发请求。 - 输入:`soap` - SOAP上下文。 - 输出:成功返回OK,失败返回ERROR。 - 变量: - `index`:循环索引。 - `ret`:透传处理函数的返回值。 ### 10. `soap_env_fault_handle` 函数 ```c LOCAL S32 soap_env_fault_handle(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return ERROR; } ONVIF_TRACE("SOAP-ENV:Fault handle."); /* do nothing and no response */ return OK; } ``` - 功能:用于处理SOAP-ENV:Fault标签的函数。这里什么也不做,因为没有请求需要响应。 - 输入:`soap` - SOAP上下文。 - 输出:始终返回OK。 ### 11. `soap_tag_handle_add` 函数 ```c void soap_tag_handle_add(char *tag, void *handle) { U32 index = 0; // 检查参数有效性 if ((NULL == tag) || (strlen(tag) >= 64) || (NULL == handle)) { ONVIF_ERROR("soap handle add error, invalid arg."); return; } // 查找第一个空槽位 while ((index < SOAP_TAG_NUM) && (NULL != g_soap_tag_handle[index].handle)) { index++; } // 如果数组已满,报错 if (index >= SOAP_TAG_NUM) { ONVIF_ERROR("soap handle add error, max support %d tags.", SOAP_TAG_NUM); return; } // 注册标签和处理函数 snprintf(g_soap_tag_handle[index].tag, sizeof(g_soap_tag_handle[index].tag), "%s", tag); g_soap_tag_handle[index].handle = handle; return; } ``` - 功能:注册一个标签处理函数到全局数组中。 - 输入: - `tag`:标签字符串。 - `handle`:处理函数指针。 - 输出:无。 ### 12. `soap_tag_handle_init` 函数 ```c void soap_tag_handle_init() { U32 index = 0; // 初始化全局数组,全部清零 for (index = 0; index < SOAP_TAG_NUM; index++) { memset(g_soap_tag_handle[index].tag, 0, sizeof(g_soap_tag_handle[index].tag)); g_soap_tag_handle[index].handle = NULL; } // 注册内置的Fault处理函数 soap_tag_handle_add("SOAP-ENV:Fault", soap_env_fault_handle); return; } ``` - 功能:初始化标签处理函数数组,并注册内置的处理函数(如SOAP-ENV:Fault)。 - 输入:无。 - 输出:无。 ### 整体模块结构 1. **初始化**:调用`soap_tag_handle_init`初始化路由表。 2. **创建上下文**:`new_soap`创建一个新的SOAP上下文。 3. **初始化上下文**:`soap_init`初始化或重置上下文。 4. **解析请求**:由外部函数(如`soap_parse_request`)解析请求,填充上下文(如设置`tag`)。 5. **处理请求**:调用`soap_serve_request`,根据标签查找处理函数并执行。 6. **错误处理**:若处理出错,使用`soap_fault`设置错误信息,然后`soap_generate_fault`生成错误报文。 7. **发送响应**:如果是UDP,直接发送;如果是HTTP,由上层发送。 8. **释放资源**:调用`free_soap`释放上下文。 ### 对外接口 - `soap_serve_request`:请求处理入口。 - `soap_fault`:业务函数用于报告错误。 - `soap_tag_handle_add`:业务模块注册处理函数。 - `soap_tag_handle_init`:模块初始化(系统启动时调用)。 --- §§相关问题§§ 1. 在`soap_serve_request`函数中,为什么处理完请求后需要判断`use_udp`标志? 2. `soap_match_tag`函数是如何实现标签匹配的? 3. 如果业务处理函数返回错误但没有设置`soap->fault`,会发生什么? 4. 如何扩展支持更多的标签处理函数(超过256个)? </think> ### SOAP 处理模块逐行详解 #### 1. 头文件与数据结构 ```c #include "soap_global.h" // SOAP核心数据结构定义 #include "soap_parse.h" // XML解析相关函数 #include "soap_pack.h" // XML打包相关函数 #define SOAP_TAG_NUM 256 // 标签处理器最大数量 typedef struct _SOAP_TAG_HANDLE { char tag[LEN_TAG]; // 存储SOAP标签名(如"tds:GetDeviceInformation") S32 (*handle)(SOAP_CONTEXT*); // 标签处理函数指针 } SOAP_TAG_HANDLE; LOCAL SOAP_TAG_HANDLE g_soap_tag_handle[SOAP_TAG_NUM]; // 全局标签处理路由表 ``` - **关键数据结构**: - `SOAP_CONTEXT`:SOAP请求处理上下文 - `ONVIF_BUF`:XML缓冲区管理结构 - `SOAP_ENV_FAULT`:错误信息容器 --- ### 核心函数详解 #### 1. 上下文管理 ```c SOAP_CONTEXT* new_soap() { SOAP_CONTEXT* soap = ONVIF_MALLOC(sizeof(SOAP_CONTEXT)); // 动态分配上下文内存 memset(soap, 0, sizeof(SOAP_CONTEXT)); // 清零初始化 onvif_create_buf(&soap->xml_buf, ONVIF_DISCV_BUF_LEN); // 创建XML缓冲区(默认4KB) return soap; } void free_soap(SOAP_CONTEXT* soap) { // 释放错误结构体内存 if (soap->fault) { ONVIF_FREE(soap->fault->soap_env_value); // 释放主错误码字符串 ONVIF_FREE(soap->fault->soap_env_subcode_1); // 释放子错误码1 // ...其他错误字段释放... ONVIF_FREE(soap->fault); // 释放错误结构体本身 } onvif_free_buf(&soap->xml_buf); // 释放XML缓冲区 ONVIF_FREE(soap); // 释放上下文本身 } void soap_init(SOAP_CONTEXT* soap) { soap->error = OK; // 重置错误状态 soap->in_ip = 0; // 清空来源IP memset(&soap->header, 0, sizeof(SOAP_ENV_HEADER)); // 清空SOAP头部 // 重置XML缓冲区指针 soap->xml_buf.last = soap->xml_buf.start; } ``` --- #### 2. 错误处理机制 ```c S32 soap_fault(SOAP_CONTEXT* soap, char* code, char* subcode1, char* subcode2, char* reason) { // 动态分配并填充错误结构体 soap->fault->soap_env_value = ONVIF_STRDUP(code); // "SOAP-ENV:Sender" soap->fault->soap_env_subcode_1 = ONVIF_STRDUP(subcode1); // "ter:InvalidArgVal" // ...其他字段赋值... } LOCAL S32 soap_out_env_fault(ONVIF_BUF* xml_buf, SOAP_ENV_FAULT* fault_data) { // 生成标准SOAP错误报文 soap_element_begin_out(xml_buf, "SOAP-ENV:Fault", NULL); // <SOAP-ENV:Fault> soap_element_begin_out(xml_buf, "SOAP-ENV:Code", NULL); // <Code> soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_value, NULL); // <Value>... // ...完整错误树构建... } ``` --- #### 3. 请求处理引擎 ```c S32 soap_serve_request(SOAP_CONTEXT* soap) { PASSTHROUGH_RET ret = onvif_serve_passthrough(soap); // 1. 尝试透传模式 if (ret == PASSTHROUGH_NOT_MATCH) { // 2. 本地处理模式 for (index = 0; index < SOAP_TAG_NUM; index++) { // 路由匹配:比较请求标签和注册标签 if (soap_match_tag(soap->tag, g_soap_tag_handle[index].tag)) { // 调用注册的处理函数 if (g_soap_tag_handle[index].handle(soap)) { // 处理失败时生成错误响应 soap_generate_fault(soap); } break; } } } // 3. UDP协议直接发送响应 if (soap->use_udp) { onvif_send_udp_packet(soap->sock, soap->in_ip, soap->sin_port, soap->xml_buf.start, soap->xml_buf.last - soap->xml_buf.start); } return OK; } ``` --- #### 4. 标签路由注册 ```c void soap_tag_handle_add(char* tag, void* handle) { // 在路由表中查找空槽位 while (index < SOAP_TAG_NUM && g_soap_tag_handle[index].handle) index++; // 注册标签处理函数 snprintf(g_soap_tag_handle[index].tag, sizeof(...), "%s", tag); g_soap_tag_handle[index].handle = handle; // 存储函数指针 } void soap_tag_handle_init() { // 初始化路由表:注册内置错误处理器 soap_tag_handle_add("SOAP-ENV:Fault", soap_env_fault_handle); } ``` --- #### 5. 特殊错误处理器 ```c LOCAL S32 soap_env_fault_handle(SOAP_CONTEXT* soap) { ONVIF_TRACE("SOAP-ENV:Fault handle."); /* 空实现:对错误报文不响应 */ return OK; } ``` --- ### 关键变量说明 | 变量名 | 类型 | 作用描述 | |--------------------------|--------------------|-----------------------------------| | `soap->xml_buf` | ONVIF_BUF | XML输入/输出缓冲区 | | `soap->tag[64]` | char | 当前处理的SOAP请求标签 | | `soap->fault` | SOAP_ENV_FAULT* | 错误信息容器 | | `soap->use_udp` | BOOL | 传输协议标识(UDP=1/HTTP=0) | | `g_soap_tag_handle` | SOAP_TAG_HANDLE[256] | 标签-处理函数映射表 | | `fault_data->soap_env_value` | char* | 主错误码(如"SOAP-ENV:Sender") | --- ### 完整工作流程 ```mermaid sequenceDiagram participant Client participant soap_serve_request participant Passthrough participant TagRouter participant Handler Client->>soap_serve_request: SOAP请求 soap_serve_request->>Passthrough: 尝试透传 alt 透传成功 Passthrough-->>Client: 直接响应 else 本地处理 soap_serve_request->>TagRouter: 路由匹配 TagRouter->>Handler: 调用注册函数 Handler->>Business: 执行业务逻辑 Business-->>soap_serve_request: 生成响应 soap_serve_request->>Client: 返回响应 end ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值