PJSIP开发手册之SIP事件通知(十三)

来源:互联网 发布:经济数据先行指标的有 编辑:程序博客网 时间:2024/05/16 09:46

第十三章 SIP特定的事件通知

SIP事件特定的通知在RFC3265“Session Initiation Protocol-SpecificEvent Notification”描述。这个核心协议定义了建立事件订阅的两种SIP方法,即SUBSCRIBE和NOTIFY,尽管其他方法也可以被定义来建立订阅(如REFER)。

这章描述了PJSIP的设计和基于基本的Dialog框架来创建基本和通用的事件通知框架的实现。这章可以用来实现高层的事件包如presence和call transfer(使用REFER)。

PJSIP的事件通知框架的实现被打包成一个静态库pjsip-simple,在pjsip目录下。为了使用它的功能,应用应该包括头文件<pjsip_simple.h>和连接到pjsip-simple静态库。

基本概念

所有类型的JSIP事件通知会话表示为pjsip_evsub对象。这个对象管理订阅的生命周期,和转化到来的请求和响应到适合的回调函数的调用。

PJSIP事件通知会话使用基本的Dialog框架来管理下层的Dialog和维护Dialog属性(像请求目标,CSeq排序,等)。因为基本的Dialog的设计允许Dialog被多个会话共用,多个事件订阅会话可能使用相同的Dialog,并且它也可以和invite会话共用这个Dialog。

为了订阅一个事件通知,应用需要创建一个事件订阅对象,指定下层的Dialog和接收订阅事件的回调函数。

到来的订阅请求(如SUBSCRIBE或REFER)将到达一个Dialog或者应用,根据这个请求是否在一个Dialog内。应用必须检测请求中的Event id并使用合适的包的API来处理订阅。例如,当请求是REFER,应用调用pjsip_xfer_create_uas()来创建服务端订阅。当SUBSCRIBE请求中的Event id是“presence“时,应用调用pjsip_pres_create_uas()来创建服务端订阅。

事件包

事件包描述了事件订阅的语义。在PJSIP中,在带有指定的事件ID的会话被创建之前事件包必须首先被注册到事件框架上。通过调用pjsip_evsub_register_pkg()可以把事件包注册到这个框架。这个函数通常当这个PJSIP模块实现的这个事件包被初始化时。

这个事件包主要负责向NOTIFY请求提供消息体。例如,PJSIP presence事件包创建所有外出的NOTIFY请求消息体使用内容类型”application/pidf+xml”或者”application/xpidf+xml”。

头部域

事件框架基于已注册的事件包提供的信息来管理外出请求的Accept,Allow-Events,Event,Expires和Subscription-State头部域的内容。它也检查到来请求中的Expires和Subscription-State头部域的内容,并相应地更新它自己的状态。

所有其他头部域(和Dialog外的头部域)必须被事件包或者应用处理。例如,refer事件包管理外出的REFER请求的Refer-To头部域。

基本的操作

这个会话描述如何使用核心的PJSIP事件订阅框架。正如你将在后面章节见到,高层的事件包(如presence和call transfer)的操作,将和核心事件框架的操作类似。

注意:为了节省空间,图中省略了”pjsip_”头。

客户端初始化订阅

客户端通过构建和发送SUBSCRIBE请求建立Dialog来初始化订阅。客户端应该将合适的证书放入这个Dialog中,这样认证的挑战可以被evsub模块自动处理。客户端应该在Dialog中设置合适的路由集。


描述:

1.        应用(或者事件包)初始化客户端订阅通过先创建一个UAC Dialog(1a),接着创建这个客户端订阅会话(1b)。应用可以1a和1b步骤之间设置Dialog的证书和路由集。

2.        通过创建请求(2a)和发送请求(2b),应用发送初始的SUBSCRIBE(或者其他建立订阅的方法,像REFER)。

3.        上面2步骤中的SUBSCRIBE请求的发送将触发on_evsub_state()回调函数被调用。这甚至可能在evsub_send_request()函数返回之前发生。

4.        步骤4中应用接收on_tsx_state()中的任何transaction状态的进度。这个回调函数是可选的,并且只为提供信息。如果这个请求被挑战,并且证书已经在这个Dialog里设置了,这个事件框架将使用正确的证书提交这个请求。

5.        当接收到2xx响应,on_evsub_state()回调将被调用。应用可以通过调用pjsip_evsub_get_state()函数得到这个订阅状态,当接收到2xx响应时,这个函数返回PJSIP_EVSUB_STATE_ACCEPTED。当接收到非2xx最终响应时,这个订阅状态将被设置为PJSIP_EVSUB_STATE_TERMINATED。

服务端接收到来的订阅


描述:

1.会话外的到来请求将总会到达应用,应用最终决定如何处理这个请求。对于到来的SUBSCRIBRE请求,如果应用想要认证这个请求,它可以有状态或无状态地发送401/407响应(步骤1a)来响应这个请求。这必须在任何Dialog或者订阅实例被创建之前完成。当应用满意这个请求(步骤1b),它可以接着创建这个服务端订阅实例(步骤2a和2b)。

