介绍一个开源的SIP(VOIP)协议库PJSIP
本文系转载,出处不可考。
假设你对SIP/VoIP技术感兴趣,哪希望你不要错过:),假设你对写出堪称优美的Code感兴趣
,那么你也不可错过:)
这期间我想分析一下一个实际的协议栈的设计到实现的相关技术,算是自己的一个学习经
历记录.
最初选择这个库做分析的原因非常easy,文档齐全:),其他良好的特征则是慢慢发现的:)
www.pjsip.org
1. PJSIP简单介绍
PJSIP的实现是为了能在嵌入式设备上高效实现SIP/VOIP.其主要特征包含:
1).极具移植性.(Extremely portable)
当前可支持平台包含:
* Win32/x86 (Win95/98/ME, NT/2000/XP/2003, mingw).
* arm, WinCE and Windows Mobile.
* Linux/x86, (user mode and as kernel module(!)).
* Linux/alpha
* Solaris/ultra.
* MacOS X/powerpc
* RTEMS (x86 and powerpc).
正移植到:
* Symbian OS
2).很小的足印.(Very small footprint)
官方宣称编译后的库<150Kb,我在PC上编译后加上strip后大概173Kb,这对于嵌入
式设备,是个好消息:)
3).高性能.(High performance)
这点我们后面能够看看是否如作者宣称的:)
4).支持众多的特征.(Many features)
这点能够从http://www.pjsip.org/sip_media_features.htm#sip_features看出.
5).充足的SIP文档.(Extensive SIP documentation)
这是我最初选择该库的原因,当然不是终于的原因,终于的原因是它的code:)
2. PJSIP的组成.
事实上说是PJSIP不是特别贴切,这个库实际上是几个部分组成的.
1).PJSIP - Open Source SIP Stack[开源的SIP协议栈]
2).PJMEDIA - Open Source Media Stack[开源的媒体栈]
3).PJNATH - Open Source NAT Traversal Helper Library[开源的NAT-T辅助库]
4).PJLIB-UTIL - Auxiliary Library[辅助工具库]
5).PJLIB - Ultra Portable Base Framework Library[基础框架库]
而在最上层库的文件夹分为:(能够使用tree -d -L 1 查看)
$TOP/build [包括Makefile]
$TOP/build.symbian [针对symbian的Makefile]
$TOP/pjlib [參考上面]
$TOP/pjlib-util [參考上面]
$TOP/pjnath [參考上面]
$TOP/pjmedia [參考上面]
$TOP/pjsip [參考上面]
$TOP/pjsip-apps
$TOP/third_party
而在每一个子文件夹,能够看到分为:
bin [编译后产生的二进制文件]
build [Makefile]
build/output
build/wince-evc4
docs [doxygen的文档,用doxygen docs/doxygen.cfg产生]
include [头文件]
lib [编译后产生的库]
src [源码]
3. PJLIB简单介绍
要理解好PJSIP,就不得不先说说PJLIB,PJLIB算的上是这个库中最基础的库,正是这个
库的优美实现,才让PJSIP变得如此优越。
PJLIB提供了一系列特征。这是我们以下分析的重点。涉及到:
1).非动态内存分配[No Dynamic Memory Allocations]
实现了内存池,获取内存是从与分配的内存池中获取,高性能程序多会自己构造内存池
,后面我们会解释该内存池的使用以及主要的原理。依据作者的比較,是常规的 malloc(
)/free()函数的30倍。
2).OS抽象[Operating System Abstraction]
实现OS抽象的根本原因在与可移植性,毋庸置疑:).
涉及到:
a).线程[Threads.]
b).线程本地存储[Thread Local Storage.]
c).相互排斥[Mutexes.]
d).信号灯[Semaphores.]
e).原子变量[Atomic Variables.]
f).临届区[Critical sections.]
g).锁对象[Lock Objects.]
h).事件对象[Event Object.]
i).时间管理[Time Data Type and Manipulation.]
j).高解析的时间戳[High Resolution Timestamp.]
等等,这些我们后面分析代码时一一看来:)
3).低层的网络相关IO[Low-Level Network I/O]
这涉及到:
a).Socket抽象[Socket Abstraction.]
b).网络地址解析[Network Address Resolution.]
c).实现针对Socket的select API[Socket select() API.]
4).时间管理[Timer Management]
这主要涉及到两个部分。一个时定时器的管理,还有就是时间解析的精度(举例说来,就
是能精确到哪个时间等级,比方 POSIX sleep(),就仅仅能以秒为单位,而使用select()则可
以实现毫秒级别的计时)
5).各种数据结构[Various Data Structures]
主要有:
a).针对字符串的操作[String Operations]
b).数组辅助[Array helper]
c).Hash表[Hash Tabl]
d).链表[Linked List]
e).红黑平衡树[Red/Black Balanced Tree]
6).异常处理[Exception Construct]
使用的是TRY/CATCH,知道C++/JAVA之类面向对象语言的人看过会宛而一笑:)
7).LOG机制[Logging Facility]
非常显然,一个良好的程序,好的LOG机制不可少。
这能非常方便的让你去调试程序,对此我
是深有体会,不论什么时候。不要忘记“好的程序,是架构出来的;而能跑的程序,是调试出
来的:)”
8).随机数以及GUID的产生[Random and GUID Generation]
GUID指的是"globally unique identifier",仅仅是一个标识而已。比方说你的省份证,
算的上是一个GUID。当然。准确说来是“china unique identifier”:).
看了这么多的特征列举,是不是非常完备,的确。
总算是初步列举完了PJLIB的基本特征了。后面我们来说说它的使用与实现:
4. PJLIB的使用
有了上述介绍,是不是非常想知道这个库的使用,没关系,我们慢慢说来:)
首先是头文件和编译出来的库的位置,这就不必多说了,除非你没有使用过手动编译的库
,假设不太了解步骤,google一下。啊:)
1).为了使用这个库,须要使用:
#include
当然,也能够选择:
#include
#include
这样的分离的方式,只是。简单介绍其间,还是使用第一种吧:),毕竟,你不须要确认到你所
需的函数或者数据结构详细到哪个详细的头文件:)
2).确保在使用PJLIB之前调用 pj_init()来完毕PJLIB库使用前说必须的一些初始化.
这是一个不可缺少的步骤.
~~~~~~~~~~~~~~~~~~~~~~~
3).使用PJLIB的一些建议
作者对使用PJLIB的程序提出了一些建议,包含例如以下 :
a).不要使用ANSI C[Do NOT Use ANSI C]
观点非常明白。ANSI C并不会让程序具有最大的移植性,应该使用PJSIP库所提供的响
应机制来实现你所须要的功能.
b).使用pj_str_t代替C风格的字符串[Use pj_str_t instead of C Strings]
原因之中的一个是移植性,之二则是PJLIB内置的pj_str_t相关操作会更快(性能).
c).从内存池分配内存[Use Pool for Memory Allocations]
这非常明显,假设你知道为什么会使用内存池的话(提示一下,性能以及易用性:))
d).使用PJLIB的LOG机制做文字显示[Use Logging for Text Display]
非常明显:)
还有些关于移植的一些问题。不在我们的讨论范围。假设你须要移植到其他平台或者
环境,请參考http://www.pjsip.org/pjlib/docs/html/porting_pjlib_pg.htm
5. PJLIB的使用以及原理
最终開始提及实现原理以及详细的编码了:),前面的列举还真是个琐碎的事情,还是奔主题
来:).
5.1高速内存池[Fast Memory Pool]
前面说过,使用内存池的原因在于性能的考虑,原因是C风格的malloc()以及C++风格的new
操作在高性能或实时条件下表现并不太好,原因在于性能的瓶颈在于内存碎片问题.
以下列举其长处与须要基本的问题:
长处:
a).不像其他内存池,同意分配不同尺寸的chunks.
b).高速.
内存chunks拥有O(1)的复杂度,而且操作不过指针的算术运算,其间不须要使用锁住任
何相互排斥量.
c).有效使用内存.
除了可能由于内存对齐的原因会浪费非常少的内存外,内存的使用效率非常高.
d).可预防内存泄漏.
在C/C++程序中假设出现内存泄漏问题,其查找过程哪个艰辛,不足为外人道也:(
[以前有次用别人的Code,出现了内存泄漏,在开发板上查找N天,又没工具可在开发板上使
用,哪个痛苦,想自杀:(]
原因非常easy,你的内存都是从内存池中获取的,就算你没有释放你获取的内存,仅仅要你记得
把内存池destroy,那么内存还是会还给系统.
还有设计带来的一些其他益处,比方可用性和灵活性:
e).内存泄漏更easy被跟踪.
这是由于你的内存是在指定的内存池中分配的,仅仅要能非常快定位到内存池,内存泄漏的侦
測就方便多了.
f).设计上从内存池中获取内存这一操作是非线程安全的.
原因是设计者觉得内存池被上层对象所拥有,线程安全应该由上层对象去保证,这种话
,没有锁的问题会让内存分配变得很的快.
g).内存池的行为像C++中的new的行为,当内存池获取内存chunks会抛出PJ_NO_MEMORY_EX
CEPTION异常,当然,由于支持异常处理,也能够使用其他方式让上层程序灵活的定义异常的
处理.
[这是异常处理的基本出发点,可是这有大量的争论,原因是这改变了程序的正常流程,谁能
去保证这样的流程是用户所须要的呢,因此C++中的异常处理饱受争议,请酌情使用]
h). 能够在后端使用不论什么的内存分配器.默认情况下是使用malloc/free管理内存池的块,
可是应用程序也能够指定自己的策略(strategy),比如从一个全局存储空间分配内存.
恩,要知道,不论什么事务都是两面的(颇为佩服创造出“双赢”这个词的语言天才, 只是。文
字游戏对于技术人员不能说是件好事情:(),好了,使用时,不要觉得这个内存池是哪种"per
fect"的技术,要记得"不论什么设计,都是在各种限制条件中的一个折中,对于'戴着镣铐的舞蹈
',除了'舞蹈',也不要忘记'镣铐'哦",不要忘了告诫:):
告诫[Caveats]:
a).使用合适的大小来初始化内存池.
使用内存池时,须要指定一个初始内存池大小, 这个值是内存池的初始值,假设你想要高
性能,要慎重选择这个值哦,太大的化会浪费内存,过小又会让内存池自身频繁的去添加内存
,显然这两种情况都不可取.
b). 注意,内存池仅仅能添加,而不能被缩小(shrink),由于内存池没有函数把内存chunks释
放还给系统,这就要去内存池的构造者和使用者明白使用内存.
恩,主要的原理都差点儿相同了,后面我们来看看怎样使用这个内存池.
5.2内存池的使用[Using Memory Pool]
内存池的使用相当的简单,扳个手指头就搞定了,假设你看明确了上面的原理和特征:)
a).创建内存池工厂[Create Pool Factory]
上面不是提及内存池的内部分配策略以及异常处理方式么, 事实上这就是指定这个的:)
当然,不须要你每一个内存池都自己取指定策略和异常处理方式,PJLIB已经有了一个默认的
实现:Caching Pool Factory,这个内存池工厂的初始化使用函数pj_caching_pool_init()
b).创建内存池[Create The Pool]
使用pj_pool_create(),其參数分别为内存工厂(Pool Factory),内存池的名字(name),初
始时的大小以及增长时的大小.
c).依据须要分配内存[Allocate Memory as Required]
然后,你就能够使用pj_pool_alloc(), pj_pool_calloc(), 或pj_pool_zalloc()从指定
的内存池依据须要去获取内存了:)
d).Destroy内存池[Destroy the Pool]
这实际上是把预分配的内存还给系统.
e).Destroy内存池工厂[Destroy the Pool Factory]
这没什么好说的.
非常easy吧:)
作者在文档中给出了一个样例:
例如以下:
#include
#define THIS_FILE "pool_sample.c"
static void my_perror(const char *title, pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(1,(THIS_FILE, "%s: %s [status=%d]", title, errmsg, status));
}
static void pool_demo_1(pj_pool_factory *pfactory)
{
unsigned i;
pj_pool_t *pool;
// Must create pool before we can allocate anything
pool = pj_pool_create(pfactory, // the factory
"pool1", // pool's name
4000, // initial size
4000, // increment size
NULL); // use default callback.
if (pool == NULL) {
my_perror("Error creating pool", PJ_ENOMEM);
return;
}
// Demo: allocate some memory chunks
for (i=0; i<1000; ++i) {
void *p;
p = pj_pool_alloc(pool, (pj_rand()+1) % 512);
// Do something with p
...
// Look! No need to free p!!
}
// Done with silly demo, must free pool to release all memory.
pj_pool_release(pool);
}
int main()
{
pj_caching_pool cp;
pj_status_t status;
// Must init PJLIB before anything else
status = pj_init();
if (status != PJ_SUCCESS) {
my_perror("Error initializing PJLIB", status);
return 1;
}
// Create the pool factory, in this case, a caching pool,
// using default pool policy.
pj_caching_pool_init(&cp, NULL, 1024*1024 );
// Do a demo
pool_demo_1(&cp.factory);
// Done with demos, destroy caching pool before exiting app.
pj_caching_pool_destroy(&cp);
return 0;
}
我就不解释了:)
介绍一个开源的SIP(VOIP)协议库PJSIP的更多相关文章
- 介绍一个开源的 C++ 开发框架 openFrameworks 。
作为一个图形图像方向的研究生,我经常都在和 OpenGL .OpenCV 等多种 C++ 库打交道.这些库遵循着不同的规则和用法:另外,为了让自己的程序具有更多的交互能力,编写界面也是一个家常便饭的工 ...
- FunDA:一个开源的函数式数据处理工具库,也是Slick的补充
如果你是一个Slick用户,或者你是一个数据库编程人员正在尝试进入函数式编程模式,那么FunDA可能会帮到你. 目前市面上FRM(Functional Relational Mapper),即函数式的 ...
- 介绍一个开源的在线管理SQLServer的小工具--SQLEntMan
近来有许多人问起SQL在线管理的问题,遂将以前用过的一个开源SQL 在线管理工具修改了一下,并分享. 看下效果图: 原项目的地址:http://sourceforge.net/projects/asp ...
- 给各位聚聚和大大介绍一个开源项目 Expression2Sql(转)
阅读目录 一.Expression2Sql介绍 二.单表简单查询 三.Where条件 四.多表关联查询 五.group by 六.order by 七.函数 八.delete 删除 九.update ...
- 几个重要的开源视频会议SIP协议栈
视频会议系统由于需要与不同的终端进行连接,因此我们需要视频会议终端遵循统一的协议,H.323协议是视频会议软件使用最广泛的协议栈,但H.323设计得较为复杂,用户在调用H.323协议过程较多,因此利用 ...
- 教你一步步发布一个开源库到 JCenter
今天想来分享下,如何一步步自己发布一个开源库到 JCenter 这方面的博客网上已经特别多了,所以本篇并不打算仅仅只是记录流程步骤而已,而是尽可能讲清楚,为什么需要有这个步骤,让大伙知其然的同时还知其 ...
- 介绍一个非常好用的跨平台C++开源框架:openFrameworks
介绍一个非常好用的跨平台C++开源框架:openFrameworks 简介 首先需要说明的一点是: openFrameworks 设计的初衷不是为计算机专业人士准备的, 而是为艺术专业人士准备的, 就 ...
- 【Hades】ades是一个开源库,基于JPA和Spring构建,通过减少开发工作量显著的改进了数据访问层的实现
几乎每个应用系统都需要通过访问数据来完成工作.要想使用领域设计方法,你就需要为实体类定义和构建资源库来实现领域对象的持久化.目前开发人员经常使用JPA来实现持久化库.JPA让持久化变得非常容易,但是仍 ...
- 分享一个开源的JavaScript统计图表库,40行代码实现专业统计图表
提升程序员工作效率的工具/技巧推荐系列 推荐一个功能强大的文件搜索工具SearchMyFiles 介绍一个好用的免费流程图和UML绘制软件-Diagram Designer 介绍Windows任务管理 ...
随机推荐
- [转]查看Linux版本信息
一.查看Linux内核版本命令(两种方法): 1.cat /proc/version [root@S-CentOS home]# cat /proc/version Linux version 2.6 ...
- Conda相关命令的使用
-- Conda basics Conda基础命令 conda info 查看已安装的环境 conda install PACKAGENNAME 安装包 conda update PACKAGENAM ...
- 【Luogu】P4208最小生成树计数(状压乱搞)
题目链接 最小生成树有两个性质,两个性质都知道的话这题就变成码农题了. 1.无论最小生成树长什么样,所有权值的边的数量是不变的.比如我有棵最小生成树有两条权值为2的边四条权值为1的边,那这个图的所有最 ...
- poj3728The merchant
The merchant Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 4800 Accepted: 1666 Desc ...
- 欧拉回路 & 欧拉路径
欧拉路径 & 欧拉回路 概念 欧拉路径: 如果图 G 种的一条路径包括所有的边,且仅通过一次的路径. 欧拉回路: 能回到起点的欧拉路径. 混合图: 既有无向边又有无向边的图. 判定 无向图 一 ...
- C#递归删除进程及其子进程
/// <summary> /// 结束进程和相关的子进程 /// </summary> /// <param name="pid">需要结束的 ...
- C语言指针与数组
C语言指针与数组 数组的下标应该从0还是1开始? 我提议的妥协方案是0.5,可惜他们未予认真考虑便一口回绝 -- Stan Kelly-Bootle 1. 数组并非指针 为什么很多人会认为指 ...
- Linux中将一个GBK编码的文件转换成UTF-8编码文件
Linux中将一个GBK编码的文件转换成UTF-8编码文件 使用iconv 命令iconv -f GBK -t UTF-8 file1 -o file2 输出另一个文件,然后再覆盖源文件内容
- Linq技巧2——限制返回数据中的继承类型
假如有像下面这样的一个模型, 怎样在查询时仅仅需要的Cars呢? 这样的几个继承关系的实体中,查询时Where 条件可以加入OfType<SubType>(),你可以这样来写: var o ...
- 求N维前缀和
转载自http://blog.csdn.net/jzhang1/article/details/50528549#comments 膜拜 #include <iostream> #incl ...