pjsip项目概要及c语言面向对象方法实现

最近在学习SIP,刚刚看了一个很好的开源工程 pjsip 很强大!  可是对 SIP 还不是很了解,特增加了一个学习计划:SIP学习

 

RFC 文档参见:http://www.ietf.org/rfc/rfc3261.txt

SIP 也是类似 HTTP 的一个协议集合,在网上搜索了一下相关的信息,摘录如下:

 

SIP消息的第一行包含消息的类型和所使用的SIP版本(2.0)。在请求中,这一行还包含一个叫做SIP URI的地址。这代表消息的目的地。
这个例子说明了如何使用请求消息INVITE、ACK和BYE,以及200 OK响应消息。SIP中还存在许多其他消息。下面给出一些请求:

消息 用法
INVITE 呼叫一个用户代理,传送一次呼叫。
ACK 确认呼叫。
BYE 终止呼叫。
CANCEL 终止还未OK的呼叫。
REGISTER 提供一项注册服务,带有一个联系地址和可以用来代替的别名。例如,在前面的例子中,地址sip:UAA@example.com就是sip:UserA@10.20.30.40的别名。然后,注册服务器example.com就可以把呼叫转发给地址10.20.30.40。
OPTIONS 询问一个用户代理的“能力”(例如,该用户代理能够识别的消息和编码)。

  现在给出一些经常使用的响应消息:

消息 用法
100 Trying 消息已收到,但是最终用户代理尚未进行处理。请等待。
180 Ringing 最终用户代理已经收到消息,正在提示用户。请等待。
200 OK 最终用户已经接受消息。
301 Moved Permanently & 302 Moved Temporarily 用户代理的地址已经改变,新的永久或临时地址位于Contact字段中。
400 Bad Request 普通错误消息。客户端不能识别消息。
401 Unauthorized & 407 Proxy Authentication Required 请使用证书重试。
404 Not Found 要联系的用户不存在或尚未注册。
408 Request Timeout 另一方没有响应。这意味着SIP消息永远不会OK。所有重试都将被丢弃。这并不意味着电话响太长时间(电话可以永远响铃)。

 

 

消息使用类似的头字段类型。下面给出其中的一些:

头字段 用法
From SIP请求的发送者。
To SIP请求的接受者。这通常与SIP URI相同(可以是一个“别名”或一个实际地址)。
Contact 用户代理的实际地址。
Call-ID 这并不是呼叫者的电话号码。它惟一地代表两个用户代理之间的完整呼叫或对话。所有相关的SIP消息都使用同一个Call-ID。例如,当一个用户代理收到一条BYE消息,根据Call-ID,它就知道要挂断哪次呼叫。
CSeq 消息的顺序编号。这在一次对话或一个Call-ID中是惟一的。这用于区别新的消息和“重试消息”。当一条初始消息没有及时OK时,重试就会进行,并会定时发送。
Content-Type 消息内payload的MIME类型。
Content-Length payload的大小,以字节为单位。信封和payload之间由一空行隔开。

 

还有一些与消息路由选择功能相关的头字段,如:Via、Route和Record-Route。许多头字段提供像Accept、User-Agent和Supported这样的功能。其他头字段则提供像Authorization、Privacy和WWW-Authenticate这样的安全性功能。还有很多其他的头字段存在。此外,这些字段中许多都有缩写语法(比如,From = f,To = t,等等)。

 

 

 

SIP 在移动开发中,经常让人提及的是 pjsip, 官方网站: http://www.pjsip.org/

他的优缺点网上和官方自有介绍。

 

我的学习计划就是对这个开源软件进行分析及与 SIP 协议进行对比,从而加大对 SIP 这块领域的了解。

 

下载源码 pjproject-1.10

通过网络了解如下信息:

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 [基础框架库]

 

通过 build 目录来看,这绝对是一个很强的跨平台平项目。还有对 Symbian 平台专门编译的目录。

按照以往的经验和上面的信息来讲 pjlib 是对跨平台进行支持的一个库!

 

而我的源码分析计划也就有了先后的顺序:

PJLIB  >PJLIB-UTIL  >  PJSIP  > PJMEDIA  PJNATH

 

还有二个目录 third_party 与 pjsip-apps 以后再看分析情况进行了解。

 

随意打开 PJLIB 目录,能够很好的看到目录的作用,

bin 生成的二进制文件
build 编译脚本
docs 文档目录
include 公开头文件目录
lib 生成的库目录
src 源码目录

 

 看来以后的工作不少! 下期去分析 PJLIB!!!

 

 

 

 

打开 PJLIB 的 include 目录

 

pjlib.h 非常明显的头文件,做为基础库的头文件,是我们分析的起点

 

  1. #ifndef __PJLIB_H__   
  2. #define __PJLIB_H__   
  3.   
  4.   
  5.   
  6. #include    // 网络相关   
  7. #include   // 网络   
  8. #include         // 数组   
  9. #include        // 断言   
  10. #include         // 字符相关   
  11. #include         // 错误支持   
  12. #include        // 异常支持   
  13. #include       // 管道缓冲?   
  14. #include   // IO 支持    
  15. #include       // IO 支持   
  16. #include          // GUID  Globally Unique Identifier(全球唯一标识符)   
  17. #include          // Hash   
  18. #include       // IO 队列         
  19. #include     // 网络   
  20. #include          // 数据处理 LIST   
  21. #include          //    
  22. #include           // 日志   
  23. #include          // 数学函数   
  24. #include            // 操作系统相关   
  25. #include          // 内存池   
  26. #include      // 内存管里相关   
  27. #include          // 随机数   
  28. #include        // 红黑树,数据处理相关   
  29. #include          // 网络相关   
  30. #include      // 网络   
  31. #include   // 网络多路选择   
  32. #include      // 网络加密相关   
  33. #include        // 字符串处理   
  34. #include         // 定时器   
  35. #include       // 宽字符,这在移动设备中经常使用   
  36.   
  37. #include    
  38.   
  39. #endif    


包含一很多 pj 目录下的头文件,我们尝试揣测一下作者的意图,既然是基础包,并且是跨平台台的项目,哪么这个库肯定是提供了一些系统最为基本相关的包装。而我们知道对跨平台进行处理的首先就是对基本数据类型进行包装。

 

types.h   看到了, 而我们打开其它头文件还发现,都是第一时间包含了此头文件。打开看一下

 

 

  1.   
  2. typedef int     pj_int32_t;  
  3.   
  4.   
  5. typedef unsigned int    pj_uint32_t;  
  6.   
  7.   
  8. typedef short       pj_int16_t;  
  9.   
  10.   
  11. typedef unsigned short  pj_uint16_t;  
  12.   
  13.   
  14. typedef signed char pj_int8_t;  
  15.   
  16.   
  17. typedef unsigned char   pj_uint8_t;  
  18.   
  19.   
  20. typedef size_t      pj_size_t;  
  21.   
  22.   
  23. typedef long        pj_ssize_t;  
  24.   
  25.   
  26. typedef int     pj_status_t;  
  27.   
  28.   
  29. typedef int     pj_bool_t;  
  30.   
  31. ......  
  32.   
  33.   
  34. #define PJ_SUCCESS  0   
  35.   
  36.   
  37. #define PJ_TRUE     1   
  38.   
  39.   
  40. #define PJ_FALSE    0   
  41.   
  42. ......  


基本的数据类型都定义了,再看上面的头文件,一个操作系统所支持的一些特征都包装好,后续的开发,如果是用 pjlib 支持的,只要 pjlib 支持的平台,应该上面的 pjsip 就是支持这个平台,同时,这个库的性能也是上面的开发程序的性能的瓶颈。

 

我们打开官网的文档,官方对此库的说明如下:

It's Open Source!
    开源

Extreme Portability
 轻便,可移值

 支持的平台:
 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

Small in Size
 非常的小巧
 
Big in Performance
 高性能

No Dynamic Memory Allocations
 没有动态内存分配器,因为这里面设计了一个快速内存池

Operating System Abstraction
 操作系统封装支持,包括线程、存储、互斥、信号、原子量、锁、事件、定时器等。