如果这个请求在一个Dialog内(例如,REFER请求),应用接收这个Dialog的回调函数

on_tsx_state()中的请求。在这种请况下,步骤1到2a是不需要的,并且应用直接执行步骤2b。

2.服务端的事件订阅需要一个Dialog。应用可能为这个订阅创建一个新的UAS Dialog(步骤2a),或者可能使用现有的Dialog(例如处理Dialog内的到来REFER请求)。应用接着创建服务端的事件订阅(步骤2b),并传入这个Dialog实例。

3.应用调用pjsip_evsub_accept()来发送响应(步骤3),并将传入的状态码放入这个响应中。这个状态码必须是2xx的。

4.步骤3中发送2XX响应将触发on_evsub_state()回调函数的调用(步骤4)。

服务端接着必须立刻发送初始的NOTIFY请求,下面将会介绍。

服务端激活订阅(发送NOTIFY)

服务端激活服务端订阅通过发送NOTIFY请求(见下面的第5步)。如果服务端想要NOTIFY请求被认证,那么它必须在创建UAS Dialog时设置证书。如果这个NOTIFY请求是一个挑战,那么只要这个Dialog中有一个正确的证书,那么evsub模块将自动重新使用合适的证书提交NOTIFY请求(步骤6)。


客户端接收NOTIFY请求

当接收到NOTIFY请求,应用可以通过在on_rx_notify()回调函数中返回401来挑战这个请求(见下面步骤5)。客户端接着将等待NOTIFY请求的立即提交。默认地,订阅框架等待NOTIFY请求重提交5秒,如果到了5秒还没有收到NOTIFY请求它将发送带有0 Expires值的SUBSCRIBE请求来终止这个订阅。

on_rx_notify()回调函数是可选的。默认的是以200响应响应到来的NOTIFY请求。

注意如果应用应答这个NOTIFY请求以2xx响应的话,事件框架只更新它的状态(根据到来的NOTIFY请求中的状态)。


服务端终止订阅

服务端终止订阅通过发送带有Subscription-State为terminated的NOTIFY请求(下面步骤8)。服务端可能在它接收到建立这个会话的初始请求之后的任何时间发送NOTIFY请求。

特别地,当这个订阅超时后,服务端应该发送Subscription-State为terminated的NOTIFY请求。


无论响应是什么,只要发送带有Subscription-State为terminated的NOTIFY请求将触发on_evsub_state()回调函数的调用(步骤8b)。然而,当这个NOTIFY请求被挑战时,这个框架将通过重新发送带有合适的证书的请求来响应这个挑战(如果这样的证书存在)。

注意:另外,接收到481(Call/TransactionDoes Not Exist),408(Request Timeout),transaction超时,或者transport错误事件时也将终止这个服务端订阅,并会触发on_evsub_state()回调函数。

客户端接收订阅终止

当接收到发送带有Subscription-State为terminated的NOTIFY请求,只有客户端给服务端的响应是2xx时,on_evsub_state()回调函数将被调用(下面步骤8)。


客户端刷新订阅

当要刷新这个订阅时,事件框架将触发on_client_refresh()回调函数。应用必须通过调用pjsip_evsub_initiate()函数创建请求和pjsip_evsub_send_request()发送请求来刷新订阅。

当PJSIP事件包中的presence或refer被使用时,这些包为这个回调函数提供了默认的实现。这个默认的实现使用了最近的Expires值。因此如果应用正在使用这些包,它不需要实现这个回调。


服务端检查刷新超时

当订阅的expires期间内没有接收到订阅的刷新,服务端订阅将触发on_server_timeout()回调。应用必须通过发送终止状态的NOTIFY来终止订阅。

当PJSIP事件包中的presence或refer被使用时,这些包为这个回调函数提供了默认的实现。这个默认的实现通过带发送终止状态和上次消息体的NOTIFY请求来终止订阅。因此如果应用正在使用这些包,它不需要实现这个回调。


指南

模块管理


初始化事件通知模块并注册这个模块到指定的Endpoint。这个函数必须在任何其他的事件订阅函数之前被调用。


获取事件通知模块的实例。

事件包管理


注册事件包到事件订阅框架。pkg_mod参数指定了事件包注册到的模块。event_name指定了事件包的名字,如“presence”(RFC3856)。accept_cnt和accept参数指定描述可接受的媒体类型的数组。Presence包可接收的媒体类型比如“application/pidf+xml”和”application/xpidf+xml”。

事件订阅状态

事件订阅状态表示为pjsip_evsub_state中的值,定义在<pjsip-simple/evsub.h>如下:


注意:

  • 当状态达到PJSIP_EVSUB_STATE_TERMINATED时,应用必须释放与订阅相关的资源,因为在这之后订阅将被销毁并且将没有任何通
  • PJSIP_EVSUB_STATE_UNKNOWN发生当服务器发送Subscription-State头部域无法识别。

事件订阅会话

PJSIP中的事件通知会话表示为pjsip_evsub结构。下面的函数用来查询这个结构的属性。


获取事件订阅状态。


返回代表状态的字符串,或者当状态是PJSIP_EVSUB_STATE_UNKNOWN时,返回服务器发送的状态字符串。


把用户定义的数据mod_data放进mod_id位置中。


恢复之前设置在mod_id位置的用户定义数据。

通用的事件订阅回调

通用的事件订阅回调用户包含用来从事件框架或者正在使用的事件包接收到通知的函数回调。

当应用正在使用一个包,应用通常要为这个包注册这个回调函数,而不是注册回调到事件框架。

通用的事件订阅回调函数声明如下。


每个回调函数的描述如下:


这个回调函数当订阅状态改变时被调用。应用必须准备接收NULL事件和其他类型的除了PJSIP_EVENT_TSX_STATE的事件。

这个回调函数是可选的,虽然通常应用想要实现这个函数。


这个回调当transaction状态改变时被调用,对于属于这个订阅的transaction(即带有可以创建订阅和NOTIFYtransaction的请求)。

这个回调是可选的,因为它只用来提供信息。


这个回调当到来的SUBSCRIBE(或者任何第一个建立这个订阅的方法)被接收之后被调用。它允许应用指定发送什么响应,并且要放什么附加的头部域和消息体。

当应用正在使用PJSIP的事件包像presence或call transfer时,这个回调是可选的;这个包的默认实现是发送200(OK)和包含当前订阅状态的NOTIFY。

然而,如果应用实现这个回调(即回调的值不为NULL),它必须当接收到这个回调时发送NOTIFY请求。建议调用pjsip_evsub_last_notify()来创建NOTIFY请求,因为这个函数考虑未订阅的请求和计算合适的过期时间间隔。


这个回调当客户端/订阅用户接收到到来的NOTIFY请求。它允许应用指定发送什么响应,并且要放什么附加的头部域和消息体。

这个回调是可选的,当它没有实现时,默认的是发送200(OK)来响应到来的NOTIFY请求。


这个回调当客户端刷新这个订阅被调用。

当应用正在使用PJSIP的事件包像presence或call transfer时,这个回调是可选的;这个事件包将通过发送时间间隔设置为当前/最近时间间隔的SUBSCRIBE请求来刷新订阅。

然而,如果应用实现这个订阅(即回调的值不为NULL),它必须自己发送刷新订阅。


这个回调当服务器在指定的订阅时间段之后没有接收到订阅刷新时被调用。

当应用正在使用PJSIP的事件包像presence或call transfer时,这个回调是可选的;事件包将发送NOTIFY来终止这个订阅。

然而,如果应用实现这个订阅(即回调的值不为NULL),它必须自己处理超时,并且建议发送状态设置为终止的NOTIFY。

事件订阅API


创建客户端订阅会话,使用dlg作为下层的dialog。event参数指定要使用的事件包,并且这必须在之前就已经注册到这个事件框架了。option参数当前只被refer订阅使用,并且其他类型的包这个参数应该设置为0。


创建服务端订阅会话,,使用dlg作为下层的dialog。rdata参数指定到来的请求。option参数当前只被refer订阅使用,并且其他类型的包这个参数应该设置为0。


强制销毁事件订阅。这个函数应该只在初始化失败时被调用。对于通常情况,订阅在终止状态时被自动销毁。

当dialog没有其他usage时,这个函数可能销毁下层的dialog。

调用这个函数来创建请求来初始化订阅,刷新订阅,或请求订阅终止。method参数必须是建立订阅的方法,像SUBSCRIBE或REFER。如果参数为NULL,那么SUBSCRIBE将被使用。expires参数将被放入请求的Expires头部域。如果这个设置为0,这将是取消订阅的请求。如果这个值是负数,默认的包中定义的过期时间间隔将被使用。

应用接着必须调用pjsip_evsub_send_request()来发送订阅请求。


发送2XX响应给SUBSCRIBE或REFER请求来接受到来的订阅请求。st_code参数必须指定2xx。在hdr_list是可选的放入响应中的头部域列表。


对于通知者,设置订阅的状态和创建NOTIFY请求给订阅者。state_str参数是可选的,它只被用在当这个状态设置为PJSIP_EVSUB_STATE_UNKNOWN。当订阅的状态设置为PJSIP_EVSUB_STATE_TERMINATED时,reason参数必须被设置。应用应该使用RFC3265中定义的值,如“noresource”,“timeout”,“giveup”,”rejected”,”probation”和”deactivated”。PJSIP不翻译这个原因字符串,它将只是把这个字符串放到外出的NOTIFY请求中。

注意当NOTIFY请求被发送时,这个订阅的状态将被设置。


对于通知者,创建一个NOTIFY请求来反映当前订阅的状态。这个函数通常被包实现者使用,而不是直接被应用使用。


发送tdata中之前创建的外出请求。

辅助API


获取和指定的transaction相关的事件订阅实例。





0 0