oSIP开发者手册

  
——本手册指导开发者利用oSIP栈开发用户代理
原文标题:oSIP User Manual
原文作者:Aymeric Moizard
联系方法: jack@atosc.org
版权保护:GNU Free Documentation License
项目网站: http://osip.atosc.org
译文作者:陈善学
联系方法: chenshanxue@263.net
 
摘要
“会话发起协议(Session Initiation Protocol-SIP) 是一个应用层的信令控制协议。用于创建、修改和终止一个或多个参与者的会话。这些会话可以是Internet 多媒体会议、IP 电话或多媒体分发(例如:语音信箱)。会话的参与者可以通过组播(multicast) 、网状单播(unicast) 或两者的混合体进行通信。”
"The Session Initiation Protocol (SIP) is an application-layer control (signaling) protocol for creating, modifying and terminating sessions with one or more participants. These sessions include Internet multimedia conferences, Internet telephone calls and multimedia distribution. Members in a session can communicate via multicast or via a mesh of unicast relations, or a combination of these."
版权
本文的版权归Aymeric Moizard所有。允许拷贝、分发和在”GNU Free Documentation License”(版本1.1或由自由软件基金会最近发布的任何版本)下的定制。需要注意的是,本文档的任何章节的引用(包括本中文翻译)需列出它们的标题、开始的文本和结尾文本,并且标明文档受”GNU Free Documentation Licence”保护。
Copyright (c) 2001 Aymeric MOIZARD. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".
前言
译者修正了原文中一些细微的错误。本文档是由译者独立完成,未免有翻译不妥之处,因此任何的建议和指正都是非常欢迎的。联系的方式是通过电子邮件至 chenshanxue@263.net
索引
 
