while (1)
{
se = (osip_event_t *) osip_fifo_get (transaction->transactionff); if (se==NULL)
osip_thread_exit ();
if ( osip_transaction_execute (transaction,se)<1) // deletion asked osip_thread_exit (); }
c. 宣布事件给应用层
这里以outgong REGISTER transaction 为例。如果一个事件看起来对状态机是有用的,那么,这就意味着在该事务的上下文中,需要完成从一个状态到另一个状态的转变。如果事件是SND_REQUEST,那么之前注册的用于宣布该行为的回调函数将被调用。当在这步没有 什么动作必须执行时,这个回调函数对应用程序而言就没有什么用。当消费最先收到的最终 响应时,一个更加感兴趣的宣告将被做出。如果关联与2xx message 的回调被调用,表明事务已成功处理。在该回调函数中,可能需要通知用户,注册已经成功完成,可以去做相关的其他动作了。
如果最终响应不是2xx,或者network 回调被调用了,则可能需要采取一些动作。比如, 如果收到的是302,可能需要往新的位置重新尝试注册。所有的这些都由用户自己来决定。 当事务抵达terminate 状态时,*kill*回调会被调用,在这里,需要将事务从事务列表中 移除:
static void cb_ict_kill_transaction(int type, osip_transaction_t *tr)
{
int i;
fprintf(stdout, \i = osip_remove_transaction (_osip, tr);
if (i!=0) fprintf(stderr, \}
5.4 如何管理对话
对话管理是osip 提供的一个强大功能。这一特性会被sip 终端使用到,这些终端具有响应call 的能力。一个对话是osip 中已经建立的一个call 的上下文。It's not useless to say that ONE invite
request can lead to several call establishment. This can happen if your call has been forked by a
proxy and several user agent was contacted and replied at the same time. It is true that this case
won't probably happen several times a month...
有两种情况创建一个对话,一种是作为caller,呼叫方,另一种是作为callee,也就是被呼叫方。
a. 作为呼叫方创建一个会话
在这种情况下,每当接收到一个code 在101 到299 的应答后,就必须创建一个对话。在osip 中,创建一个对话最好的地方自然就是宣告该sip 消息的回调函数了。当然了,每当接收到一个响应时,需要检查是否已经存在一个对话,这个对话由这个客户代理之前的应答创建,并且也关联于当前的INVITE 请求。回调函数中的执行代码应该类似下面的示例:
void cb_rcv1xx(osip_transaction_t *tr,osip_message_t *sip)
11
{
osip_dialog_t *dialog;
if (MSG_IS_RESPONSEFOR(sip, \{
dialog = my_application_search_existing_dialog(sip); if (dialog==NULL) //NO EXISTING DIALOG {
i = osip_dialog_init_as_uac(&dialog, sip); my_application_add_existing_dialog(dialog); } } else
{
// no dialog establishment for other REQUEST } }
b. 作为被呼叫方创建一个会话
作为一个被呼叫方,当接收到第一个INVITE 请求的传输时就需要创建对话。做这项工 作正确的地方自然也是在回调函数中,此时的回调函数的位置应该是此前注册的用于通告新的INVITE 请求的回调。首先创建一个180 或者200 的应答,然后使用下面类似的代码创建一个对话:
osip_dialog_t *dialog;
osip_dialog_init_as_uas(&dialog, original_invite, response_that_you_build); 要让这一切工作起来,必须保证应答是有效的,比如,不能忘了生成一个新的tag,并将它放到应答的头部to 域。对话的管理对此有非常大的依赖。
5.5 如何使用SDP 协商
SDP 的offer/answer 模型几乎是所有sip 互操作性问题的来源。rfc 文档中定义的SDP 常常没有按期往的那样实现。举个例子,几乎所有的sip 应用都忘了添加强制的's'域到SDP 包中。另外一个错误是认为SDP 包不需要一个'p'和'e'域。即使它们都是可选的,但是,它们都是强制的。由于这些原因,协商看起来是一项艰巨任务。
a. 是否需要SDP negotiator
自然,仅仅sip 终端才需要SDP negotiator(sdp协商)。高级的应用可能觉得它没什么用,但是,当能够通过协商修改SDP 的应答,我想就没有不使用它的理由了。它将简化协商的代码。
b. 如何初始化SDP negotiator
下面的代码展示了如何初始化SDP negotiator: struct osip_rfc3264 *cnf; int i;
i = osip_rfc3264_init(&cnf); if (i!=0) {
fprintf(stderr, \return -1; }
12
c. 如何添加媒体的支持
这步必须添加一组已知的编解码器。为了简化实现,可以添加sdp_media_t 元素。下面的代码展示了如何添加对G729 编码器的支持:
sdp_media_t *med;
sdp_attribute_t *attr;
i = sdp_media_init(&med);
med->m_proto = osip_strdup(\
med->m_media = osip_strdup(\
osip_list_add(med->m_payloads, osip_strdup(\i = sdp_attribute_init (&attr);
attr->a_att_field = osip_strdup(\attr->a_att_value = osip_strdup(\osip_list_add (med->a_attributes, attr, -1); osip_rfc3264_add_audio_media(cnf, med, -1);
d. 如何执行协商
执行协商是复杂的调用序列。这是因为为了给开发者提供足够的灵活性来适应各种环境。
这使得negotiator 使用起来变得复杂。源码中的src/test/torture_rfc3262.c 为这部分的测试程序,从中你可以找到为一个answer 建立一个offer 的完整的协商过程。当针对所有媒体行的协商完成后,仍然必须手动修改SDP message 中缺失的一些元素。特别的,必须针对当前的环境配置来填写ip 地址和端口号。
13
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库osip2协议栈原理分析以及总结(3)在线全文阅读。
相关推荐: