第七章 发送消息
发送和接收消息是SIP应用的核心操作。每个模块里的on_rx_request()和on_rx_response()回调函数来处理接收到来的消息。
这章将介绍基本的发送外出消息的方法,例如,不使用transaction或dialog。
下一章Transaction介绍关于如何有状态地处理请求(到来请求和出去请求)。
发送消息概述
创建消息
PJSIP提供了多种API来创建请求和响应消息。下面是几种创建消息的方法:
所有的消息创建API(除了底层的pjsip_endpt_create_tdata())将设置传输缓存(pjsip_tx_data)的引用计数器为1,这意味着应用(或栈)在某个时刻必须减小引用计数器去销毁传输缓存。
所有的消息发送API将减小传输缓存(pjsip_tx_data)的引用计数器。这意味着如果应用没有对传输缓存的引用计数器做其他处理,这个缓存将在发送后销毁。
发送消息
调用pjsip_endpt_acquire_transport()和pjsip_transport_send()函数是最基本的发送消息的方式。但是,在这种工作方式下,你需要知道目的地址(即sockaddr,不仅仅是主机名)。因为从得到消息和精确的socket地址有一些步骤要做(例如,决定使用哪个地址,执行RFC 3263的查找等),事实上这个函数是很底层的直接调用。
核心API发送消息使用的是pjsip_endpt_send_request_stateless()和pjsip_endpt_send_response()函数。这是两个功能强大的函数,因为它们自动地处理transport层,并且是上层模块(例如,transation)的基础。
pjsip_endpt_send_request_stateless()函数是用来发送请求消息的,它的执行流程如下:
pjsip_endpt_send_response()函数是用来发送响应消息的,它的执行流程如下:
因为可以异步发送消息(如,在TCP已经连接之后),所以所有函数提供通知应用关于传输状态的回调函数。这些回调函数也会通知应用关于失败处理是否发生,并且应用可以复写这些行为。
函数指南
发送响应
基本函数
根据rdata中的请求,利用状态码st_code和状态短语st_text来创建响应消息。如果st_text为NULL,则使用默认的状态短语。
根据rdata中接收到的请求消息来决定发送响应的目的地址和transport。这个函数参照RFC 3261的18.2.2节和RFC 3581来计算目的地址和transport。目的地址和transport信息将返回在res_addr参数中。
无状态地发送response,使用res_addr中的目的地址和transport。响应地址信息(res_addr)通常调用pjsip_get_response_addr()来初始化。
当回调函数cb被调用时,传输的状态和其他信息(包括原始的令牌)将会存储在pjsip_send_state中。如果消息发送成功,回调函数中的sent参数将是一个非0的正数。如果失败则sent参数是个负值,错误码是它不带负号的部分(即status=-sent)。如果cont的值不为0,则表示函数将要尝试其它的地址来发送这个消息(即失败处理)。应用可以通过设置这个参数为0,来不用尝试其它地址。
如果应用没有指定cb,这个函数将不会失败处理去尝试发送到下个地址,以防这个已选择的transport传送消息失败。
如果消息有效或一个非0错误码,这个函数将返回PJ_SUCCESS。然而即使返回PJ_SUCCESS,也不代表保证消息已被成功发送。
注意,回调函数也有可能在该函数返回之前被调用。复合函数
这个函数对一个到来的请求创建并发送一个响应。另外,调用者可以需要指定消息体和附加在响应消息中的头部域(存放在hdr_list和body参数中)。如果没有附加的头部域和消息体,则这些参数设为NULL。
如果响应成功地创建并发送到transport层,或产生一个非0的错误码,则该函数返回PJ_SUCCESS。然而即使返回PJ_SUCCESS,也不代表保证消息已被成功发送。
发送请求创建一个新的,空的传输数据。
创建指定method、target URI、from、to和contact头部域的请求消息。call_id和cseq是可选择的。如果text被指定了,那么一个“text/plain”消息体将被加入。请求消息初始引用计数器为1,并返回给发送者在p_tdata。
通过浅拷贝创建一个新的请求头部域。
根据rdata中接收到的响应从初始的请求消息中创建ACK请求消息。这个函数通常在INVITE请求接收到非成功的响应时被transaction使用。一个INVITE请求的成功响应的ACK请求消息通常是dialog的创建请求的函数生成的。
根据之前在tdata中发送的请求消息创建CANCEL请求。这个函数将创建一个新的传输数据缓存在p_data中。
无状态地发送tdata中的请求消息。这个函数将关注使用哪个目的地址和transport,关注请求行和Route头部域中的URI。下面是这个函数执行的一些步骤:
当回调函数cb被调用时,传输的状态和其他信息(包括原始的令牌)将会存储在pjsip_send_state中。如果消息发送成功,回调函数中的sent参数将是一个非0的正数。如果失败则sent参数是个负值,错误码是它不带负号的部分(即status=-sent)。如果cont的值不为0,则表示函数将要尝试其它的地址来发送这个消息(即失败处理)。应用可以通过设置这个参数为0,来不用尝试其它地址。
如果应用没有指定cb,这个函数将不会失败处理去尝试发送到下个地址,以防这个已选择的transport传送消息失败。
如果消息有效或一个非0错误码,这个函数将返回PJ_SUCCESS。然而即使返回PJ_SUCCESS,也不代表保证消息已被成功发送。
注意,回调函数也有可能在该函数返回之前被调用。
无状态的代理转发
代理服务器可能选择无状态地转发一个消息。当使用这种方式处理的时候,必须严格遵循RFC3261的16.11节。
创建一个新消息用来转发到上流的新的目的地址URI在uri中。这个新的消息是对rdata中的请求消息的深拷贝,除非有其他拷贝机制定义在options参数中。如果branch参数不为NULL,则将被用作Via头部域的branch参数。如果为NULL,则一个唯一的branch参数将会被使用。
代理服务器从rdata中的响应消息创建一个新的响应消息,用来转发到下流。注意,这个函数实际上将拷贝这个响应不检查响应的有效性或删除最顶端的Via头部域。这个函数将深拷贝响应,除非有其他拷贝机制定义在options参数中。
根据到来的请求消息的信息创建一个全局唯一的branch参数。这个函数保证重传的同样的请求会有相同的branch ID。
这个函数也能使用在循环检查过程中。如果相同的URI的相同的请求到达代理服务器,它将
计算成同一个branch id。
注意,返回的字符串是从rdata的内存池中分配的。
例子
发送响应
未找到发送账号时无状态地发送响应
无状态地处理认证失败
其他方式来发送无状态的响应:
无状态的重定位
发送请求
无状态地发送请求
无状态地转发