Low-Level Network I/O
 低级网络操作封装
 
 
Timer Management
 定时器管理

Various Data Structures
 多种数据结构,如:List、Array、String、Hash、RBTree、异常


Logging Facility
 日志处理

Random and GUID Generation
 随机数与GUID生成器

 

由此可见这个库确实是一个不错的库,可以单独拿出来使用,也可以向自己的新平台进行移值,当成为基础库去使用。具体的每一个功能不一一分析了!

 

古语日:
    三人务于精熟,而亮独观其大略! 

适合而此,目是SIP不能因为 PJLIB 的精彩而忽略SIP.

 

 

 

 

这个库看名字应该是对 PJLIB 进行扩展的一个包。同样打开 include 看看,发现一个头文件 pjlib-util.h

 

  1. #ifndef __PJLIB_UTIL_H__   
  2. #define __PJLIB_UTIL_H__   
  3.   
  4.   
  5.   
  6.   
  7. #include    
  8. #include    
  9.   
  10.   
  11. #include    
  12.   
  13.   
  14. #include    
  15. #include    
  16. #include    
  17. #include    
  18. #include    
  19. #include    
  20.   
  21.   
  22. #include    
  23. #include    
  24. #include    
  25.   
  26.   
  27. #include    
  28.   
  29.   
  30. #include    
  31.   
  32.   
  33. #include    
  34.   
  35.   
  36. #include    
  37.   
  38.   
  39. #include    
  40.   
  41.   
  42. #include    
  43.   
  44. #endif    


大概有如下内容:

  • DNS 查询
  • DNS 服务器
  • 文本扫描
  • XML 处理
  • STUN  在网上查是 (Simple Traversal of UDP over NATs,NAT 的UDP简单穿越)是一种网络协议
  • PCAP
  • HTTP

 

由此可见,这个包提供了更为高级的一些工具库,特别是针对网络方面,进行了相关封装。暂时不深入去了解,不过可以确定的是,如果可行我们其它地方需要用网络实现,特别是跨平台的项目,使用这里面的 XML HTTP 都是一件不错的选择!

 

以后有需要再详细分析该部份代码

 

 

 

 

PJSIP 我们要分析的重点

 

引用官方的一张图:

PJSIP 结构

 

PJLIB 做为与系统打交道底层支持库

PJLIB-UTIL 是封装的一些工具库

PJNATH  则是对 NAT 相关做支持

PJMEDIA 完成 SDP 与媒体栈的封装

而上面的又需要第三方库支持,就是源码目录中的 third_party

PJSIP 则简单很多只需要 PJLIB-UTIL 与 PJLIB 的支持

在上面又包装一层 PJSIP-SIMPLE 增加个人信息与IM的支持。

然后整合 PJSIP-SIMPLE 和 PJMEDIA 包装成 PJSIP-UA

最后包装成 PJSUA 来完成对应用程序的支持。

PJLIB 也开放给应用程序。

 

这就是PJSIP 团队想表达出整个设计,及用户使用的一个分层图表。同时也包含了各个部件在整个PJSIP库中的作用。

 

 

 

 

PJSIP URI Parser

在 SIP 中,用户 URI 描述是最基本的一项信息,也有相应的格式,先看看这个是怎样解析的吧。

 

实际上在好的工程下面都有完备的单元测试代码,同样在 PJSIP 中也是能找到相关的测试代码,我们可以打开 pjproject-1.10\pjsip\src\test\uri_test.c

 

在代码中举例出不下 38 种 URI 格式,用以程序分析,可见仅仅一个字符串解析,这个工程也是做的相当严谨。

 

在本工程中可以有如下收获,对以后的分析也是很重要的。

 

测试框架

为了让测试更加简单,更加有效,PJSIP中使用了一个很好的测试框架。本测试的入口函数是 uri_test 是为 test.c 中调用

在 test.c 中使用了宏 DO_TEST 简单封装了一个测试接口。并通过测试接口的反回值进行测试正确性验证。

 

在本测试中使用了查表法进行测试用例的扩充。

在测试后还进行了简单的统计。