正文
第一章 SIP协议
SIP协议是用于发起、控制和终结多媒体会话的信令协议。它被IETF( http://www.ietf.org )以rfc2543发表。
SIP是IETF致力于将电话服务带入IP网络众多协议的一个组成部分(它与SDP、RTP、RTCP、RTSP、RSVP、TRIP等众多协议构成SIP系统协议栈)。其将要变成正在发展的IP电话——这个朝气蓬勃的电信工业——的标准之一。正如同电子邮件协议一样,SIP将会变得越来越普及和大众化… …
SIP独立与媒体
传统电话使用一种媒体编码个师通讯(正如被我所熟知的时隙和PCM概念)。现在,这种方式将被终结。我们的电话可以以不同的质量保证和不同的编码方法连接电视、连接摄像机、连接其他电话进行通信。SIP具有媒体协商等功能。任何多媒体应用(例如:游戏、远程教学)都可以使用SIP来建立会话。
SIP独立于传输层
SIP并不和任何的传输层紧密结合。这一构思将使得SIP在第三代网络中受到最小的互操作影响。无线电话的要求(例如漫游功能)同样被关心。SIP完美的构思,使得其适合作为新蜂窝电话时代的信令协议。
SIP有很好的扩展性
在rfc2543中定义了6种类型的事务(INVITE,BYE,CANCEL… …)。这些事务被用于媒体协商、创建、修改和终结呼叫。许多其它的服务已经提供这些方式(例如H.323系统),但SIP以其为扩展性为目的设计和事务模型重用(对于服务器是透明的,被用于使用新类型事务创建辅助服务)。下面是可能的服务列表,其中的一些已经被实现。
短信,用于实时信息
预定或通告,用于会议管理
委托,用于呼叫转移等管理
SIP和最终用户服务
    “SIP透明支持名字映射和重定向服务,提供ISDN和智能网络电话服务同样的一些功能。这些特性也使得个人移动成为可能。”
参考阅读:rfc2543.txt(章节1.1)
SIP服务器被用于定位用户和分发请求的用户定位信息。这些途径,使得最终用户代理发起很少的请求,并能获得多种多样的服务。
许多扩展性在建议文档中定义(查询SIP相关的draft)。我们也可以增加个性化的电话功能并和现有已存在的服务器保持交互。
第二章 SIP协议概述
    本章目的并不是为了细致的描述rfc(我们必须通过阅读rfc获取协议细节),其紧紧提供快速、不完整的协议语法和行为概述。
SIP语法
SIP是一个基于utf8文本编码格式的协议(这使其消息具有很好的可读性,并易于调试)。SIP协议中描述了请求、地址(URL)、应答和个个头部字段的语法信息。整个语法信息以扩展巴克斯范式的形式描述,可以在 Columbia获得。
这些语法定义参考了Mail和HTTP的定义方式。SIP定义了6种请求的类型。最基础的方法有:
INVITE ACK CANCEL BYE INFO OPTIONS
正如我们在请求的BNF定义中看到的(参考rfc2543第三章节),SIP不限定于在规范中已定义的简短的方法列表,扩展性被充分的考虑了。不限定于上面的列表,任何其他类型的请求都可以被oSIP处理。当然这一切的一切,依赖于大家的通知和贡献新的可能方法的样例程序等等。目前的开发库没有太多的努力,以便明确的支持用于提供开发SIP电话的能力。
INVITE sip:jacK@atosc.org SIP/2.0
Via: SIP/2.0/UDP home.sipworld.org
To: sip:jacK@atosc.org
From: sip:cha@sipworld.org
Call-ID: 35778645354@home.sipworld.org
CSeq: 1 INVITE
Contact: sip:cha@home.sipworld.org
Content-type: application/sdp
Content-length: 267
 
v=0
o=user1 53655765 2353687637 IN IP4 128.3.4.5
s=Mbone Audio
i=Discussion of Mbone Engineering Issues
e=mbone@somewhere.com
c=IN IP4 128.3.4.5
t=0 0
m=audio 3456 RTP/AVP 0
a=rtpmap:0 PCMU/8000
    样例2-1 INVITE请求消息内容
INVITE请求被用于初始化和定制会话。现在,cha从sipword.com呼叫在atos.org的jack。这个请求将被发送到由atosc.org管理的SIP代理服务器,其将被前转到jack的通讯设备,设备拥有真正IP地址。
SIP事务
SIP使用事物控制和管理会话。事务(INVITE,CANCEL,BYE… …)通常是当前会话进展的记忆体。一些其它的事物(SUBSCRIBE,NOTIFY… …)对会话来讲并不必要。一个事物是由请求和应答(一些的中间应答和最终应答)构成。以下头部字段:TO、From、Call-ID和Cseq被用于在一个事务中识别相关联的消息。
因为SIP可以使用不可靠的传输层协议(在IP网络中推荐使用UDP),SIP也定义了在一个事务中消息重传的规则。
UAC1               UAS2
 
 jacks   |   INVITE         |
initiate a|----------------->|   Bob's
 call    |                  | Phone starts
|       180 Ringing|   ringing
|<-----------------|
|       180 Ringing|
|<-----------------|
|           200 OK |
|<-----------------|
|   ACK            |
|----------------->|
样例 2-2 INVITE事务
这是一个最基本的点对点的信令呼叫流程的展示。仅有两个 SIP用户代理(UAC/UAS)之间进行的呼叫过程。(重传的情况没有被展示)
SIP会话
事务 (Transactions)被用户代理用于控制会话。一个会话总是有INVITE消息发起的。SIP定义了一系列的应答状态码。一个代理服务器可以应答你一个非常知名的”404 User Not Found”(我们在使用HTTP的时候,也经常遇到“ HTTP 404 - 未找到文件”)。错误也分不同的等级。一个事物呼叫失败了,但可能仍在尝试新的定位,进行新的事务呼叫。应答 3XX用于重定向机制;4XX、5XX、6XX应答各自用于标识终端错误、服务器错误和全局错误。
 
         BOB           home.net    Jack (atosc.org)
         UA1           Redirect           UA2
          | REGISTER      |               .
          |-------------->|               .
          |        200 OK |               .
          |<--------------|               .
          .               .               .
later... .               .               .
          .               .               .
          .               |      REGISTER |
          .               |<--------------|
          .               | 200 OK        |
          .               |-------------->|
          .               .               .
later... .               .               .
          | INVITE jack@home.net          .
          |-------------->|               .
          |302 Moved temporarily          .
          |<--------------|               .
          | ACK           |               .
          |-------------->|               .
          | INVITE jack@office.atosc.org |
          | audio                         |
          |------------------------------>|
          |                  180 Ringing |
          |<------------------------------|
          |                  180 Ringing |
          |<------------------------------|
          |                       200 OK |
          |<------------------------------|
          | ACK jack@office.atosc.org     |
          |------------------------------>|
          .                               .
later     .                               .
          .                               .
          |      INVITE bob@bob.home.net |
          |      audio + video            |
          |<------------------------------|
          |    200 OK                     |
          |------------------------------>|
          |     ACK bob@bob.home.org      |
          |<------------------------------|
          .                               .
later     .                               .
          .                               .
          | BYE jack@office.atosc.org     |
          |------------------------------>|
          |                       200 OK |
          |<------------------------------|
          |                               |
样例 2-3 一个完整的会话
首先,所有的用户代理向注册服务器(在这个样例中,重定向服务器具有注册服务功能。注册服务器也就是定位服务器)。
会话由 INVITE事务发起,联系在jack@home.net。一个重定向服务器使用重定向功能将找到的jack办公室的地址信息反馈给发起呼叫端UA1。UA1获取了新的目标地址的信息,发起对jack@atosc.org的呼叫。UA2首先振铃,jack接起电话发回200 OK成功应答。过了几分钟,jack和bob向使用他们的摄像头,以便能看到对方的视频信息。新的会话媒体参数修改由jack发起INVITE而进行交互协商,最终结束由bob发起。
Server 行为
SIP为代理、重定向和注册服务器定义了行为描述(我将其理解为有限状态机),具体还要阅读rfc… …
通常,一个用户代理向本域管理的代理服务器发起请求。例如我们并不知其将要互相之间的通讯者的具体位置,我们以用户 @域名格式的SIP地址呼叫对方。本域代理服务器(出于安全的考虑,其中可以加入防火墙功能)使用DNS查询,从而定位对方在哪个域里面。一旦对方的域服务器被找寻到,并是安全的(出于安全考虑,可能使用Ipsec认证),请求被前转。对方域代理服务器进行具体定位。如果对方可用(也就是说对方已经在他的域注册服务器注册),所以对方域服务器前转请求到真正的用户。在本地网络中,其他协议标准被用于找寻最终被叫用户(例如finger… …)
第三章 oSIP开发库
在本节中,希望你已经具备 SIP系统很好的知识基础,单纯的指就是SIP rfc文档。如果你打算使用SIP,你一个要仔细阅读rfc2543。
目标
oSIP项目启动于2000年7月,第一个发布的版本是在2001年5月(0.5.0)。
oSIP开发库是第一个自由软件项目。在第三代网络体系中,越来越多的电信运营商将要使用IP电话(Linux也成为支撑平台的幸运儿)。发展的一个侧面是在不久的将来,Linux将更多支持多媒体工具。oSIP,作为SIP开发库,将允许建造互操作的注册服务器、用户代理(软件电话)和代理服务器。所有的这些都平添了Linux将作为下一代电话产品的机会。
但 oSIP的目标并非仅仅在PC应用。OSIP具有足够的灵活和微小,以便在小的操作系统(例如手持设备)满足其特定要求。从0.7.0版本发布,线程的支持作为可选项。作为开发者的我们,可以在应用程序设计时进行选择。OSIP在将来会完美的适用于蜂窝设备和嵌入式系统当中。oSIP被众所周知应用于实时操作系统VxWorks当中,并且其他支持将是简单的事情。
开发的层次
语法分析器
oSIP现在支持utf8和完整的SIP语法。0.7.0版本包含一个有很小例外、很好适应性的SIP语法分析器。并且,新的语法分析器在2001年7月25日被公布,可能还有一些bug。如果发现了,请务必报告到osip@atosc.org,我将修正他们。
OSIP中包含一个语法分析器,其能够读写任何在rfc中描述的SIP消息。目前oSIP能够分析很小一部分头部,例如Via、Call-ID、To、From、Contact、Cseq、Route、Record-Route、mime-version、Content-Type和Content-length。所有其他头部以字符串的格式存储。更多的头部将会被增加进来。
现在, SIP语法分析器以MIME格式支持多种属性(这一部分还未被测试)。
有时不希望的行为可能发生。 oSIP分析之后的消息可能有一些变化。oSIP不能维持如下可靠性:(不过,oSIP保持完整的适应性)
头部字段的顺序
一行中出现多个头部
字段中出现额外的空格
出现 LWS(内部的CRLF)
在 to,from,contact…字段中出现引号
有限状态机
4个有限状态机已经被测试过。并且在第八次加的夫SIPit中,与大约30个设备进行互通测试,表现得很稳定。
事务管理
oSIP公布了一个易于使用的用户界面。事务是通过使用4个有限状态机定型的,这些都是oSIP库的核心。每一个事物使用独立的FIFO,这些队列被外部模块填充。事件(events)一经用户请求便被列入队列。一系列的动态回叫注册,用于应用程序得知每一个事物的进展情况。
在 oSIP上建造一个应用程序需要许多的模块。
首先,我们建造一个管理传输层特性的模块。这样使得 oSIP独立于传输层。
之后,我们将需要建造时钟管理的模块(一个简单的,远没有达到优秀得一个样例已经被提供)。这个模块将负责重传和决定何时删除事务上下文是安全的。
最后,我们需要建造多媒体应用部分。这一部分将不得不维持和更新会话(会话是依赖于事务的)的状态。
事务管理已经被测试,表现的很稳定。并且 oSIP不需要太多的内存便运行得很快。自从0.7.0,开发库能够被用于多线程模式或者不使用,这完全由开发者决定。
谁将受益于oSIP
oSIP提供一个简单的界面,用于为SIP增加多媒体应用。并且,oSIP并不和任何SIP开发商和操作系统紧密结合。我们可以建造最终用户代理、无状态的代理服务器、重定向服务器和网关。如果你像开发stateful proxy,应阅读最后的“Proxy开发的注意点”和前面的事务管理。
允许的平台
    开发库建造初便考虑了可移植性,其可以被很快应用于支持POSIX的任何系统当中。它已经在Solaris,HP unix,RT OS VxWorks和Windows。GNU/Linux(2.2.16和2.4.7)被用于最初的开发。
第四章 oSIP语法分析器
文件
./libosip-x.x.x/parser是SIP语法分析器的源代码目录。
./libosip-x.x.x是开发库的目录。
#inlcude<osip/smsg.h>是一个包含SIP语法分析API的库文件。
SIP-URL(SIP地址)
URL定义的语法结构和设计目标
URL被用于描述分布在SIP网络中的每一个实体:SIP用户代理、定位服务器、SIP代理服务器、SIP重定向服务器等等。这些实体都要有他们完整的SIP URL进行标识。在开发库中用url_t格式去定义如下字段:”To”、”From”、”Contact”、”Route”和”Record-Route”;list_t扩展定义了url中包含的固定参数和不固定的头部值。
SIP URL的结构定义:
typedef struct _sipurl_t{
char *scheme;
char *username;
char *password;
char *host;
char *port;
list_t *url_params;
list_t *url_headers;
}url_t
url_t结构体操作的API
url_init
[功能描述]
分配内存,并对结构体作初始化。(在下面的 API中将会有很多的初始化函数。一般而言,分配内存调用malloc,释放内存调用free,由于作者做了改写使的调用的函数分别为smalloc和sfree,初赋值做的就是将一些变量赋值为0或NULL)。
[参数描述]
int url_init(url_t **url);
成功返回 0,失败会使程序自动退出。
 
url_free
[功能描述]
释放操作完成的 url_t结构,并对url_t结构中的变量赋值为空。
[参数描述]
void url_free(url_t *url);
 
url_parse
[功能描述]
分解输入的串信息,并赋值到已定义的 url_t结构变量当中。
[参数描述]
int url_parse(url_t *url,char *filed_value);
成功返回 0,失败返回-1,后面函数返回值大体如此定义。
 
url_2char
[功能描述]
将一个 url_t中的结构化信息转化并组合赋值给一个字符串。
[参数描述]
int url_2char(url_t *url,char **field_value);
成功返回 0。
char **field_value会在url_2char中做初始化。
 
url_clone
[功能描述]
进行两个 url_t结构实例的复制(使用标准函数memcpy也可以完成,但对于链表不一定可以,未曾测试)。
[参数描述]
int url_clone(url_t *url,url_t **dest);
成功返回 0,失败返回-1。
 
url_setscheme
url_setusername
url_setpassword
url_sethost
url_setport
[功能描述]
设定 url当中的摘要部分、用户名、密码、主机和端口
[参数描述]
void url_setscheme(url_t *url, char *scheme);
void url_setusername(url_t *url, char *username);
void url_setpassword(url_t *url, char *password);
void url_sethost(url_t *url, char *host);
void url_setport(url_t *url, char *port);
 
url_getscheme
url_getusername
url_getpassword
url_gethost
url_getport
[功能描述]
获取 url当中的一些特定部分的值,并返回。
[参数描述]
char* url_getscheme(url_t *url);
char* url_getusername(url_t *url);
char* url_getpassword(url_t *url);
char* url_gethost(url_t *url);
char* url_getport(url_t *url);
 
其它相关有用的函数 API
#define url_set_transport_udp(U)    url_param_add(U->url_params, "transport", "udp")
#define url_set_transport_tcp(U)    url_param_add(U->url_params, "transport", "tcp")
#define url_set_transport_sctp(U) url_param_add(U->url_params, "transport", "sctp")
#define url_set_transport_tls(U)    url_param_add(U->url_params, "transport", "tls")
#define url_set_transport(U,T)      url_param_add(U->url_params, "transport", T)
 
#define url_set_user_phone(U)      url_param_add(U->url_params, "user", "phone")
#define url_set_user_ip(U)         url_param_add(U->url_params, "user", "ip")
#define url_set_user(U, USER)      url_param_add(U->url_params, "user", USER)
 
#define url_set_method_invite(U) url_param_add(U->url_params, "method", "INVITE")
#define url_set_method_ack(U)      url_param_add(U->url_params, "method", "ACK")
#define url_set_method_options(U) url_param_add(U->url_params, "method", "OPTIONS")
#define url_set_method_bye(U)      url_param_add(U->url_params, "method", "BYE")
#define url_set_method_cancel(U) url_param_add(U->url_params, "method", "CANCEL")
#define url_set_method_register(U) url_param_add(U->url_params,"method", "REGISTER")
#define url_set_method(U, M)       url_param_add(U->url_params, "method", M)
#define url_set_ttl(U, T)          url_param_add(U->url_params, "ttl", T)
#define url_set_maddr(U, M)        url_param_add(U->url_params, "maddr", M)
这些函数都是必要理解的(参照 osip/urls.h)
 
url_param_t和url_header_t操作的API
你可以参考前面细述的关于参数和头部值之间的区别。 在下面的描述的param的函数中,你只要将param换为header,即可完成对url_header_t的操作。这些函数完成了对结构的初始化、分解、设定和克隆等操作。
param参数的结构定义如下:
typedef struct _url_parm_t{
char *gname;
char *gvalue;
}url_param_t;
header参数的结构定义如下:
typedef struct _url_parm_t{
char *gname;
char *gvalue;
}url_header_t
 
url_param_init
[功能描述]
初始化 url_param_t结构。
[参数描述]
int url_param_init(url_param_t **url_param);
成功返回 0。
 
url_param_free
[功能描述]
释放 url_param所对应url_param_t结构实例。
[参数描述]
int url_param_free(url_param_t *url_param);
成功返回 0。
 
url_param_set
[功能描述]
将字符串 pname(参数名)和pvalue(参数名所对应的值)赋值到url_param_t结构当中。
[参数描述]
void url_param_set(url_param_t *url_param,char *pname,char *pvalue);
 
url_param_add
[功能描述]
添加一个赋值好的 url_param_t结构到url_t里面的参数列表当中。
[参数描述]
int url_param_add(list_t *url_params,char *pname,char *pvalue);
成功返回 0,失败返回-1。
 
url_param_freelist
[功能描述]
释放 url_t中的参数列表结构。
[参数描述]
void url_param_freelist(list_t *url_params);
 
url_param_getbyname
[功能描述]
在 list_t列表中寻找含有pname的url_param,并将此参数对应关系值赋给url_param_t **url_param。最终结果存放在url_param_t **url_param中。
[参数描述]
void url_param_getbyname(list_t *url_params,char *pname,url_param_t **url_param)
        
下面给出两个对于参数和头部值非常重要的操作函数:
int url_uparam_add(url_t *url,char *pname,char *pvalue)
#define url_uparam_add(F,N,V) url_param_add(F->url_params,N,V)
int url_uparam_getbyname(url_t *url,char *pname,url_param_t **url_param)
#define url_uparam_getbyname(F,N,UH) url_param_getbyname(F->url_params, N,UH)
对于使用 param,请仔细参考标准rfc2543。
SIP headers操作的API
在 rfc2543中定义了大约40个字段。SIP Message(SIP消息)就是由这一系列的字段排列构成的。在下面的API定义分三部分划分并阐述,第一部分API用于创建、分配、分析和打印SIP的头部元素;第二部分展示某头部特有的API,在这里我仅仅展示”to”所特有的函数;第三部分是一些扩展的API,其不仅适合于”to”字段,同样适合于”From”、”Contact”、”Route”、”Record-Route”和其他头部,着提供了头部的扩展性。
To的结构定义如下:
typedef struct _to_t{
char *displayname;
url_t *url;
list_t *gen_params;
}to_t;
SIP语法分析器能够分析如下头部:Via、To、From、Cseq、Call-Id、Contact、Route、Record-Route、Content-Type、Content-Length、Mime-Version。其他头部通过特殊的API以串的格式存取。
你可以潜意识的将 ”to_”替换为下面的值”via_”、”from_”、”cseq_”、”call_id_”、”contact_”、”route_”、”record_route_”、”content_type_”、”content_length_”、”mime_version_”。
如果你想细致了解每一个字段的结构定义,请阅读 osip/smsgtypes.h;想了解字段对应的函数,请阅读osip/smsg.h库文件,其对于功能描述、参数描述和使用描述都会有深入的理解。
第一部分:创建、分配、分析和打印SIP的头部元素
to_init
[功能描述]
对 to字段进行初始化。
[参数描述]
int to_init(to_t **to);
成功返回 0,不必做失败判断处理。
 
to_free
[功能描述]
释放 to字段。
[参数描述]
void to_free(to_t *to);
 
to_2char
[功能描述]
将 to结构转化赋值给一个字符串。
[参数描述]
int to_2char(to_t *to,char *filed_value);
成功返回 0,失败返回-1。
 
to_clone
[功能描述]
将 to信息克隆到目标
[参数描述]
int to_clone(to_t *to,to_t **dest);
 
第二部分:To特有的API
to_setdisplayname
[功能描述]
设定 to字段里面的display name。
[参数描述]
void to_setdisplayname(to_t *to,char *value);
 
to_getdisplayname
[功能描述]
从 to字段中提取display name并返回。
[参数描述]
char *to_getdisplayname(to_t *to);
 
to_seturl
[功能描述]
在 to字段当中设定url值。
[参数描述]
void to_seturl(to_t *to,url_t *url);
 
to_geturl
[功能描述]
从 to字段中提取url值。
[参数描述]
url_t *to_geturl(to_t *to);
 
to_param_add
[功能描述]
在 to字段中增加一个gen_param
[参数描述]
int to_param_add(to_t *to,char *name,char *value);
成功返回 0,失败返回-1。
注意:
#define to_param_add(T,N,V) generic_param_add((T)->gen_params,N,V)
 
to_param_get
[功能描述]
选取 to结构里面list_t的第pos的内容。
[参数描述]
int to_param_get(to_t *to,int pos,generic_param_t **gp);
成功返回 pos,失败返回-1;
注意:
#define to_param_get(T,I,GP) from_param_get((from_t*)T, I, GP)
 
to_settag
[功能描述]
在 to字段内设定tag的值。
[参数描述]
void to_settag(to_t *to,char *tag)
注意:
#define to_set_tag(T,V) generic_param_add((T)->gen_params, "tag",V)
 
to_gettag
[功能描述]
将 to结构中的tag值,返回成generic_param_t结构
[参数描述]
void to_gettag(to_t *to,generic_param_t **dest);
#define to_gettag(T,GP) generic_param_getbyname((T)->gen_params, sgetcopy("tag"),GP)
 
to_param_getbyname
[功能描述]
在 to字段中寻取参数名为pname的值
[参数描述]
         int to_param_getbyname(to_t *to,char *pname,generic_param_t **dest)
         成功返回0
#define to_param_getbyname(T,N,GP) generic_param_getbyname((T)->gen_params,N,GP)
 
第三部分:扩展API
此部分的参数是很多字段的组成部分,例如 ”To”、”From”、”Contact”、”Route”、”Record-Route”和”Conten-Type”。
To结构和generic_param_t结构
typedef struct _generic_param_t{
char *gname;
char *gvalue;
}generic_param_t
 
typedef _to_t{
char *displayname;
url_t *url;
list_t *gen_params;
}to_t;
提供一个样例:
to:”chenshx”<sip:chenshx@192.168.25.47>;tag=ae56fr-dz-23
 
generic_param_init
[功能描述]
对 generic_param_t结构进行初始化。
[参数描述]
int generic_param_init(generic_param_t *gen_param)
成功返回 0。
 
generic_param_free
[功能描述]
释放 generic_param_t结构。
[参数描述]
void generic_param_free(generic_param_t *gen_param);
 
generic_param_set
[功能描述]
将 pname和pvalue设定到generic_param_t结构中去。
[参数描述]
void generic_param_set(generic_param_t *gen_param,char *pname,char *pvalue);
 
generic_param_getname
generic_param-setname
generic_param_getvalue
generic_param_setvalue
[功能描述]
此四个函数主要操作 generic_param_t结构,对结构中的gname和gvalue进行取值和赋值操作。
[参数描述]
char *generic_param_getname(generic_param_t *gen_param)
void generic_param-setname(generic_param_t *gen_param,char *gname)
char *generic_param_getvalue(generic_param_t *gen_param)
void generic_param_setvalue(generic_param_t *gen_param,char *gvalue)
成功返回 char *,否则返回NULL。
 
generic_param_add
[功能描述]
将 name和其对应的value赋值到gen_params列表当中。
[参数描述]
int generic_param_add(list_t *gen_params,char *name,char *value);
成功返回 0。
 
generic_param_getbyname
[功能描述]
在 list_t列表的gen_params中寻找参数名为name的值,并返回到generic_param_t结构当中。
[参数描述]
int generic_param_getbyname(list_t *gen_params,char *name,generic_param_t **gen_param);
成功返回 0。
 
generic_param_freelist
[功能描述]
释放掉 list_t结构中的gen_params
[参数描述]
void generic_param_freelist(list_t *gen_params);
 
SIP Message操作的API
SIP Message结构定义分三部分:第一部分是一行,其为request的request-uri或response的status code,其结构单独定义为startline_t;第二部分是一些列的字段;最后一部分是一些列的其他头部字段和附件或配属。
目前 sip_t结构还没有完成,非”From”、”To”、”Call-id”、”CSeq”、”Via”、”Contact”、”Route”、”Record-Route”、”MIME-Version”、”Content-Type”、”Conten-Length”被存贮成一系列的通用头部。
结构定义如下:
typedef struct _sip_t{
startline_t *strtline;
 
/*for all header fully implemented by oSIP*/
from_t *from;
to_t *to;
call_id_t *call_id;
cseq_t *cseq;
list_t *vias;
list_t *contacts;
list_t *record_routes;
list_t *routes;
content_type_t *content_type;
content_length_t *contentlength;
mime_version_t *mime_version;
 
/*for all other headers*/
list_t *headers;
 
/*for all attachments*/
list_t *bodies;
}sip_t;
 
msg_init
[功能描述]
对 sip_t结构进行初始化。
[参数描述]
int msg_init(sip_t **msg);
成功返回 0。
 
msg_free
[功能描述]
对 sip_t结构的信息进行释放。
[参数描述]
void msg_free(sip_t *msg);
 
msg_parse
[功能描述]
分解字符串并将其赋值到 sip_t结构体的实例当中。
[参数描述]
int msg_parse(sip_t *msg,char *field_value);
成功返回 0,失败返回-1。
 
msg_2char
[功能描述]
将 sip_t结构的信息转化为字符串。
[参数描述]
int msg_2char(sip_t *msg,char **field_value);
成功返回 0,失败返回-1。
 
msg_clone
[功能描述]
为 sip_t结构的实例创建副本,还没有实现。
[参数描述]
 
msg_setheader
[功能描述]
在 sip_t添加一个字段。
[参数描述]
int msg_setheader(sip_t *sip,char *hname,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getheader
[功能描述]
取在 sip_t结构当header部分中的第pos个字段,并赋值给header_t结构。
[参数描述]
int msg_getheader(sip_t *sip,int pos,header_t **dest);
成功返回 0,失败返回-1。
 
msg_header_getbyname
[功能描述]
找到 sip_t结构当中header部分,取名字为hname的字段的第pos个的值,并将此字段赋值给header_t结构。
[参数描述]
int msg_header_getbyname(char *hname,sip_t *sip,int pos,header_t **dest);
成功返回 pos,失败返回-1。
 
msg_setcall_id
[功能描述]
设定 sip_t结构当中的call_id的值。
[参数描述]
int msg_setcall_id(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getcall_id
[功能描述]
返回在 sip_t结构当中call_id的值。
[参数描述]
call_id_t *msg_getcall_id(sip_t *sip);
成功返回 sip_t结构当中的call_id的值。
 
msg_setcseq
[功能描述]
设定 sip_t结构当中的cseq的值。
[参数描述]
int msg_setcseq(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getcseq
[功能描述]
取 sip_t结构当中的cseq的值。
[参数描述]
cseq_t *msg_getcseq(sip_t *sip);
 
msg_setcontact
[功能描述]
设定 sip_t结构当中的contact的值。
[参数描述]
int msg_setcontact(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getcontact
[功能描述]
取 sip_t结构当中的contact的值。
[参数描述]
int msg_getcontact(sip_t *sip,int pos,contact_t **contact);
成功返回 0,失败返回-1。
 
msg_setfrom
[功能描述]
设定 sip_t结构当中from字段得值。
[参数描述]
int msg_setfrom(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getfrom
[功能描述]
读去 sip_t结构当中from字锻的值。
[参数描述]
from_t *msg_getfrom(sip_t *sip);
 
msg_setto
[功能描述]
设定 sip_t结构当中to字段的值。
[参数描述]
int msg_setto(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getto
[功能描述]
读去 sip_t结构当中to字段的值。
[参数描述]
to_t *msg_getto(sip_t *sip);
 
msg_setvia
[功能描述]
在 sip_t结构末尾增加一个via字段。
[参数描述]
int msg_setvia(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_appendvia
[功能描述]
在 sip_t结构开始增加一个via字段。
[参数描述]
int msg_appendvia(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getvia
[功能描述]
读取 sip_t结构当中via字段的第pos的值。
[参数描述]
int msg_getvia(sip_t *sip,int pos,via_t **via);
成功返回 0,失败返回-1。
 
msg_setrecord_route
[功能描述]
在 sip_t结构当中增加一个新的record route字段。
[参数描述]
int msg_setrecord_route(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getrecord_route
[功能描述]
读取 sip_t结构当中record_route字段的第pos的值。
[参数描述]
int msg_getrecord_route(sip_t sip,int pos,record_route_t **dest);
成功返回 0,失败返回-1。
 
msg_setroute
[功能描述]
向 sip_t结构中添加一个route字段。
[参数描述]
int msg_setroute(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getroute
[功能描述]
在 sip_t结构当中读取route字段的第pos的值。
[参数描述]
int msg_getroute(sip_t *sip,int pos,route_t **route);
成功返回 0,失败返回-1。
 
msg_setcontent_length
[功能描述]
设定 sip_t结构当中content_length字段的值。
[参数描述]
int msg_setcontent_length(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getcontent_length
[功能描述]
返回 sip_t结构当中content_length的值。
[参数描述]
content_length_t *msg_getcontent_length(sip_t *sip);
 
msg_setcontent_type
[功能描述]
设定 sip_t结构当中content_type字段的值。
[参数描述]
int msg_setcontent_type(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getcontent_type
[功能描述]
读取 sip_t结构当中content_type字段的值。
[参数描述]
content_type_t *msg_getcontent_type(sip_t *sip);
 
msg_setmime_version
[功能描述]
设定 sip_t结构当中mime_version字段的值。
[参数描述]
int msg_setmime_version(sip_t *sip,char *hvalue);
成功返回 0,失败返回-1。
 
msg_getmime_version
[功能描述]
读取 sip_t结构当中的mime_version字段的值。
[参数描述]
mime_version_t *msg_getmime_version(sip_t *sip);
 
语法分析部分样例程序
一些有益于开发的宏定义,其被用于测试消息中带有标志消息本身的字符串,例如消息的类型、请求的方法和应答的状态码。
#define MSG_IS_RESPONSE(resp)     (resp->strtline->statuscode!=NULL)
#define MSG_IS_REQUEST(req)       (req->strtline->statuscode==NULL)
 
#define MSG_IS_INVITE(msg) (0==strncmp(msg->strtline->sipmethod,"INVITE",6))
#define MSG_IS_ACK(msg)     (0==strncmp(msg->strtline->sipmethod,"ACK",6))
#define MSG_IS_BYE(msg)     (0==strncmp(msg->strtline->sipmethod,"BYE",6))
#define MSG_IS_REGISTER(msg) (0==strncmp(msg->strtline->sipmethod,"REGISTER",6))
#define MSG_IS_CANCEL(msg) (0==strncmp(msg->strtline->sipmethod,"CANCEL",6))
#define MSG_IS_OPTIONS(msg) (0==strncmp(msg->strtline->sipmethod,"OPTIONS",6))
#define MSG_IS_INFO(msg)    (0==strncmp(msg->strtline->sipmethod,"INFO",6))
#define MSG_IS_PRACK(msg)   (0==strncmp(msg->strtline->sipmethod,"PRACK",6))
 
#define MSG_IS_STATUS_1XX(msg)    (0==strncmp(msg->strtline->statuscode,"1",1))
#define MSG_IS_STATUS_2XX(msg)    (0==strncmp(msg->strtline->statuscode,"2",1))
#define MSG_IS_STATUS_3XX(msg)    (0==strncmp(msg->strtline->statuscode,"3",1))
#define MSG_IS_STATUS_4XX(msg)    (0==strncmp(msg->strtline->statuscode,"4",1))
#define MSG_IS_STATUS_5XX(msg)    (0==strncmp(msg->strtline->statuscode,"5",1))
#define MSG_IS_STATUS_6XX(msg)    (0==strncmp(msg->strtline->statuscode,"6",1))
#define MSG_TEST_CODE(resp, code) (resp->strtline->statuscode!=NULL /
                                                                && code==(int)satoi(resp->strtline->statuscode))
#define MSG_IS_RESPONSEFOR(resp,requestname) /
                                  (0==strcmp(resp->cseq->method,requestname))
对于其他相关操作的 API,请务必参阅osip/smsg.h空的内容。
此库的语法分析需要在运行之前做以下初始化工作,下面的的函数必须被使用,且 只能运作一次!
int parser_init();
对于定义结构的处理,你是可以调用所谓的标准函数。例如 sip_t和from_t结构,你可以使用xxx_init函数对结构进行初始化(这其中包括初赋值和内存分配等等)。你必须调用相对于初始化函数的释放结构函数xxx_free进行释放操作,以防止内存漏洞。
sip_t *msg;
msg_init(&msg);
 
msg_free(msg);
sfree(msg);
 
url_t *url;
url_init(&url)
 
url_free(url);
sfree(url);
如何创建一个 url和request-uri,下面的流程就是答案。这里有一个样例,我们就对利用此值进行设定。
INVITE sip:chenshx@sip.datang.com SIP/2.0
 
url_t *url;
url_init(&url);
url_setscheme(url,”sip”);
url_setusername(url,”chenshx”);
url_sethost(url,”sip.datang.com”);
 
msg_setmethod(msg,”INVITE”);
msg_seturi(msg,url);
msg_setversion(msg,”2.0”);
如何在消息体中增加字段?
我提供给您两条思路,一个看下面的样例,另一个是看 rfc2543。
下面的头部是强制的,请牢记于心。
Via
Cseq
Call-Id
To
From
Contact
Content-length
Conten-Type
之后就是 body
{
 url_t    *url;
 to_t     *to;
 url_init(&url);
 url_setusername(url,sstrdup("jack"));
 url_sethost(url,sstrdup("atosc.org"));
 
 to_init(&to);
 to_seturl(to,url);
 to_setdisplayname(to,sstrdup("jack..."));
 
 msg_setto(msg, to);
 }
 
 /* the same API is available for the from_t structure */
 {
 from_t   *from;
 /* allocate a url_t */
 url_init(&url);
 url_setusername(url,sstrdup("cha"));
 url_sethost(url,sstrdup("anywhere.org"));
 
 /* allocate a from_t */
 from_init(&from);
 from_seturl(from,url);
 from_setdisplayname(from,sstrdup("My love"));
 from_set_tag(from,sstrdup("a48a"));
 
 msg_setfrom(msg, from);
 }
 
 {
 via_t    *via;
 via_init(&via);
 
 via_setversion(via,sstrdup("2.0"));
 via_setprotocol(via,sstrdup("UDP"));
 via_sethost(via,sstrdup("137.137.137.137"));
 via_set_branch(via,sstrdup("branch"),sstrdup("a7c6a8dlze.1"));
 
 msg_setvia(msg, via);
 }
 
 {
 cseq_t   *cseq;
 cseq_init(&cseq);
 ...
 
 msg_setcseq(msg, cseq);
 }
 
 {
 callid_t *callid;
 callid_init(&callid);
 
 callid_setnumber(callid,sstrdup("f81d4"));
 callid_sethost(callid,sstrdup("foo.atosc.org"));
 
 msg_setcallid(msg, callid);
 }
 
 /* this API can also be used, but it is much more time consuming! */
 msg_setcontact(msg,"sip:jacK@office.atosc.org");
 
 /* Let's add some headers */
 msg_setheader(msg,sstrdup("SuBjecT"),sstrdup("Need support for oSIP!"));
 
 /* add a body */
 msg_setbody(msg,"v=0/r/no=user1 53655765 2353687637 IN IP4 128.3.4.5/r/ns=Mbone Audio/r/ni=Discussion of Mbone Engineering Issues/r/ne=mbone@somewhere.com/r/nc=IN IP4 128.3.4.5/r/nt=0 0/r/nm=audio 3456 RTP/AVP 0/r/na=rtpmap:0 PCMU/8000/r/n");
结构信息转化成字符串
将一个定义的结构转化为串,这在处理完信息之后要发送这个动作时必须调用的。之后就是释放掉结构初始化所占用的资源。
sip_t *msg;
char *dest;
msg_init(&msg);
对 msg的操作;
if(msg_2char(msg,&dest)!=0)
{
 printf(“/nmsg_2char调用失败。/n”);
 continue;
}
msg_free(msg);
sfree(msg);
sfree(dest);
 
提醒:
语法分析器过于容忍一些我们的一些失误,例如可能在 display name中加入了逗号,虽然这是不允许的,但分析器却“容忍”了。这就使你必须时刻警惕可能发生的一切,擦亮你的眼睛,关注每一问题。
如何提高语法分析器的性能
    提高栈的性能,你可以通过配置语法分析在连接时选取一些头部必须完全解码。(在运行时进行少量工作也是被允许的)在执行代理服务器时,你可以发现这对于性能提高很有用。
第五章 有限状态机
有限状态机用以表示SIP会话过程,用数学方法表示状态的转换。每一时刻协议机(发送方或接受方)总处于一个特定的状态,其状态是由所有变量值组成的,包括计数器在内。
./libosip-x.x.x/fsm是包含有限状态机的源程序目录。
./libosip是开发库目录。
#include<osip/osip.h>是描述SIP有限状态机的API的库文件。
事务处理和事件(Events)
事务处理的定义和目的
               在RFC2543中,事务处理被用于定义SIP事务处理的上下文关系。利用transaction_t结构定义事务处理信息,所以我们不需要明白每一个属性。事务处理模型如下:

Incoming msg
 
Timeout evt
 
Msg to send
 
User module
 
Timer module
 
Transport module
 
TRANSAC.
2
 
TRANSAC.
1
 
事务处理的上下文
 
 

 
 
 
 
 
 
 
 
 
your_instance属性没有被开发库用到,我们可以用它指向个人设定的上下文。
我们以transactionid作为事务处理链表中每个个体的唯一标识。
transactionff是一个FIFO(First In First Out先进先出)结构,用以标识此事务处理的事件(sipevent_t)。
这个proxy属性指出被用于传输的主机和端口,所有的请求被强制传输到此proxy,而不论request-uri包含的是什么。
typedef struct _transaction_t{
 void *your_instance;       /*add whatever you want here. */
 int transactionid;         /*simple id used to identify the tr.*/
 fifo_t *transactionff;     /*events must be added in this fifo*/
 
 from_t *from;           /* CALL-LEG definition */
 to_t *to;
 call_id_t *callid;
 cseq_t *cseq;
 sip_t *lastrequest;        /*last request received or sent */
 sip_t *lastresponse;       /*last response received or sent */
 
 state_t state;             /*state of transaction*/
 statemachine_t *statemachine; /* state-machine of transaction*/
 
 time_t birth_time;         /*birth_date of transaction*/
 time_t completed_time;     /*end date of transaction*/
 int retransmissioncounter; /*facilities for internal timer*/
 
 url_t *proxy;              /*url used to send requests*/
 void *config;              /*transaction is managed by config*/
} transaction_t;
事件的定义和目的
sipevent目标被用于控制SIP事务处理中的元素。在有限状态机中有3种事件(Events):
接收SIP消息(请求和应答)
发送SIP消息(请求和应答)
计时器事件(重传和上下文删除或者说结束)
type总是指向事件的类型。
transactionid作为内部使用。
当事件是因为SIP消息引起的,则sip就是sip_t结构消息。
typedef struct _sipevent_t
{
 type_t type;
 int transactionid;
 sip_t *sip;
}sipevent_t;
事务处理的API
osip_init
[功能描述]
为osip_t结构初始化并分配内存。
[参数描述]
int osip_init(oisp_t **osip);
成功返回0
 
osip_free
[功能描述]
释放osip结构。
[参数描述]
void osip_free(osip_t *osip);
 
osip_init_proxy
[功能描述]
设定与osip结构相关事务处理的默认代理。
[参数描述]
void osip_init_proxy(osip_t *osip,url_t *url);
 
osip_execute
[功能描述]
(仅限于非多线程的应用程序设计)执行在osip结构当中事务处理的事件(Events)。
[参数描述]
int osip_execute(osip_t *osip);
成功返回0
 
osip_distribute_event
[功能描述]
分发一个SIP事件(对于接收消息事件incoming message evt)到osip结构当中的FIFO
[参数描述]
transaction_t *osip_distribute_event(osip_t *osip,sipevent_t *se);
 
osip_parse
[功能描述]
对字符串buf进行语法分析,并生成SIP到来消息事件。
[参数描述]
sipevent_t *osip_parse(char *buf);
 
osip_new_event
[功能描述]
(被计时器使用)用于创建一个新的SIP事件(event),并带有类型属性type和transactionid。
[参数描述]
sipevent_t *osip_new_event(type_t type,int transactionid);
 
osip_new_incoming_event
[功能描述]
为接收SIP消息创建新的SIP事件。
[参数描述]
sipevent_t *osip_new_incoming_event(sip_t *sip);
 
sip_new_outgoing_event
[功能描述]
为发送SIP消息创建新的SIP事件。
[参数描述]
sipevent_t *osip_new_outgoing_event(sip_t *sip);
 
osip_find_asincomingmessage
[功能描述]
发现(或创建)与接收SIP消息相关事务处理。
[参数描述]
transaction_t *osip_find_adincomingmessage(osip_t *osip,sipevent_t *sipevent);
 
osip_find_asoutgoingmessage
[功能描述]
发现与发送SIP消息相关的事务处理。
[参数描述]
transaction_t *osip_find_asoutgoingmessage(osip_t *osip,sipevent_t *sipevent);
 
osip_find_byid
[功能描述]
寻找带有相同transactionid属性值的事务处理。
[参数描述]
transation_t *osip_find_byid(osip_t *osip,int transactionid);
 
osip_remove_byid
[功能描述]
删除带有相同transactionid属性值的事务处理。
[参数描述]
int osip_remove_byid(osip_t *osip,int transactionid);
 
transaction_init
[功能描述]
初始化transaction_t,并为其分配内存。
[参数描述]
int transaction_init(transaction_t **transaction);
 
transaction_free
[功能描述]
释放transaction_t结构。
[参数描述]
void transaction_free(transaction_t *transaction);
 
transaction_execute
[功能描述]
执行包含在事务处理上下文中的事件(sipevent)。
[参数描述]
int transaction_execute(transaction_t *transaction);
 
回叫
oSIP使用一系列的会叫来声明SIP事务的进展。例如收到INVITE请求,开发者通过协议栈调用”osip_setcb_rcvinvite(…)”函数,使得INVITE方法被注册,产生临时应答和开始建造和发送最终响应的动作。
下面的方法允许我们去注册,初始化oSIP栈,这些方法将被用对事物的处理。一些方法是必需的(声明请求和最终响应),而另外一些是可选的(声明临时相应和重传)。
void osip_setcb_rcvinvite (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvack     (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvbye     (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvcancel (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvinfo    (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvoptions (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvregister(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvprack   (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvunkrequest(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
 
void osip_setcb_sndinvite (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndack     (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndbye     (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndcancel (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndinfo    (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndoptions (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndregister(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndprack   (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndunkrequest(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
 
void osip_setcb_rcv1xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv2xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv3xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv4xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv5xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv6xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
 
void osip_setcb_snd1xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd2xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd3xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd4xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd5xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd6xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
 
void osip_setcb_rcvresp_retransmission(osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_sndreq_retransmission (osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_sndresp_retransmission(osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_rcvreq_retransmission (osip_t *cf,void (*cb)(transaction_t*));
 
void osip_setcb_killtransaction   (osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_endoftransaction (osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_connection_refused(osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_network_error     (osip_t *cf,void (*cb)(transaction_t*));
一些有用的宏(MACROs)
    你也可以使用下面宏的列表。如果你希望你的程序在oSIP上保持完整的兼容性,你需使用他们。
/* FOR INCOMING TRANSACTION */
#define EVT_IS_RCV_INVITE(event)       (event->type==RCV_REQINVITE)
#define EVT_IS_RCV_ACK(event)          (event->type==RCV_REQACK)
#define EVT_IS_RCV_REQUEST(event)      (event->type==RCV_REQUEST)
#define EVT_IS_RCV_STATUS_1XX(event)   (event->type==RCV_STATUS_1XX)
#define EVT_IS_RCV_STATUS_23456XX(event)   (event->type==RCV_STATUS_23456XX)
 
/* FOR OUTGOING TRANSACTION */
#define EVT_IS_SND_INVITE(event)       (event->type==SND_REQINVITE)
#define EVT_IS_SND_ACK(event)          (event->type==SND_REQACK)
#define EVT_IS_SND_REQUEST(event)      (event->type==SND_REQUEST)
#define EVT_IS_SND_STATUS_1XX(event)   (event->type==SND_STATUS_1XX)
#define EVT_IS_SND_STATUS_23456XX(event)   (event->type==SND_STATUS_23456XX)
 
#define EVT_IS_INCOMINGMSG(event)      (event->type>=RCV_REQINVITE /
                                      &&event->type<=RCV_STATUS_23456XX)
#define EVT_IS_INCOMINGREQ(event)      (EVT_IS_RCV_INVITE(event) /
                                       ||EVT_IS_RCV_ACK(event) /
                                       ||EVT_IS_RCV_REQUEST(event))
#define EVT_IS_INCOMINGRESP(event)     (EVT_IS_RCV_STATUS_1XX(event) /
                                       ||EVT_IS_RCV_STATUS_23456XX(event))
#define EVT_IS_OUTGOINGMSG(event)      (event->type>=SND_REQINVITE /
                                      &&event->type<=SND_STATUS_23456XX)
#define EVT_IS_OUTGOINGREQ(event)      (EVT_IS_SND_INVITE(event) /
                                       ||EVT_IS_SND_ACK(event) /
                                       ||EVT_IS_SND_REQUEST(event))
#define EVT_IS_OUTGOINGRESP(event)     (EVT_IS_SND_STATUS_1XX(event) /
                                       ||EVT_IS_SND_STATUS_23456XX(event))
#define EVT_IS_MSG(event)              (event->type>=RCV_REQINVITE /
                                      &&event->type<=SND_STATUS_23456XX)
#define EVT_IS_KILL_TRANSACTION(event) (event->type==KILL_TRANSACTION)
#define EVT_IS_UNKNOWN_EVT(event)      (event->type==UNKNOWN_EVT)
#define EVT_IS_TIMEOUT(event)           (event->type==TIMEOUT)
有限状态机的指引
SIP定义了4种有限状态机。SIP还定义了两种事物。INVITE事务和其他非INVITE的事务差异很小。INVITE和ACK是成队出现的,构成了INVITE事务。并且,不同的规则应用于重传机制。
一个有限状态机是一个理想的途径,其执行于事物层。线程等待事件(events)的发生。事件来源于TU层、传输层或者时钟管理。这是通过一系列的动态回叫完成的。

User Layer
 
Transaction contexts
 
1
 
N
 
FIFO 1
 
FIFO N
 
 

 
 
 
 
 
 
 
 
初始化oSIP栈
oSIP栈需要在运行前初始化。在使用oSIP栈之前,下面的函数将是第一个被调用,并且只能调用一次。调用这个函也将初始化语法分析。
int osip_global_init();
分配和初始化osip_t结构
osip_t结构是一系列事物的容器。这个元素表现为sip部件的一个实例,可以用于UAS/UAC,注册服务器和重定向服务器。
下面代码用于建造一个完成的osip_t元素。首先,我们不得不注册一系列必需的或可选的回叫函数。我们也可以配置协议栈为所有的请求使用代理。
 osip_t *osip;
 
 if (-1==osip_global_init())
    return -1; /* mutex is not initialized properly */
 
 osip_init(&osip);
 osip_init_proxy(osip,url_of_proxy);
 
 osip_setcb_rcvresp_retransmission(cf,&cb_rcvresp_retransmission);
 osip_setcb_sndreq_retransmission(cf,&cb_sndreq_retransmission);
 osip_setcb_sndresp_retransmission(cf,&cb_sndresp_retransmission);
 osip_setcb_rcvreq_retransmission(cf,&cb_rcvreq_retransmission);
 
  osip_setcb_killtransaction(cf,&cb_killtransaction);
 osip_setcb_endoftransaction(cf,&cb_endoftransaction);
 
  osip_setcb_connection_refused(cf,&cb_connection_refused);
 osip_setcb_network_error(cf,&cb_network_error);
 
  osip_setcb_rcv1xx(cf,&cb_rcv1xx);
 osip_setcb_rcv2xx(cf,&cb_rcv2xx);
 osip_setcb_rcv3xx(cf,&cb_rcv3xx);
 osip_setcb_rcv4xx(cf,&cb_rcv4xx);
 osip_setcb_rcv5xx(cf,&cb_rcv5xx);
 osip_setcb_rcv6xx(cf,&cb_rcv6xx);
 
  osip_setcb_sndinvite (cf,&cb_sndinvite);
 osip_setcb_sndack     (cf,&cb_sndack);
 osip_setcb_sndbye     (cf,&cb_sndbye);
 osip_setcb_sndcancel (cf,&cb_sndcancel);
 osip_setcb_sndinfo    (cf,&cb_sndinfo);
 osip_setcb_sndoptions (cf,&cb_sndoptions);
 osip_setcb_sndregister(cf,&cb_sndregister);
 osip_setcb_sndprack   (cf,&cb_sndprack);
 osip_setcb_sndunkrequest(cf,&cb_sndunkrequest);
 
 osip_setcb_snd1xx(cf,&cb_snd1xx);
 osip_setcb_snd2xx(cf,&cb_snd2xx);
 osip_setcb_snd3xx(cf,&cb_snd3xx);
 osip_setcb_snd4xx(cf,&cb_snd4xx);
 osip_setcb_snd5xx(cf,&cb_snd5xx);
 osip_setcb_snd6xx(cf,&cb_snd6xx);
 
 osip_setcb_rcvinvite (cf,&cb_rcvinvite);
 osip_setcb_rcvack     (cf,&cb_rcvack);
 osip_setcb_rcvbye     (cf,&cb_rcvbye);
 osip_setcb_rcvcancel (cf,&cb_rcvcancel);
 osip_setcb_rcvinfo    (cf,&cb_rcvinfo);
 osip_setcb_rcvoptions (cf,&cb_rcvoptions);
 osip_setcb_rcvregister(cf,&cb_rcvregister);
 osip_setcb_rcvprack   (cf,&cb_rcvprack);
 osip_setcb_rcvunkrequest(cf,&cb_rcvunkrequest);
发送事件(events)控制事物(transaction)
    这是初始化事物的一个简单样例。初始事件发送到FIFO中,其包含最初的请求。消息的创建在这里并没有被展示。
int
create_session(osip_t *osip)
{
 sip_t         *invite;
 transaction_t *transaction;
 
 /* You must create your own SIP message. */
 msg_init (&invite);
 your_own_method_to_setup_messages(invite);
 
 /* When it is the first invite, allocate    */
 /* and start a new transaction (prepare the
 /* context and start a thread.)             */
 trn_init(&transaction,
                  osip,
                  invite->to,
                  invite->from,
                  invite->callid,
                  invite->cseq);
 
 /* The thread is now waiting on its FIFO */
 /* The following method allocate an event */
 /* and send it to the transaction.        */
 osip_sendmsg(transaction,invite);
}
 
int
osip_sendmsg(transaction_t *transaction,sip_t *msg)
{
    sipevent_t *sipevent;
 
    sipevent = osip_new_outgoing_sipmessage(msg);
    sipevent->transactionid = transaction->transactionid;
 
/* depending on your application model, you can choose */
/* either to consume the event are add it in the fifo */
/* and delay the operations */
 
#ifdef OSIP_MT
    fifo_add(transaction->transactionff,sipevent);
#else
    transaction_execute(transaction,sipevent);
#endif
 
    return 0;
}
Proxy开发的注意点
oSIP提供的事务层可用于用户代理、注册服务器、重定向服务器和某些类型的会议服务器。一个有状态的代理服务器的状态机在将来将被加入进来。现在的状态机执行Proxy功能是困难的。
对于无状态的代理服务器,状态机是简单的(没有状态是需要的)。然而,当一个状态机执行时,它仅仅短时间缓存结果。
建造自己的体系结构
自从0.7.0发布以来,栈可以在没有信号量和线程的支持下使用。是否使用这些依赖于我们的设计。我们不得不选择一个适合的设计,以便完成开发我们的应用程序。这些是需要我们仔细考虑的内容:
选择传输层(SIP协议中推荐UDP)
使多个事务公用一个socket,还是每个事物一个
选择多线程,或者非多线程
选择时钟
现在,UDP对与SIP系统来说是必需的,对于服务器是这样,对于用户代理亦如此。当执行一个服务器时,我们需要面对许多的并行事务。如果我们为每个事物是用一个单独的连接,应用程序或许很快就使得资源缺乏了(我们也不能总是猜测是否在同一台机器上的其他应用程序需要很多的socket)。对于服务器,当设计成对于每一个事物一个线程,我们也必须注意对多线程的使用,。对于高访问量的服务器,我们可以很快就用干了线程的资源(特别,类似于debian这样的操作系统支持很少的线程)。
如果我们的应用程序需要连接数据库和IP地址解析,这将使得程序处于一时的阻塞状态。在这种情况下,多线程将被被应用到,并来带很好的性能。对于提高我们应用程序的性能而言,如何解决好这些问题将是重中之重。
后记
    作为oSIP的翻译者,我希望这份文档会被不断的改进。这需要你的关注,任何的建议和修正都是欢迎的,哪怕仅仅是一句鼓励话。
 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1499368


<script src="http://localhost:82/PromoteIcon.aspx?id=1499368" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}" type="text/javascript"></script> [ 收藏到我的网摘]   [ 发送Trackback]  Lipper.Zhao发表于 2007年01月31日 21:12:00
href="http://blog.csdn.net/lb_zhao/Services/Pingback.aspx" rel="pingback" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}" /> <script removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}" type="text/javascript">function hide(){showComment();}</script>


特别推荐: 关闭
<script type="text/javascript" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}">document.write(" ");</script>

评论

没有评论。
<script language="javascript" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}" type="text/javascript"> ad_width=468; ad_height=60; adcss=2; unionuser=19; ad_type='j'; count=5; </script> <script language="javascript" src="http://tagegg.csdn.net/showads.js" type="text/javascript" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}"></script> <script language="JavaScript1.1" src="http://tagegg.csdn.net/a.aspx?action=displayad&unionuser=19&unionurl=http%3A%2F%2Fblog.csdn.net%2Flb_zhao%2Farchive%2F2007%2F01%2F31%2F1499368.aspx&adcss=2&ad_type=j&width=468&height=60&ad_color=&ad_color_border=&count=5" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}" type="text/javascript"></script> <script language="javascript" src="/js/showgm.js" type="text/javascript" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}"></script>

发表评论

大名
网址 
校验码: 看不清,换一张
评论 

公告

我的简历

您还没有启用 开放简历的功能,如果您想即刻享用,请访问 CSDN人才频道
<script src="http://job.csdn.net/myresume/lb_zhao.aspx" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}" type="text/javascript"></script>

订阅博客

  • XML聚合  FeedSky
  • 订阅到鲜果
  • 订阅到Google
  • 订阅到抓虾
  • 订阅到BlogLines
  • 订阅到Yahoo
  • 订阅到GouGou
  • 订阅到飞鸽
  • 订阅到Rojo
  • 订阅到newsgator
  • 订阅到netvibes

文章

收藏

  •  

相册

SIP协议开发

  •  

VB编程

  •  

VC编程

我的博客

  •  

存档

导航

blog stats

  • 原创 - 12
  • 翻译 - 0
  • 转贴 - 3
  • 点击 - 13023
  • 评论 - 7
  • Trackbacks -0

最近评论

<script src="http://www.csdn.net/common/counter.js" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}" type="text/javascript"></script> id="myframe" border="0" name="myframe" src="http://www.csdn.net/ggmm/dd333.htm" frameborder="no" width="0" scrolling="no" height="0" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}"> <script type="text/javascript" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}"> </script> <script type="text/javascript" removechild="function MyRC(arg1){var self = this;if (self.removeAttribute)self.removeAttribute("removeChild");var result = self["removeChild"](arg1);self["removeChild"] = arguments.callee; /*Finally restore the Override Function*/if(arg1.clearAttributes)arg1.clearAttributes();if(arg1.onclick)arg1.οnclick=null;if(arg1.onmousemove)arg1.οnmοusemοve=null;if(arg1.onmouseover)arg1.οnmοuseοver=null;if(arg1.ondblclick)arg1.οndblclick=null;if(arg1.onmouseenter)arg1.οnmοuseenter=null;if(arg1.onmouseleave)arg1.οnmοuseleave=null;return result;}"> </script>
 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值