SOAP

一:为什么需要SOAP?

随着计算机技术的不断发展,现代企业面临的环境越来越复杂,其信息系统大多数为多平台、多系统的复杂系统。这就要求今天的企业解决方案具有广泛的兼容能 力,可以支持不同的系统平台、数据格式和多种连接方式,要求在Internet 环境下,实现系统是松散耦合的、跨平台的,与语言无关的,与特定接口无关的,而且要提供对Web 应用程序的可靠访问。

随 着异种计算环境的不断增加,各种系统间的互操作性就愈显得必要,要求系统能够无缝地进行通信和共享数据,从而在 Internet 环境下,消除巨大的信息孤岛,实现信息共享、进行数据交换,达到信息的一致性。Web services 希望实现不同的系统之间能够用"软件-软件对话"的方式相互调用,打破了软件应用、网站和各种设备之间的格格不入的状态,实现"基于WEB无缝集成"的目 标。

今年四月份的时候,W3C联盟召开了第一次 Web 服务专题研讨会,目的为探索 W3C 应向哪个方向发展才能实现新兴的 Web 服务架构的标准化,期间提出了一个"Web 服务堆栈"的构想,如下图,从图中可以看出,SOAP在WEB服务堆栈中作为用于 XML 消息传递的一种非常普遍的协议,发挥着十分重要的作用。


图 1:SOAP 在 WEB 服务堆栈中的作用
SOAP 在 WEB 服务堆栈中的作用




回页首


二:什么是SOAP?

SOAP(Simple Object Access Protocol )简单对象访问协议是在分散或分布式的环境中交换信息的简单的协议,是一个基于XML的协议,它包括四个部分:SOAP封装(envelop),封装定义 了一个描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们的框架;SOAP编码规则(encoding rules),用于表示应用程序需要使用的数据类型的实例; SOAP RPC表示(RPC representation),表示远程过程调用和应答的协定;SOAP绑定(binding),使用底层协议交换信息。

虽然这四个部分都作为SOAP的一部分,作为一个整体定义的,但他们在功能上是相交的、彼此独立的。特别的,信封和编码规则是被定义在不同的XML命名空间(namespace)中,这样使得定义更加简单。

SOAP 的两个主要设计目标是简单性和可扩展性。这就意味着有一些传统消息系统或分布式对象系统中的某些性质将不是SOAP规范的一部分。比如:分布式垃圾收集 (Distributed garbage collection)、成批传送消息(Boxcarring or batching of messages)、对象引用 (Objects-by-reference(which requires distributed garbage collection))、对象激活 (Activation(which requires objects-by-reference))。

SOAP消息举例:

1.第一个例子阐明了SOAP中一个简单的通信信息,包括了两个不是SOAP定义而是应用程序定义的元素:头块元素alertcontrol 和体块元素alert。头块元素包括两个参数:priority 和expires。体块元素包括的是实际传送的信息。


图 2:SOAP 中一个简单的通信信息
SOAP 中一个简单的通信信息
(EXAMPLE 1)

2.SOAP 通信与底层的不同协议和不同的交换格式有关,下面的例子SOAP使用HTTP作为底层通信协议,从而可以很好的使用request/response机制 来传送信息。 SOAP/HTTP请求包括一个GetLastTradePrice的块元素,该请求携带一个字符串参数和ticker符号,在SOAP应答中返回一个浮 点数。XML名域用来区分SOAP标志符和应用程序特定的标志符。


图 3:使用 HTTP 作为底层通信协议
使用 HTTP 作为底层通信协议
(EXAMPLE 2)

3. 例3 展示的是StockQuote SOAP服务信息,是对例2的请求作出的一条应答消息。


图 4:对例 2 的请求作出的一条应答消息
对例 2 的请求作出的一条应答消息
(EXAMPLE 3)



回页首


三:SOAP与CORBA,COM/DCOM的区别?

在SOAP刚刚提出来的时候,许多人就提出了疑问:SOAP与CORBA和DCOM的区别何在?

  1. CORBA(Common Object Request Broker
    Architecture)公共对象请求代理体系结构是由OMG组织制订的一种标准的面向对象应用程序体系规范。由对象请求代理ORB、对象服务、公共设 施、域接口和应用接口这几个部分组成。其核心部分是对象请求代理ORB(Object Request Broker)。ORB提供了一种机制,通过这种机制,对象可以透明的发出请求和接收响应。分布的、可以互操作的对象可以利用ORB构造可以互操作的应 用。ORB可看作是在对象之间建立客户/服务关系的一种中间件。基于ORB,客户可以透明的调用服务对象提供的方法,该服务对象可以与客户运行在同一台机 器上,也可以运行在其他机器上通过网络与客户进行交互。ORB截取客户发送的请求,并负责在该软件总线上找到实现该请求的服务对象,然后完成参数、方法调 用,并返回最终结果。CORBA 1.1 由对象管理组织在 1991 年发布。定义了接口定义语言(IDL)和应用编程接口(API),从而通过实现对象请求代理(ORB)来激活客户/服务器的交互。CORBA 2.0 于 1994 年的 12 月发布定义了如何跨越不同的 ORB 提供者而进行通讯。
  2. COM/DCOM(Component Object Model / Distributed Component Object
    Model )是微软公司提出的分布式组件对象模型标准,支持在局域网、广域网甚至Internet上不同计算机的对象之间的通讯。DCOM基于COM的应用程序、组 件、工具等的基础之上,处理网络协议的低层次的细节问题,而不必关心太多的网络协议细节,从而使用户能够集中精力解决用户所要求的问题。DCOM位于应用 程序的组件之间,将组件以不可见的方式胶合在一起组成具有完整功能的应用程序。
  3. SOAP 与CORBA,DCOM/COM的比较。

    3.1 首先指出的是SOAP不会取代CORBA,COM/DCOM,三者的概念有所区别。COM/DCOM是个组件模型标准,CORBA是分布式应用的服务标 准。CORBA和DCOM为分布式应用程序建立服务,服务对象来执行客户端调用的服务。而SOAP是基于XML和HTTP的分布式对象的通信协议,是 COM/DCOM和CORBA对象进行通讯的协议。实际上,利用SOAP的互操作性和CORBA强大的执行能力,两者可以很好的结合在一起。 OMG (Object Management Group responsible for the CORBA specification)正在关注这方面的发展。

    3.2. CORBA应用程序和DCOM应用程序不能实现互操作,两者不能在一起协作。因为在ORPC(Object RPC)协议中,用ObjRef代表了一个正在运行对象的引用;在CORBA/IIOP(Internet Inter-Orb Protocol)中,用交换可互操作对象引用IOR(Interoperable Object Reference)代表一个服务器的对象引用。不幸的是,IOR 与 ObjRef不能够关联起来。然而,使用SOAP可以实现在垂直应用层面上CORBA ,DCOM技术的水平整合,能够更好的集成CORBA,DCOM为一个整体。

    3.3 SOAP并没有定义信息的语义,服务质量,基于INTERNET的事务处理。而是采用 XML 进行消息编码,正确的处理需要服务器和客户端本身来执行,理解和执行彼此使用的信息格式(ONE-TO- ONE,REQUEST/REPLY,BROADCAST,ETC),应用程序本身在语义解析中扮演着十分重要的角色。而CORBA,DCOM表示了传送 信息的语义,对参数和返回值使用二进制编码。可对诸如参数名称或类型的任何元信息都不编码,但使中介很难处理消息。又因为每个系统使用不同的二进制编码, 系统间的互操作的很难实现。

    3.4 尽管CORBA可以在不同的平台上执行,DCOM可以在微软的各种平台上运行,但是基于CORBA和DCOM的解决方案必须依赖于单一的应用程序。比如 说,假如运行的是DCOM服务器程序,所有的分布式的客户端不得不运行于微软的操作平台上。CORBA 虽然可以运行于不同的平台,但CORBA的互操作性并没有在更高层的服务上进行扩展,如安全性和事务处理,在这种情况下,许多提供的服务没有得到很好的优 化。DCOM和CORBA适合于服务器--服务器间的通讯,但是对于客户端--服务器的通讯十分脆弱,尤其当客户程序分布在INTERNET上更是如此。

    3.5 SOAP不象DCOM一样试图定义分布式系统的所有元素,SOAP没有提供分布式类库,类型安全检查,版本控制等等,SOAP比它处于一个更低的层次,有 点类似于IIOP在CORBA的作用,DCOM却提供了一些额外的协议功能,是IIOP 或者SOAP所不具备的。然而,许多. DCOM的额外功能只有在服务器��服务器间通信时才会用到,对于客户端��服务器之间的通信则是多余的。





回页首


四:SOAP=RPC+HTTP+XML

SOAP简单的理解,就是这样的一个开放协议SOAP=RPC+HTTP+XML:采用HTTP作为底层通讯协议;RPC作为一致性的调用途径,XML作 为数据传送的格式,允许服务提供者和服务客户经过防火墙在INTERNET进行通讯交互。RPC的描叙可能不大准确,因为SOAP一开始构思就是要实现平 台与环境的无关性和独立性,每一个通过网络的远程调用都可以通过SOAP封装起来,包括DCE(Distributed Computing Environment ) RPC CALLS,COM/DCOM CALLS, CORBA CALLS, JAVA CALLS,etc。

SOAP 使用 HTTP 传送 XML,尽管HTTP 不是有效率的通讯协议,而且 XML 还需要额外的文件解析(parse),两者使得交易的速度大大低于其它方案。但是XML 是一个开放、健全、有语义的讯息机制,而 HTTP 是一个广泛又能避免许多关于防火墙的问题,从而使SOAP得到了广泛的应用。但是如果效率对你来说很重要,那么你应该多考虑其它的方式,而不要用 SOAP。

为了更好的理解SOAP,HTTP,XML如何工作的,不妨先考虑一下COM/DCOM的运行机 制,DCOM处理网络协议的低层次的细节问题,如PROXY/STUB间的通讯,生命周期的管理,对象的标识。在客户端与服务器端进行交互的时 候,DCOM采用NDR(Network Data Representation)作为数据表示,它是低层次的与平台无关的数据表现形式。

DCOM 是有效的,灵活的,但也是很复杂的。而SOAP的一个主要优点就在于它的简单性,SOAP使用HTTP作为网络通讯协议,接受和传送数据参数时采用XML 作为数据格式,从而代替了DCOM中的NDR格式,SOAP和 DCOM执行过程是类似的,如下图,但是用XML取代 NDR作为编码表现形式,提供了更高层次上的抽象,与平台和环境无关。


图 5:用 XML 取代 NDR 作为编码表现形式
用 XML 取代 NDR 作为编码表现形式

客户端发送请求时,不管客户端是什么平台的,首先把请求转换成XML格式,SOAP网关可自动执行这个转换。为了保证传送时参数,方法名,返回值的唯一性,SOAP协议使用了一个私有标记表,从而服务端的SOAP网关可以正确的解析,这有点类似于COM/DCOM

中 的桩(STUB)。转化成XML格式后,SOAP终端名(远程调用方法名)及其他的一些协议标识信息被封装成HTTP请求,然后发送给服务器。如果应用程 序要求,服务器返回一个HTTP应答信息给客户端。与通常对HTML页面的HTTP GET请求不同的是,此请求设置了一些HTTP HEADER,标识着一个SOAP服务激发,和HTTP包一起传送。例如:对于一个询问股票价格的应用程序,服务器端具有组件提供某股票当前的价格,组件 是COM或CORBA在服务器上建立的。客户端发送一个SOAP请求给服务器询问股票价格。服务器依赖于服务器上的SOAP网关,使用内嵌的HTML对象 调用合适的方法, 然后把得到的价格通过SOAP应答传给客户端。





回页首


五.SOAP 的前景

W3C于2000年5月8日发表了Simple Object Access Protocol (SOAP) 1.1版本,具体规范发布在下列站点上( http://www.w3.org/TR/SOAP/)。又与今年7月9号推出了SOAP Version 1.2版本的建议草案,具体规范发布在下列站点上( http://www.w3.org/TR/soap12/)。编写SOAP Version 1.1版本的工作小组的成员包括: DevelopMentor, International Business Machines Corporation, Lotus Development Corporation, Microsoft, UserLand Software

SOAP 的推出是令人兴奋的。可以相信,随着网络服务的的不断发展,它将极大的改变我们的思考模式和开发模式。现在,已有许多大公司着手支持SOAP的开发,去年 IBM公司 和 Microsoft公司 都发行了实现 SOAP 的第一批版本。 IBM 公司启动了Apache SOAP 项目计划,微软最近又推出了SOAPtoolkit2.0的正式版,主要包括如下的一些特征:SOAP的高层接口和低层接口,消息对象接口,完全支持 WSDL 1.1标准,支持用户自定义类型映射,并且提供了丰富和完整的开发文档以及应用实例。而且,两家公司正在互操作性方面努力研究。可以乐观的估计,不用多 久,SOAP 互操作性的时代就将来临。

与SOAP相关的一些标准:

  1. HTTP 1.0 or greater( http://www.w3.org/Protocols/HTTP/ietf-http-ext
  2. the core W3C XML recommendation( http://www.w3.org/TR/1998/REC-xml-19980210
  3. W3C XML namespace recommendation( http://www.w3.org/TR/REC-xml-names).
  4. XML Schema( http://www.w3.org/TR/xmlschema-1/

目前支持SOAP的一些公司产品:

Organization Product
Rogue WaveNouveau ORB
IonaOrbix 2000
ObjectSpaceVoyager
Digital CreationsZope, the Python Application Server
UserLandFrontier groupware product
MicrosoftWindows DNA 2000

SOAP是一个协议,与编程语言无关。实际上,许多语言已经开始支持SOAP,如:java,c/c++,vb,c#,perl,php.下面列出了在Java/C++/Perl/ADA/Python环境下SOAP的执行工具:

鉴于SOAP是当前的新技术,国内资料贫乏,主要参考了国外的一些资料及IBM和MICROSOFT的相关资料,文章偏颇之处,请多指教!

/****************************************************************************** * 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、付费专栏及课程。

余额充值