最后输入出信息是以 HTML 格式输出,更容易查阅。

查表法用例

查表法的使用,更方便测试用例的扩充。

与平台无关性编程

以 PJLIB 为基础,进行了平台无关性编程,能够让测试在不同平台上有效快速的运行。

pjsip_sip_uri 格式

通过不同的测试用例,也学到了最为关键的 pjsip_sip_uri 结构体的格式。

 

  1.   
  2. typedef struct pjsip_sip_uri  
  3.  
  4.     pjsip_uri_vptr *vptr;         
  5.     pj_str_t        user;         
  6.     pj_str_t        passwd;       
  7.     pj_str_t        host;         
  8.     int         port;         
  9.     pj_str_t        user_param;       
  10.     pj_str_t        method_param;     
  11.     pj_str_t        transport_param;      
  12.     int         ttl_param;        
  13.     int         lr_param;         
  14.     pj_str_t        maddr_param;      
  15.     pjsip_param     other_param;      
  16.     pjsip_param     header_param;     
  17. pjsip_sip_uri;  


注释写的很清楚,但是还是要说一下,并且拿出代码中测试的最长的一个 URI 来对照

sip:user:password@localhost:5060;transport=tcp;user=ip;ttl=255;lr=1;maddr=127.0.0.1;method=ACK;pickup=hurry;message=I am sorry?Subject=Hello There&Server=SIP Server

user user
passwd password
host localhost
port 5060
user_param ip
method_param ACK
transport_param tcp
ttl_param 255
lr_param 1
maddr_param 127.0.0.1
other_param pickup, message
header_param Subject, Server

由此可见一些规律

  • sip: 打头
  • 用分号进行分割
  • 除第一段后每段都可以用 key=value 的样式进行填充
  • 有些 key 是固定的,其它的 key 可以扩展,会放到 other_param 中
  • 最后可以带 ? 附加更多的信息,此信息会放到 header_param 中

 

相信我们知道了这些,这个 URI 的分析也就很清楚了,同进更明白 SIP 中的 URI 可以如何的扩充自己的参数。

 

最后留下一个疑问?

他的结构体中第一行 pjsip_uri_vptr *vptr; 是用来做什么呢, 后面分析!

 

 

 

 

C语言中的面向对像

 

上文的 uri 分析中我们发现结构体总是有一个指针,注释写的很明白,是一个虚拟函数指针,和C++中的虚表中却是十分想像。

 

