——本手册指导开发者利用oSIP栈开发用户代理
原文标题:oSIP User Manual
原文作者:Aymeric Moizard
联系方法: [email protected]
版权保护:GNU Free Documentation License
项目网站: http://osip.atosc.org
译文作者:陈善学
联系方法: [email protected]
摘要
“会话发起协议(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".
前言
译者修正了原文中一些细微的错误。本文档是由译者独立完成,未免有翻译不妥之处,因此任何的建议和指正都是非常欢迎的。联系的方式是通过电子邮件至 [email protected]
索引
摘要. 1
版权. 1
前言. 1
索引. 2
正文. 3
第一章 SIP协议. 3
SIP独立与媒体. 3
SIP独立于传输层. 3
SIP有很好的扩展性. 3
SIP和最终用户服务. 3
第二章 SIP协议概述. 4
SIP语法. 4
SIP事务. 5
SIP会话. 5
Server 行为. 7
第三章 oSIP开发库. 7
目标. 7
开发的层次. 7
语法分析器. 7
有限状态机. 8
事务管理. 8
谁将受益于oSIP 8
允许的平台. 8
第四章 oSIP语法分析器. 8
文件. 8
SIP-URL(SIP地址). 9
URL定义的语法结构和设计目标. 9
url_t结构体操作的API 9
url_param_t和url_header_t操作的API 11
SIP headers操作的API 13
SIP Message操作的API 17
语法分析部分样例程序. 23
结构信息转化成字符串. 26
如何提高语法分析器的性能. 27
第五章 有限状态机. 27
事务处理和事件(Events) 27
事务处理的定义和目的. 27
事件的定义和目的. 28
事务处理的API 28
回叫. 31
一些有用的宏(MACROs) 32
有限状态机的指引. 33
初始化oSIP栈. 33
分配和初始化osip_t结构. 33
发送事件(events)控制事物(transaction) 34
Proxy开发的注意点. 36
建造自己的体系结构. 36
后记. 36
正文
第一章 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:[email protected] SIP/2.0
Via: SIP/2.0/UDP home.sipworld.org
To: sip:[email protected]
From: sip:[email protected]
Call-ID: [email protected]
CSeq: 1 INVITE
Contact: sip:[email protected]
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
[email protected]
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 [email protected] .
|-------------->| .
|302 Moved temporarily .
|<--------------| .
| ACK | .
|-------------->| .
| INVITE [email protected] |
| audio |
|------------------------------>|
| 180 Ringing |
|<------------------------------|
| 180 Ringing |
|<------------------------------|
| 200 OK |
|<------------------------------|
| ACK [email protected] |
|------------------------------>|
. .
later . .
. .
| INVITE [email protected] |
| audio + video |
|<------------------------------|
| 200 OK |
|------------------------------>|
| ACK [email protected] |
|<------------------------------|
. .
later . .
. .
| BYE [email protected] |
|------------------------------>|
| 200 OK |
|<------------------------------|
| |
样例
2-3 一个完整的会话
首先,所有的用户代理向注册服务器(在这个样例中,重定向服务器具有注册服务功能。注册服务器也就是定位服务器)。
会话由
INVITE事务发起,联系在[email protected]。一个重定向服务器使用重定向功能将找到的jack办公室的地址信息反馈给发起呼叫端UA1。UA1获取了新的目标地址的信息,发起对[email protected]的呼叫。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。如果发现了,请务必报告到[email protected],我将修正他们。
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:[email protected]>;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:[email protected] 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:[email protected]");
/* 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/[email protected]/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;