国标28181:libosip2 使用

1060 篇文章 297 订阅

初始化

使用osip时,您的第一个任务是初始化解析器和状态机。这必须在使用libosip2之前完成。

#include <sys/time.h>
#include <osip2/osip.h>
int i;
osip_t *osip;
i=osip_init(&osip);
if (i!=0)
	return -1;

如果您想使用状态机(事务管理),第二步是设置一些回调,这些回调将通知您SIP事务状态的任何更改。

以下大多数回调都是可选的。比如:

  // callback called when a SIP message must be sent.
    osip_set_cb_send_message(osip, &cb_udp_snd_message);
    // callback called when a SIP transaction is TERMINATED.
    osip_set_kill_transaction_callback(osip ,OSIP_ICT_KILL_TRANSACTION,
                                       &cb_ict_kill_transaction);
    osip_set_kill_transaction_callback(osip ,OSIP_NIST_KILL_TRANSACTION,
                                       &cb_ist_kill_transaction);
    osip_set_kill_transaction_callback(osip ,OSIP_NICT_KILL_TRANSACTION,
                                       &cb_nict_kill_transaction);
    osip_set_kill_transaction_callback(osip ,OSIP_NIST_KILL_TRANSACTION,
                                       &cb_nist_kill_transaction);
    // callback called when the callback to send message have failed.
    osip_set_transport_error_callback(osip ,OSIP_ICT_TRANSPORT_ERROR,
                                      &cb_transport_error);
    osip_set_transport_error_callback(osip ,OSIP_IST_TRANSPORT_ERROR,
                                      &cb_transport_error);
    osip_set_transport_error_callback(osip ,OSIP_NICT_TRANSPORT_ERROR,
                                      &cb_transport_error);
    osip_set_transport_error_callback(osip ,OSIP_NIST_TRANSPORT_ERROR,
                                      &cb_transport_error);
    // callback called when a received answer has been accepted by the transaction.
    osip_set_message_callback(osip ,OSIP_ICT_STATUS_1XX_RECEIVED, &cb_rcv1xx);
    osip_set_message_callback(osip ,OSIP_ICT_STATUS_2XX_RECEIVED, &cb_rcv2xx);
    osip_set_message_callback(osip ,OSIP_ICT_STATUS_3XX_RECEIVED, &cb_rcv3456xx);
    osip_set_message_callback(osip ,OSIP_ICT_STATUS_4XX_RECEIVED, &cb_rcv3456xx);
    osip_set_message_callback(osip ,OSIP_ICT_STATUS_5XX_RECEIVED, &cb_rcv3456xx);
    osip_set_message_callback(osip ,OSIP_ICT_STATUS_6XX_RECEIVED, &cb_rcv3456xx);
    // callback called when a received answer has been accepted by the transaction.
    osip_set_message_callback(osip ,OSIP_NICT_STATUS_1XX_RECEIVED, &cb_rcv1xx);
    osip_set_message_callback(osip ,OSIP_NICT_STATUS_2XX_RECEIVED, &cb_rcv2xx);
    osip_set_message_callback(osip ,OSIP_NICT_STATUS_3XX_RECEIVED, &cb_rcv3456xx);
    osip_set_message_callback(osip ,OSIP_NICT_STATUS_4XX_RECEIVED, &cb_rcv3456xx);
    osip_set_message_callback(osip ,OSIP_NICT_STATUS_5XX_RECEIVED, &cb_rcv3456xx);
    osip_set_message_callback(osip ,OSIP_NICT_STATUS_6XX_RECEIVED, &cb_rcv3456xx);
    // callback called when a received request has been accepted by the transaction.
    osip_set_message_callback(osip ,OSIP_IST_INVITE_RECEIVED,     &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_IST_ACK_RECEIVED,        &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_NIST_REGISTER_RECEIVED,  &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_NIST_BYE_RECEIVED,       &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_NIST_CANCEL_RECEIVED,    &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_NIST_INFO_RECEIVED,      &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_NIST_OPTIONS_RECEIVED,   &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_NIST_SUBSCRIBE_RECEIVED, &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_NIST_NOTIFY_RECEIVED,    &cb_rcvreq);
    osip_set_message_callback(osip ,OSIP_NIST_UNKNOWN_REQUEST_RECEIVED, &cb_rcvreq);
    // other callbacks exists... They are optionnal.

如何解析URL

为了演示如何使用libosip2解析器,最简单的方法是开始使用URI。(统一资源标识符)

对于每一个解析器(头、SIP消息或URI),您总会找到一些接近这个最小方法子集的东西:

// allocation/release of memory.
	xxxx_init(osip_xxx_t **el);
	xxxx_free(osip_xxx_t *el);
	xxxx_parse(osip_xxx_t *el, char *source);
	xxxx_to_str(osip_xxx_t *el, char **dest);

