收藏 分销(赏)

osip源代码框架详解.doc

上传人:快乐****生活 文档编号:4860569 上传时间:2024-10-15 格式:DOC 页数:35 大小:1.13MB
下载 相关 举报
osip源代码框架详解.doc_第1页
第1页 / 共35页
osip源代码框架详解.doc_第2页
第2页 / 共35页
点击查看更多>>
资源描述
Q/CT XXXX.1-2008 Osip协议源代码框架详解 Prepared by Mao minghua Date 2009.09.25 Reviewed by Date Approved by Date Revision History Version Author Reviewed By Comments Issued Date 0.1 Mao minghua 描述osip协议栈的源代码框架 目录 1 符号及缩写 4 2 整体描述 4 3 OSIP包的源代码框架解析 5 3.1 osip的transaction的event的产生 5 3.1.1 定时器事件的产生过程 6 3.1.2 报文触发的事件 7 3.2 osip 的transaction的event处理流程 7 3.2.1 ICT的处理流程 8 3.2.2 IST的处理流程 9 3.2.3 NICT的处理流程 9 3.2.4 NIST的处理流程 9 3.3 Osip报文的解析 10 3.3.1 sip协议报文的解析整理流程 10 3.3.2 Osip报文头的解析 12 3.3.3 uri的解析 14 3.3.4 添加一个新的协议header字段 15 3.4 osip的transaction的管理 16 3.5 osip中dialog的管理 18 4 EXOSIP包的源代码框架解析 19 4.1 Lib库的初始化和销毁 20 4.2 Lib库的主处理线程 23 4.2.1 2xx应答的重发处理机制 24 4.2.2 Exosip_execute执行流程 24 4.2.2.1 Exosip_read_message的处理 26 4.2.2.2 eXosip_process_response_out_of_transaction的处理流程: 29 4.2.3 eXosip_automatic_action处理流程 29 4.3 Call的处理 30 4.3.1 创建Call的第一个INVITE 30 4.3.2 INVITE的ACK应答的创建和发送 32 4.3.3 dialog内的请求的创建和发送 33 4.3.4 Dialog内answer的创建和发送 33 4.4 Register的处理 34 4.4.1 向一个服务器第一次注册 35 4.4.2 调整一个注册的注册超时时间 35 4.4.3 发送一个register注册 35 Osip源代码框架详解 1 符号及缩写 缩写 英文全称 中文名称 ICT Invite Client Transaction Invite类型的客户端事务 IST Invite Server Transaction Invite类型的服务端事务 NICT Not Invite Client Transaction 非Invite类型的客户端事务 NIST Not Invite Server Transaction 非Invite类型的服务端事务 IMS IP Multimedia Subsystem IP多媒体子系统 PSVT Packet service video telephony 分组域可视电话 SIP Session Initiation Protocol 会话初始协议 UDP User Datagram Protocol 用户数据报协议 URL Uniform Resource Locator 统一资源定位器 2 整体描述 开源代码的osip协议栈分为两个源代码包,整个协议栈采用lib库的形式,在内部没有使用到任务,采取与TCP/IP协议栈一样的策略,所以在使用上需要上层管理任务直接调用lib库提供的接口。因为在Lib库内部没有使用到像定时器、发送队列等的任务,而同时需要使用到定时器,所以在lib库的内部采用轮训遍历的方式不停的检查是否有定时器超时,这在某种程度上会浪费CPU的允许时间。同时整个lib库实现了对call, notify等的管理,为了实现重入,在应用启用多线程的条件下,内部启用的信号量和锁的使用,在下面的分析中不涉及到信号量和锁机制。 Lib库按照sip协议栈的层次关系分为两个lib包,底层的osip lib包实现对单个请求、应答、ACK的处理,包括message的解析、拼装、内容set和get,单个请求形成的transaction相关操作以及通信两端形成的一个dialog的操作。 Lib库上层的exosip lib在底层osip lib库的实现基础上,实现对sip协议整理逻辑上的管理。Exosip主要关注的是sip协议的业务流程,包括call的整体管理,notify的整体管理, publish的管理,register的管理,option的管理,refer的管理和subscription的管理,其中最主要的为call和register的管理,这两个为sip协议栈必须实现的部分,另几个功能为sip协议栈扩展部分。从这几个业务的管理流程出发,在业务的底层它们会使用到相似的一些功能,如注册的认证,发送message,接收message,每个请求和应答形成的transaction,多个transaction组合而成的dialog。 在message的处理方面,可以分为两类,一类为发送的message,因为是主动发送,所以上层管理层知道是什么类型的message,lib库直接提供各类接口供使用。一类为接收到的message,因为不知道是哪种类型,所以需要根据解析出来的message的信息来进行处理,这部分的处理在udp.c文件中。 整个lib库的初始化在exOsip中介绍。 3 Osip包的源代码框架解析 在osip源代码包中最主要的包括了message的相关操作,其中最重要的为message的解析,即从获取到的一个message中解析生成一个能够被代码直接处理的message数据结构-——osip_message_t。与message结构相关的操作包括根据message数据结构的信息安装sip协议规范组装成一个message字符串;message结构的初始化和释放;message结构的拷贝操作;以及从message结构中获取各种已经解析的成员变量的值和设置各个成员变量的值。在message的解析部分,除了message的头之外,还包括了body的解析,涉及到sdp协议,包括对每个sdp字段的解析。 在osip源代码包中,设计了一个与同一个请求相关的所有message的集合——transaction,在发送或接收到一个新的请求的时候就会生成一个transaction,其中ACK和CANCEL请求是比较特殊的,对于非2xx应答的ACK和初始INVITE请求是属于同一个transaction的,而对于2xx的请求是属于单独的transaction的,所以其重传操作由UAC来控制,而不在INVITE的transaction内部进行控制。CANCEL的请求除了本身建立一个transaction外,根据协议它还会去匹配要CANCEL掉的请求的transaction,如果匹配成功会CANCEL掉相应的transaction。 在osip包中同样设计了dialog相关操作,包括dialog的建立,dialog信息的保存,dialog的匹配及删除等操作。 其它方面,包括多线程中使用到的锁和信号量及信号,内部使用到的链表,用于事件的队列(需要先进先出策略),一些平台无关的封装,定时器以及常量等的定义。这部分比较简单,而且都是最底层函数,直接封装了系统调用层。 3.1 osip的transaction的event的产生 transaction的状态变化是由事件来驱动的,当transaction上有事件产生时,根据事件的类型和当前transaction的状态来处理该event。 Transaction上的事件分为两类:一为定时器事件,在设定的定时器超时时会产生相应的定时器事件;另一类为事件驱动事件,如发送一个请求、应答或接收到一个请求、应答,发送一个ACK和接收到一个ACK,即是由报文产生的事件。 3.1.1 定时器事件的产生过程 ICT、IST、NICT和NIST的定时器的事件产生流程都一样,对于每一个transaction,其定时器是有顺序的,ICT流程中TIMEOUT_B的优先级最高,TIMEOUT_B定时器触发后,会触发kill transaction的操作。当transactionff队列中有未处理的事件时,不处理定时器,直接返回,所以在transactionff队列中总的事件的数量是不多的。 所有的定时器函数调用底层同一个定时器检查函数__osip_transaction_need_timer_x_event。该函数会先检查该定时器是否启动,判断条件为(timer->tv_sec == -1),如果启动,检查当前时间是否超过定时器中设定的时间,如果是,则产生新的定时器事件。 因为定时器没有一个单独的任务,所以是采样轮训的方式检查是否有新的定时器事件产生,而不是根据系统时钟中断进行检测,因此会比较占用系统资源。 定时器的启动和修改使用接口osip_gettimeofday和add_gettimeofday。只需要设定定时器的超时时间,即设定了一个新的定时器。取消一个定时器,只需要修改定时器的timer->tv_sev为-1。 3.1.2 报文触发的事件 包括一个新的invite、response、ack的发送或接收,除了对非2xx的应答ack外,其他的请求和应答都会产生一个新的transaction,并且产生一个新的sipevent事件。 3.2 osip 的transaction的event处理流程 在sip协议栈中为了更快更好的处理transaction,根据协议栈的描述,划分为四种不同的transaction,分别为ICT、IST、NICT和NIST。四种不同的transaction会有不同的处理流程和状态转换表,以及使用到不同的定时器。 ICT、IST、NICT和NIST的状态转换采样注册函数处理方式,为便于管理和使用注册函数,源码中使用了四个全局变量管理四种不同类型transaction的转换表:ict_fsm、ist_fsm、nist_fsm和nist_fsm。 osip结构如下: struct osip { void *application_context; /**< User defined Pointer */ /* list of transactions for ict, ist, nict, nist */ osip_list_t osip_ict_transactions; /**< list of ict transactions */ osip_list_t osip_ist_transactions; /**< list of ist transactions */ osip_list_t osip_nict_transactions; /**< list of nict transactions */ osip_list_t osip_nist_transactions; /**< list of nist transactions */ …… } 整体简单处理流程如下图: 图 31:transaction的event处理流程 3.2.1 ICT的处理流程 如上图,ICT事件处理时: 1) 检查osip管理结构中的osip_ict_transactions链表,如果没有链表元素,直接返回OSIP_SUCCESS 2) 获取链表中元素个数,并保存transaction到临时局部数组 3) 遍历所有transaction,osip_fifo_tryget顺序获取每个transaction中的事件,调用osip_transaction_execute处理每个事件,直到所有transaction中的所有事件被处理完毕,然后返回。 Osip_transacton_execute执行时,根据传入的参数osip_transaction_t * transaction中的transactionde 类型获取到状态转移表的全局管理变量ict_fsm,并且根据event的type和transaction的状态调用fsm_callmethod——在文件fsm_misc.c,是状态转移注册函数的总入口——查询找到event的处理函数,并调用处理函数进行event的处理。 ICT的相关event的注册处理函数在ict_fsm.c文件和ict.c文件。ICT使用到了3个定时器:TIMEOUT_A、TIMEOUT_B和TIMEOUT_D。 在client端发送Invite而需要创建新的ICT的transaction时,TIMEOUT_B被启动,时长为64*DEFAULT_T1(DEFAULT_TI为500ms),TIMEOUT_B为整个transaction的生命周期时长,如果超过这个时间,transaction会被结束。如同传输层使用的是没有传输保证的UDP,则设置TIMEOUT_A,TIMEOUT_D的间隔时间为DEFAULT_T1和64* DEFAULT_T1。如果传输层使用的是面向连接的TCP及相关协议,则直接使用TCP内部的重传机制,不在SIP协议层提供传输的保护机制,所以不启动TIMEOUT_A和TIMEOUT_D。TIMEOUT_A管理Invite的传送,在Invite被发送时,启动定时器TIMEOUT_A,并且在超时时间内还没接收到response的时候,重发该Invite。TIMEOUT_D用于管理ACK,当接收到的response不是>=300时,client端发送ACK,当重复接收到该invite的response时,重发该ACK,确保server端在kill tranction前能接收到ACK。 3.2.2 IST的处理流程 同ICT的处理流程,处理osip中的osip_ist_transaction链表。IST的相关event的注册处理函数在ist_fsm.c文件和ist.c文件。 IST使用了定时器TIMEOUT_G、TIMEOUT_H和TIMEOUT_I。使用方式与ICTL类似,详细见协议栈说明。 3.2.3 NICT的处理流程 同ICT的处理流程,处理osip中的osip_nict_transaction链表。NICT的相关event的注册处理函数在nict_fsm.c文件和nict.c文件。 NICT使用了定时器TIMEOUT_E、TIMEOUT_F和TIMEOUT_K。 3.2.4 NIST的处理流程 同ICT的处理流程,处理osip中的osip_nist_transaction链表。NIST的相关event的注册处理函数在nist_fsm.c文件和nist.c文件。 NIST使用了定时器TIMEOUT_J。 3.3 Osip报文的解析 3.3.1 sip协议报文的解析整理流程 当接收到一个message的时候,需要解析该message,生成一个代码能够处理的数据结构,该结构定义为struct osip_message,该结构定义的一个message的全部相关信息,这些信息主要是供transaction和dialog及dialog的更上一层如call,notify等的使用。 对一个message的解析流程如下图所示: 在接收到一个message时,调用函数osip_message_parse进行message的解析。首先调用函数osip_util_replace_all_lws替换掉message中的连续出现的 ‘\r\n\t’、‘\r\t’、‘\n\t’、‘\r\n ’、‘\r ’、‘\n ’为空格,message是以‘\0’为结束标志的,message的headers和body之间的分界是以’\r\n\r\n’为标志的,替换只替换到’\r\n\r\n’为止,即只替换headers部分出现的\t、\r、\n。由于sip协议栈规定,每个headers都是起新行,而且新行的头一个字符不为空格或\t,所以两个header之间的\r\n不会被替换掉,替换的只是一个允许multi合并项的header的内部多个值之间的“\r\n\t”或“\r\n ”。 举例如下:有两个header,其中Subject只允许单个值出现, Route允许有多个值出现,而且允许分行,但是分行必须以空格或\t开头,而Subject和Route行必需顶格开始,前面是没有空格或\t的,osip_util_replace_all_lws函数将Route header value中的两行间的\r\n\t转化为空格,即在逻辑上就成为一行了。 Subject: Lunch Route: <sip:alice@>, <sip:bob@> <sip:carol@> 一个message由三部分组成,首先是message的startline部分,该行指明这是一个sip的message,包括sip标志,请求或应答说明,状态值,然后以\r\n做为和headers的分隔符。该\r\n不会被osip_util_replace_all_lws替换为空格,如请求的INVITE sip:bob@ SIP/2.0或应答的SIP/2.0 200 OK,在三个属性之间有且仅有一个空格。起始行的解析由__osip_message_startline_parse进行解析,解析得到message的类型,message的sipversion以及message的status_code,当status_code为初始化值0时,该message为一个请求,否则为应答。请求的startline由__osip_message_startline_parsereq进行解析,得到请求的request_uri;应答的startline由__osip_message_startline_parseresp进行解析。Startline部分的解析是严格安装出现的三个属性的顺序进行解析的,并将解析结果保存在osip_message的结构成员变量中。 然后解析messge的headers部分,调用函数 msg_headers_parse。说明见osip的header报文头解析。 如果message中在headers之后不是结束符’\0’,则继续解析message的负载部分,调用函数msg_osip_body_parse进行解析。Message的body解析首先查询headers头解析中保存的content——即body――的属性:content_type,如果content_type中的type不为multipart,即不支持多种mime方式的content,说明body中就一个编码方式,直接将整个body解析为一个内容;如果type为multitype,说明有多个编码方式的body组合在一起形成一个整体的body,则以”--”为分隔符解析body,将body分为多个mime编码方式的字符串,每个解析后的body内容保存在osip_message结构中的bodies结构成员中。 3.3.2 Osip报文头的解析 在解析message的header的时候,因为前面的osip_util_replace_all_lws已经转化了单个header内部出现的\r、\n和\t为空格,所以每个header之间可以使用\r\n做为分隔符进行分隔。如果字符串开头start_of_header已经到达结束符”\0”,则全部header解析完毕,返回成功;调用__osip_find_next_crlf找到这个header的结束字符并保存在end_of_header中;如果start_of_header为\r或\n,则已经解析到\r\n\r\n即headers的结束字符串,则返回成功,并且保存start_of_header到body中,即body是从\r\n字符串开始解析的,所以在body解析时,需要跳过\r\n及之后的空格部分;根据header内部分隔符“:”,取出header的hname和hvalue,其中hvalue在某些hname的情况下是允许为空的,然后调用osip_message_set_multiple_header来解析该header的hvalue字符串;解析成功后,置start_of_header为已经解析完的header的end_of_header,开始解析下一个header。 在osip_message_set_multiple_header中,将headers分为两类,一类如上面例子中的Subject,只允许一个值,则直接调用osip_message_set__header进行解析;一类如上面例子中的Router,允许多个值,根据sip协议,每个值之间以“,”进行分隔,所以需要查询整个hvalue字符串,根据”,”将hvalue分隔成多个值,每个值调用osip_message_set__header进行解析并保存解析结果到osip_message的数据成员变量中。因为hvalue允许使用引号将值引起来,所以需要特别处理“,”是否出现在引号内部的问题。只有在引号外部的“,”才是header值的分隔符,而内部的“,”只是一个header值的一部分。 osip源码中osip_message_set__header对于message headers的解析采用注册函数的方式实现,采用这种方式能够在后继版本很方便的进行新的header的添加,并且不会影响到整个源代码的框架流程。 Osip_parser_cfg.c文件中定义了header头解析所使用到的全局管理变量:static __osip_message_config_t pconfig[NUMBER_OF_HEADERS]; __osip_message_config_t的结构定义如下: typedef struct ___osip_message_config_t { char *hname; int (*setheader) (osip_message_t *, const char *); int ignored_when_invalid; }__osip_message_config_t; hname为sip协议定义的头字段的字符串,这些字符串定义在osip_const.h文件中;函数指针setheader为该协议header的对应的解析函数;ignored_when_invalid为是否忽略该header解析错误的标志,该标志值为1时,在解析该协议header发送错误时,忽略该错误,除sip协议规定的几个必要header之外,其他头应该采用忽略方式。 为了更快的根据header的hname,找到对应的setheader解析函数,采用了hash表的查询方式,根据hname生成一个hash值,并且需要保证没有两个不同的hname对应到同一个hash值中,以提高查询的速度。调用__osip_message_is_known_header (hname)获取到在数组中的index, 调用__osip_message_call_method (my_index, sip, hvalue)解析协议header,并且解析的结果保存在结构osip_message_t * dest,中。 每一个header都包含几个通用的操作:header字符串的解析函数,即上段讲到的osip_message_set_xxx解析函数;header解析后的结构的获取函数,osip_message_get_xxx函数;根据header解析后的结构生成字符串的函数:osip_xxx_str;header解析后的结构的copy函数osip_xxx_clone;header解析后的结构的是否函数:osip_xxx_free;以及header解析结构的初始化函数:osip_xxx_init。 对每个header的几个相关操作最终目的是提供协议的整个header的整体操作,包括osip_message_init,osip_message_free,osip_message_clone和osip_message_parse。 3.3.3 uri的解析 绝大部分的header的解析都是相识的,只有其中有参数的部分的header的解析会比较复杂,最主要的有from、to、contact等,因为除了本身就有参数之外,其值中的request_uri本身也可以包含有参数,而这两种参数之间是有区别的。 Sip协议栈规定header的表示分为 header’s name, header’s value和header’s parameter。其中name和value之间用“:”分隔,value与parameter之间用“;”分隔,parameter之间也使用“;”相分隔。 在结构定义中header的value根据具体header包含的信息进行结构变量的定义,而如果包含parameter则直接定义一个gen_params的链表,所有的parameter都保存在这个链表中。 如下面from的定义,包含有from的名称及一个url,及相关的parameter: struct osip_from { char *displayname; /**< Display Name */ osip_uri_t *url; /**< url */ osip_list_t gen_params; /**< other From parameters */ }; 对应parameter的解析直接调用__osip_generic_param_parseall,该函数解析header的单个hvalue字符串中包含的所有parameter,在函数内部会根据“;”将字符串划分为几个parameter,然后解析每个parameter,将解析结果保存在gen_params链表中。Parameter的格式为pname=pvalue类型,等号两边允许空格。 From、to、contact以及via中间都可能出现url。url的解析接口为osip_uri_parse,输入为url的字符串,解析的结构保存在结构osip_uri_t之中。url包含有三部分内容:url的基本信息,url的header头部分和url的参数部分。开始部分与header头部分用“?”进行分隔,header头之间用”&”进行分隔,header头部分与参数部分用”;”进行分隔,参数之间也使用“;”进行分隔。Header部分调用函数osip_uri_parse_headers进行解析,结果保存在osip_uri_t结构中的url_headers成员变量中;parameter部分调用函数osip_uri_parse_params进行解析,其结果保存在osip_uri_t的url_params成员变量中。 在from、to、contact等包含url的header中,如果url中包含parameter,则整个url必需使用“<” “>”括起来,以表示一个完整url部分。所以解析from等header时需要检查是否包含”<”字符。 3.3.4 添加一个新的协议header字段 1) 需要添加多个一个对该字段进行解析的文件,包含一个header常用到的几个基本通用操作,如果该header有特殊的地方需要处理,需要增加相关的处理函数,文件名一般定义为osip_xxx.c和osip_xxx.h 2) 需要在parser_init中注册新的header的解析函数,需要修改static __osip_message_config_t pconfig[NUMBER_OF_HEADERS]中的NUMBER_OF_HEADERS宏值。 3) 在osip_const.h中添加新的header的宏定义,osip的相关的常量宏定义都定义在该文件 4) 在osip_message.c文件额osip_message_init函数中添加对该header相关结构的初始化操作。在osip_message_free函数中同样添加对该header的相关释放操作,在osip_message_clone中添加对该header的clone相关操作。 5) 在osip_message_to_str.c文件中的_osip_message_to_str函数中添加该header转化为string的函数注册。 6) 如果该header不允许重复多个出现,即不允许multiple header,则在osip_message_parse.c文件的 osip_message_set_multiple_header函数中添加对该header的处理。 7) 在osip_message.h的头文件中的osip_message结构中添加对该header字段的结构。 8) 在osip_headers.h文件中添加新的header的头文件引用。 3.4 osip的transaction的管理 transaction的操作主要包括transaction的初始化、transaction的free、transaction的匹配、从transaction中获取信息和设置transaction信息。 根据sip协议描述一个transaction由5个必要部分组成:from、to、topvia、call-id和cseq,这5个部分一起识别某一个transaction,如果缺少任何一部分,该transaction就会设置失败。 所以对每个部分的设置都会有一个设置函数:__osip_transaction_set_topvia用于设置topvia,对于发送端topvia为自己的via,对于接收端topvia为将message转发到自己的最后一个sip-proxy服务器,__osip_transaction_set_from用于设置message的发送端,__osip_transaction_set_to用于设置message的接收端,__osip_transaction_set_call_id用于设置一个dialog的标识值,该值是随机生成的,算法保证很长一段时间内生成的cal_id是不相同的,__osip_transaction_set_cseq用于设置cseq值,该值在同一个dialog内部是一直保持增长的,即同一个dialog的后面的transaction的cseq会比前面的transaction的值大,按照sip协议其初始值可以是随机数,代码实现中如果是非register请求,从1开始,如果是register请求的dialog,从20开始。 Transaction的初始化发生在接收到一个新的请求或发送一个请求的时候,该请求以及经过解析成为一个可以直接使用请求信息的结构osip_message_t。其初始化具体过程如上面所述,在设置完那5个部分后,还需要初始化event的队列,以及根据osip_message_t的type初始化使用到的定时器结构,如ICT的ict_context。其它部分的初始化在exosip源代码中实现,相关的如your_instance、in_socket、out_socket和record都是未了方便exosip中对transaction的管理而设置的。 Transaction中的event的相关操作在如前面所述。 在transaction的匹配中,根据RFC3261的最新sip协议的描述,由topvia的branch_id是否相同来匹配,如果相同,就是同一个transaction的请求和应答及ACK,为兼容旧版本的transaction的匹配规则,同时支持根据call_id, cseq, from_tag, to_tag来匹配transaction。如下图,为便于管理的transaction,所有的transaction保持在osip_t结构的四条链表中,按照处理流程的不同分为发送出去的INVITE类型请求和应答、其它类型请求和应答,接收到的INVITE请求和应答、其它请求和应答。 struct osip { void *application_context; /**< User defined Pointer */ /* list of transactions for ict, ist, nict, nist */ osip_list_t osip_ict_transactions; /**< list of ict transactions */ osip_list_t osip_ist_transactions; /**< list of ist transactions */ osip_list_t osip_nict_transactions; /**< list of nict transactions */ osip_list_t osip_nist_transactions; /**< list of nist transactions */ …… #if defined(HAVE_DICT_DICT_H) dict *osip_ict_hastable; /**< htable of ict transactions */ dict *osip_ist_hastable; /**< htable of ist transactions */ dict *osip_nict_hastable; /**< htable of nict transactions */ dict *osip_nist_hastable; /**< htable of nist transactions */ #endif }; 在transaction的匹配过程中,如果是发出的请求,因为本地的transaction都会分配一个不重复的transaction_id,所以只需要比配transaction_id即可;对于incoming的message,如果是request,则匹配branch_id或者为兼容前面版本进行transaction的比配,按照协议RFC3261的17-2-3节的方式进行匹配;如果incoming的message是response,则匹配branch_id或根据RFC3261的17-1-3节的规则进行匹配。 3.5 osip中dialog的管理 dialog的相关的管理操作包括dialog的初始化建立过程,dialog的销毁free过程,以及dialog的匹配。此外dialog中保存了相关的路由信息和cseq信息。 Dialog结构如下,由call_id、local_tag和remote_tag唯一确定一个dialog: struc
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服