不此在 PJSIP 中十分常见,在 Android 源码中,还有其它的开源工程也是很常见的。好的,写个简单工程来解释疑惑。


 

  1.   
  2.   
  3. #include       
  4. #include    
  5. #include    
  6.   
  7. // 定义一个动物类,有二个方法   
  8. struct v_animal_ptr  
  9.     const char(*get_name)(); // 获取名字   
  10.     void (*shout)(int volume); // 喊叫   
  11. };  
  12.   
  13. typedef struct v_animal_ptr animal_vptr;  
  14.   
  15. typedef const char(*GET_NAME)();  
  16. typedef void        (*SHOUT)(int volume);  
  17.   
  18. // 定义一个动物类    
  19. struct animal  
  20.     animal_vptr *vptr;  
  21. };  
  22.   
  23. static const charanimal_get_name(const void *class 
  24.  
  25.     return (*((struct animal*)class)->vptr->get_name)();  
  26.  
  27.   
  28. static void animal_shout(const void *classint volume)  
  29.  
  30.     return (*((struct animal*)class)->vptr->shout)(volume);  
  31.  
  32.   
  33.   
  34. // 定义一个猪类,有一个高度值。   
  35. struct pig  
  36.     animal_vptr *vptr;  
  37.     int    height;  
  38. };  
  39.   
  40. static const charpig_get_name()  
  41.     return "i am pig" 
  42. };  
  43.   
  44. static void pig_shout(int volume)  
  45.     printf("heng heng %d\n"volume);  
  46. };  
  47.   
  48. // 定义猪的实现方法   
  49. static animal_vptr pig_vptr   
  50.  
  51.     (GET_NAME)  &pig_get_name,  
  52.     (SHOUT)     &pig_shout,  
  53. };  
  54.   
  55. // 定义一个狗类,有一个颜色值。   
  56. struct dog  
  57.     animal_vptr *vptr;  
  58.     int    color;  
  59. };  
  60.   
  61. static const chardog_get_name()  
  62.     return "i am dog" 
  63. };  
  64.   
  65. static void dog_shout(int volume)  
  66.     printf("wang wang %d\n"volume);  
  67. };  
  68.   
  69. static animal_vptr dog_vptr   
  70.  
  71.     (GET_NAME)  &dog_get_name,  
  72.     (SHOUT)     &dog_shout,  
  73. };  
  74.   
  75. static struct pig* init_pig()  
  76.     struct pig* anim (struct pig*) malloc(sizeof(struct pig));  
  77.     anim->vptr &pig_vptr;  
  78.     anim->height 10;  
  79.       
  80.     return anim;  
  81.  
  82.   
  83. static struct dog* init_dog()  
  84.     struct dog* anim (struct dog*) malloc(sizeof(struct dog));  
  85.     anim->vptr &dog_vptr;  
  86.     anim->color 255;  
  87.       
  88.     return anim;  
  89.  
  90.   
  91. int main()    
  92.  
  93.     struct animal *anim NULL;  
  94.     struct pig    *pig  NULL;   
  95.     struct dog    *dog  NULL;  
  96.   
  97.     // pig 子类   
  98.     pig init_pig();  
  99.     printf("name=%s\n"animal_get_name(pig));  
  100.       
  101.     printf("------------------------------------\n");  
  102.       
  103.     // dog 子类   
  104.     dog init_dog();  
  105.     printf("name=%s\n"animal_get_name(dog));  
  106.       
  107.     printf("------------------------------------\n");  
  108.   
  109.     // 转向 动物父类进行操作   
  110.     anim (struct animal *)pig;  
  111.     printf("name=%s\n"animal_get_name(anim));  
  112.     animal_shout(anim, 20);  
  113.       
  114.     printf("------------------------------------\n");  
  115.       
  116.     // 转向 动物父类进行操作   
  117.     anim (struct animal *)dog;  
  118.     printf("name=%s\n"animal_get_name(anim));  
  119.     animal_shout(anim, 100);  
  120.       
  121.     return 0;    
  122.    


运行结果:

  

[plain]  view plain copy print ?
  1. ./test  
  2. name=i am pig  
  3. ------------------------------------  
  4. name=i am dog  
  5. ------------------------------------  
  6. name=i am pig  
  7. heng heng 20  
  8. ------------------------------------  
  9. name=i am dog  
  10. wang wang 100  

 

上面的例子有点长,可以仔细看看,如果不关心实现细节,可以看一下 main 函数

确实可以模拟 C++ 类继承的方式进行操作了!

 

从开源工程确实能够了解很多有意思的东西!   想更深入了解可以看我的测试代码及 PJSIP 中的代码。

 

 

 

 

下图是来至 http://zhangwenjie.net 中的 PJSIP 开发指南

 

 

可惜在写此文的时候不能够访问!

 

从上图中可以看到 END POINT 是一个中心,有着非常重要的作用。而 PJSIP 官网中:

https://trac.pjsip.org/repos/doxygen/pjsip/html/modules.htm

 

也说到是一个 Very Core 部份,并且放到第一位!

 

我们必需要了解一下这部份!

官网是这样说的:

 

Detailed Description

SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of all SIP objects in an application. It performs the following roles:

  • it manages the allocation/deallocation of memory pools for all objects.
  • it manages listeners and transports, and how they are used by transactions.
  • it receives incoming messages from transport layer and automatically dispatches them to the correct transaction (or create a new one).
  • it has a single instance of timer management (timer heap).
  • it manages modules, which is the primary means of extending the library.
  • it provides single polling function for all objects and distributes events.
  • it automatically handles incoming requests which can not be handled by existing modules (such as when incoming request has unsupported method).
  • and so on..

 

看来是非常重要的一个模块!

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值