对于URL解析器相关API位于osip_uri.h,下面是一个例子:

#include <osip2/osip.h>

using namespace std;


int main(void)
{

    char buffer[236] = "sip:some@192.168.31.131:50027";

    osip_uri_t *uri;
    int i;
    i=osip_uri_init(&uri);
    if (i!=0) { fprintf(stderr, "cannot allocate\n"); return -1; }
    i=osip_uri_parse(uri, buffer);
    if (i!=0) { fprintf(stderr, "cannot parse uri\n"); }

    printf("uri->string = %s", uri->host);
    osip_uri_free(uri);

    return 0;
}

下面是将URI转换为可打印字符串所需的序列。注意,在这个序列中,dest是动态分配的,必须在调用序列结束时释放,以避免内存泄漏。


#include <memory>
#include <osipparser2/osip_port.h>
#include <osipparser2/osip_message.h>

int main(int argc, char **argv) {

    char buffer[236] = "sip:some@192.168.31.131:50027";

    osip_uri_t *uri;
    int i;
    i=osip_uri_init(&uri);
    if (i!=0) { fprintf(stderr, "cannot allocate\n"); return -1; }
    i=osip_uri_parse(uri, buffer);
    if (i!=0) { fprintf(stderr, "cannot parse uri\n"); }


    // -------------------
    char *dest;
    i = osip_uri_to_str(uri, &dest);
    if (i!=0) { fprintf(stderr, "cannot get printable URI\n"); return -1; }
    fprintf(stdout, "URI: %s\n", dest);
    osip_free(dest);




    osip_uri_free(uri);

    return 0;
}

如何解析SIP消息

对于SIP消息解析器相关API位于osip_message.h`:

下面是解析包含sip请求或响应的给定缓冲区所需的序列。由于SIP消息的正文部分可能包含二进制数据,因此必须将缓冲区的长度指定给API。

    osip_message_t *sip;
	int i;
	i=osip_message_init(&sip);
	if (i!=0) { fprintf(stderr, "cannot allocate\n"); return -1; }
	i=osip_message_parse(sip, buffer, length_of_buffer);
	if (i!=0) { fprintf(stderr, "cannot parse sip message\n"); }
	osip_message_free(sip);

下面是将消息转换为可打印字符串所需的序列。注意,在这个序列中,dest是动态分配的,必须在调用序列结束时释放,以避免内存泄漏。

转换SIP消息时,分配的缓冲区的最终长度将在第三个参数中返回。然后您就知道了接收到的数据的长度。

    char *dest=NULL;
	int length=0;
	i = osip_message_to_str(sip, &dest, &length);
	if (i!=0) { fprintf(stderr, "cannot get printable message\n"); return -1; }
	fprintf(stdout, "message:\n%s\n", dest);
	osip_free(dest);

在使用libosip2及其事务管理功能时,通常只需要创建合适的事件。因此,您可能会使用此API(仅用于您收到的SIP消息!):

	osip_event_t *evt;
	int length = size_of_buffer;
	evt = osip_parse(buffer, i);

重要的是要理解,libosip2解析器不会完全检查消息是否兼容且格式正确。应用层仍然对此负责。下面的字符串显示了一个包含奇怪端口号的请求URI!

INVITE sip:jack@atosc.org:abcd SIP/2.0

libosip2解析器不会检测到这是一个错误,这将取决于您在evry action上验证事情是否如您所期望的那样!

如何管理事务

osip实现了SIP RFC定义的不同事务的4中状态机:

  • ICT :Invite Client Transaction (Section 17.1.1)
  • IST:Non Invite Client Transaction (Section 17.1.2)
  • IST : Invite Server Transaction
  • IST : Invite Server Transaction

下面是一个RFC中一个ICT例子:

在这里插入图片描述
如上图所示,ICT有四种状态: CALLING(调用), PROCEEDING(继续), COMPLETED(完成)、TERMINATED (终止)。要“执行”状态机,您将构建事件,将它们提供给正确的事务上下文,如果当前状态允许该事件,则事务的状态将被更新。

事件分为三类:

  • SIP消息
  • 计时器
  • 传输错误

开启一个新事务

假设您想要实现一个用户代理,并且想要启动一个注册事务。使用解析器库,首先必须构建符合SIP的消息。(oSIP作为一个低层库,提供了一个构建SIP消息的接口,但是否正确填写所有必需字段取决于您。)一旦构建了SIP消息,就可以开始新的事务了。以下是代码:

 osip_t *osip       = your_global_osip_context;
    osip_transaction_t *transaction;
    osip_message_t     *sip_register_message;
    osip_event_t       *sipevent;
    application_build_register(&sip_register_message);
    osip_transaction_init(&transaction,
                          NICT, //a REGISTER is a Non-Invite-Client-Transaction
                          osip,
                          sip_register_message);
    // 如果您有一个要与该事务关联的特殊上下文,可以使用一个特殊方法将您的上下文与该事务上下文关联。
    osip_transaction_set_your_instance(transaction, any_pointer);
    //此时,oSIP中存在事务上下文,但仍然必须将SIP消息提供给有限状态机。
    sipevent = osip_new_outgoing_sipmessage (msg);
    sipevent->transactionid =  transaction->transactionid;
    osip_transaction_add_event (transaction, sipevent);
    // 此时,事件将由oSIP处理。(内存资源也将由oSIP处理)。请注意,没有采取任何行动。

在fsm中添加新事件的代码类似。

消费事件

上一步显示了如何创建事务,以及添加新事件的一种可能方法。(请注意,一些事件(超时事件)将由oSIP添加,而不是由应用程序添加)。

在这一步中,我们将描述oSIP将如何使用事件。

  • 注意它不允许在同一时间同时消费一个事件!fsm必须在事务中按顺序使用事件。这意味着在调用osip_transaction_execute()时,在第一次调用返回之前,禁止使用相同的事务上下文再次调用此方法。在多线程应用程序中,如果一个线程处理一个事务,则代码如下:
  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 ();
  }

通知应用层有事件发生

比如:当一个事件被认为对fsm有用时,这意味着必须在事务上下文中完成从一个状态到另一个状态的转换。如果事件是SND_REQUEST(这是一个outgoing REGISTER 的情况),之前注册这个动作的回调将被调用。

当事务达到终止状态(当kill回调被调用时,你必须将它从已知的事务列表中移除

	static void cb_ict_kill_transaction(int type, osip_transaction_t *tr) 
	{
	  int i;
	  fprintf(stdout, "testosip: transaction is over\n");
	  i = osip_remove_transaction (_osip, tr);
	  if (i!=0) fprintf(stderr, "testosip: cannot remove transaction\n");
	}

如何管理Dialogs

对话管理是oSIP提供的一个功能强大的工具。这个特性是SIP终端需要的,它有能力接听呼叫。(例如,对INVITE回答200 OK)。

一个Dialog是在oSIP中建立呼叫的上下文。 一个invite请求可能导致多个call 建立(当有代理时可能会发生)

有两种创建Dialog的方法,在一种情况下,您是CALLER (发起者),而在另一种情况下,您将是(应答者CALLEE)。

创建一个CALLER Dialog

在这种情况下,每次收到代码介于101和299之间的响应时,都必须创建一个Dialog。在oSIP中,实际创建Dialog的最佳位置当然是在此类SIP消息的回调中。当然,每次收到响应时,都必须检查与此invite相关联的现有Dialog,该Dialog可能是由来自同一用户代理的早期SIP应答创建的。回调中的代码如下所示:

	void cb_rcv1xx(osip_transaction_t *tr,osip_message_t *sip)
	{
		osip_dialog_t *dialog;
		if (MSG_IS_RESPONSEFOR(sip, "INVITE")&&!MSG_TEST_CODE(sip, 100))
		{
			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
		}
	}

创建一个CALLEE Dialog

当接收到INVITE请求的第一个事务时在响应的回调函数中创建一个CALLEE Dialog。

首先,将构建一个SIP应答,比如180或200,您将能够通过调用以下代码来创建一个对话框:

osip_dialog_t*dialog;
osip_dialog_init_as_uas(&dialog、原始邀请、响应构建);

要使事务正常运行,就必须创建一个合法的响应:不要忘记创建一个新tag ,并将其放在“To”标题中。对话管理在很大程度上依赖于这个标签。

如何使用sdp negotiator(已经废弃)

有两个相关的文件: osip_negotiation.h osip_rfc3264.h提供相同的功能,但是 osip_negotiation.h已经过时,不应该在使用

只有SIP endpoint才需要这个功能,一般是不需要的

怎么用

先初始化一个sdp negotiator

struct osip_rfc3264 *cnf;
    int i;
    i = osip_rfc3264_init(&cnf);
    if (i!=0)
    {
        fprintf(stderr, "Cannot Initialize Negotiator feature.\n");
        return -1;
    }

然后必须添加一组已知的编解码器。为了简化实现,可以添加sdp_media_t元素。下一段代码展示了如何添加对G729编解码器的支持。

sdp_media_t *med;
	sdp_attribute_t *attr;
	i = sdp_media_init(&med);
	med->m_proto = osip_strdup("RTP/AVP");
	med->m_media = osip_strdup("audio");
	osip_list_add(med->m_payloads, osip_strdup("18"), -1);
	i = sdp_attribute_init (&attr);
	attr->a_att_field = osip_strdup("rtpmap");
	attr->a_att_value = osip_strdup("G729/8000");
	osip_list_add (med->a_attributes, attr, -1);
	osip_rfc3264_add_audio_media(cnf, med, -1);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值