源文出处http://sofia-sip.sourceforge.net/refdocs/programming.html

编程指南

编写可移植代码

Sofia-SIP软件代码大部分都是可移植的。所有核心模块都是(至少应该是)遵循ANSI C 89规范的,也用到了一些ANSI C 99特性。如果有平台依赖的部分,都被集中分离到独立的C文件中,并封装接口,与软件其他部分隔离。

SU模块处理OS特定功能的抽象,比如内存管理、socket通信、线程和时间函数。

ANSI C 99 特性

Sofia-SIP软件中用到了以下的ANSI C 99特性:

·   Integer types

·   functions va_copy() and snprintf()

·   整数类型

·   va_copy()和snprintf()

在Sofia-SIP软件中不得使用以下ANSI C 99特性:

·   definition of a variable in the middle offunction code. (so always define your variables in the beginning of the block)

·   在函数体中间定义变量。(因此,请把变量定义在代码块的开头部分)

 

整数类型

如你所知,本机存储大小的长度取决于硬件、OS和编译器。这意味着实践中,int或long的长度不一定是32位。因此,您需要确保您打算存储在int中的值,实际上可以在不同平台上适用于int。根据经验,如果整数值超过8位,就应该使用具有定义长度的类型。

不过,如果你记住上面说的话,那么使用本地整数类型也无妨。根源是本地数据类型往往会有最好的性能表现。Int型总是存储最快的类型(通常也是最大长度)。

永远不要假定任何数据类型的长度。坚持使用sizeof()操作符获取长度。

Ç99标准定义了以下一些固定长度数据类型:

·   int64_t

·   uint64_t

·   int32_t

·   uint32_t

·   int16_t

·   uint16_t

·   int8_t

·   uint8_t

如果要使用这些数据类型,您必须包含头文件<sofia-sip/su_types.h>,它负责引用正确的文件。如果su头文件不可用,那么,您必须把以下代码片段包含到所有打算使用这些数据类型的文件中:

#if HAVE_STDINT_H

#include <stdint.h>

#elif HAVE_INTTYPES_H

#include <inttypes.h>

#else

#error Define HAVE_STDINT_H as 1 if youhave <stdint.h>, \

or HAVE_INTTYPES_H if you have<inttypes.h>

#endif

 

字节序

不同平台的主机字节序是不同的。当您只做本地处理时,不需要关心字节顺序。一旦您开始编写网络收发数据的代码,那你就必须考虑(字节序)。

如果需要转换字节序,很简单,调用下列函数之一就可以了:

函数htonl()会把无符号长整型从主机字节序转换为网络字节序。

函数htons()会把无符号短整型从主机字节序转换为网络字节序。

函数ntohl()会把无符号长整从网络字节序转换为主机字节序。

函数ntohs()会把无符号短整型从网络字节序转换为主机字节序。

要调用这些函数,您需要引用头文件<netinet/in.h> 或 <sofia-sip/su.h>

 

压缩结构体

默认情况下,编译器通常会调整结构体布局以提高访问速度。这意味着结构体内的字段会以32位对齐。如果您需要节省内存,可以使用结构体压缩。

为了告诉编译器你只需用要特定数量的位来存储一个变量,您可以使用位域。编译器或许会压缩位域,或许不会。

struct foo {

unsigned bar:5;

unsigned foo:2;

unsigned :0;

int      something;

}

如果编译器决定压缩这个结构,这段代码就会生成一个结构,在前七位中有bar和foo,然后从下一个32位边界开始存储结构体的其他字段。

使用位域压缩会有一个问题:在ARM平台下,通常不能访问不是以32位边界开始的32位数据。因此,实例结构中的:0填充字段看起来很方便,但使用要小心,这个字段的初始化在一些ARM编译器上会失败。(详情请咨询Kai Vehmanen)

有一种方式可以强制数据结构压缩—使用预处理指令#pragma(pack)。这个指令是编译器相关的,因此,如果你打算写真正的可移植代码,那么最好不要用。尽管我们已经在Sofia-SIP的部分代码中使用了它。唯一的选择是编写函数,通过位操作从32位字段获取所需的位,这不太方便,但是也不容易出错。

一个char缓存强制转换为int32_t,也会有同样的内存对齐问题。ARM架构下,您只能从32位边界开始读取int32_t,要特别小心。

使用位域和结构体压缩时,要小心这些陷阱。如果您不需要使用它们(比如说解析二进制协议),尽量不要使用。

 

文件和目录结构

Sofia-SIP库模块可以定义为libsofia-sip-ua目录层次结构下的一个子目录,其中包含一个<modulename> .docs文件(<modulename>当然是模块的实际名称)。

如果您想开始开发新模块,请联系Sofia-SIP开发团队,以便他们可以帮您设置基本模块。

模块目录的内容概述:

·    <modulename>.docs 
模块相关的主要文档文件。详细信息请参考Module documentation in <module>.docs

  • pictures 子目录
    包含模块文档所需的任何图片/图像。 使用的文件格式是GIF(用于html页面)和EPS(用于latex)。 如果使用某些程序(例如MS Visio)创建图片,则原始文件也必须存储在此处。
    (Note that old modules may have "images" subdirectory instead of "pictures")
    (注意:旧的模块子目录名可能是"images",而不是"pictures")

·   Makefile.am文件
请参处理GNU Autotools一章。

  • (可选)模块和模块测试的源码文件。 如果需要,源代码文件也可以位于子目录中。

 

编写面向对象代码

虽然C语言本身没有提供任何面向对象的特性,但也是可以用C语言进行面向对象编程。Sofia是用C语言实现的,使用了很多面向对象的技术。

数据隐藏

数据隐藏是把两个模块彻底分离的一种方法。模块外部的代码不能直接访问模块内部的数据,只能通过模块提供的接口函数达到目的(访问数据)。数据隐藏同时让我们在两个对象间定义协议变得更容易—所有通信都通过函数调用完成。

那么怎么用C语言实现数据隐藏呢?最简单的答案就是在头文件中声明数据结构体,但不要在头文件中定义。在<sofia-sip/msg.h>头文件中,我们用typedef 为struct msg_s结构体声明了一种名为msg_t 的数据结构,但是它的实际定义内容是放在"msg_internal.h"文件里的。msg模块之外的程序不能直接访问 struct msg_s 结构体,但它们可以通过msg模块在<sofia-sip/msg.h>接口中提供的方法函数访问msg_t对象。msg模块的实现方可以自由地改变结构体内部的布局,只需要保证函数接口的稳定(对外接口不要频繁变更)。

 

接口

抽象接口是Sofia采用的另一种面向对象实践。在<sofia-sip/msg_types.h>中定义的解析头域相关实现,是抽象接口的一个良好实例。消息头域 msg_header_t的类型定义,使用了两个C结构体:struct msg_common_s (msg_common_t)和struct msg_hclass_s(msg_hclass_t)。

抽象接口是由 msg_hclass_t结构里的虚函数表实现的。有点像C++典型实现的抽象类和虚函数。对于每个头域的实现,需要初始化函数表以实现相应的功能,如解码、编码、操作头域等等。和C++不同的是:对象类(msg_hclass_t)是用一个真实的数据结构描述的,其中包含了头域的特有数据,比如头域的名字。

msg_hclass_tsip_contact_class[] =

{{

/* hc_hash: */     sip_contact_hash,

/* hc_parse: */    sip_contact_d,

/* hc_print: */    sip_contact_e,

/* hc_dxtra: */    sip_contact_dup_xtra,

/* hc_dup_one: */  sip_contact_dup_one,

/* hc_update: */   sip_contact_update,

/* hc_name: */     "Contact",

/* hc_len: */      sizeof("Contact") - 1,

/* hc_short: */    "m",

/* hc_size: */     MSG_ALIGN(sizeof(sip_contact_t), sizeof(void*)),

/* hc_params: */   offsetof(sip_contact_t, m_params),

/* hc_kind: */     msg_kind_append,

/* hc_critical:*/ 0

}};

 

继承和派生对象

继承是在Sofia中有限使用的一种面向对象实践。最常见的继承例子是su_home_t的用法。许多对象是从su_home_t派生出来的,这意味着它们可以使用<su_alloc.h>中的的各种基础内存管理功能(函数)。

在这种情况下,继承意味着可以将指向派生对象的指针可以转换为指向基础对象的指针。换句话说,派生对象必须在其内存区域的开始处放一个基础对象:

struct derived

{

struct base base[1];

int         extra;

char       *data;

};

有三种方法可以把一个指向派生对象的指针转换为指向基础对象的指针:

struct base *base1 = (struct base *)derived;

struct base *base2 =&derived->base;

struct base *base3 =derived->base;

第三种方法之所以可用,是因为基础对象被处理为一元数组。

 

模板

Sofia库里有一些模板是用宏实现。其中包括<sofia-sip/htable.h>里定义的hash表,它可用于为不同对象定义hash表和访问器;此外还有在<sofia-sip/rbtree.h>定义的红黑树。

 

内存管理

当为特定任务分配大量内存块时,基于主页的内存管理非常有用。分配是通过内存主页完成的,内存主页保留了每个分配的内存块的引用。当内存主页被释放后,将同时释放它所引用的所有内存块。这简化了应用程序逻辑,因为应用程序代码不需要跟踪分配的内存并分别释放每个分配的块。

更多内存管理服务相关的内容,请参考<sofia-sip/su_alloc.h>的文档和memory managment tutorial 。

 

上下文数据的内存管理

内存主页用法的的一个典型实例是:在对象中保存一个内存页结构(su_home_t),作为操作上下文信息结构的一部分。

/* context infostructure */

struct context {

su_home_t  ctx_home[1];      /* memory home */

other_t   *ctx_other_stuff;  /* example of memory areas */

...

};

/* context pointer*/

struct context *ctx;

/* Allocatememory for context structure and initialize memory home */

ctx = su_home_clone(NULL, sizeof (struct context));

/* Allocatememory and register it with memory home */

ctx->ctx_other_stuff = su_zalloc(ctx->ctx_home, sizeof(other_t));

... processing and allocating more memory ...

/* Releaseregistered memory areas, home, and context structure */

su_home_zap(ctx->ctx_home);

 

组合分配内存

主页内存管理让程序员生活变得更轻松的另一个地方是:子程序进行多次的内存分配,一旦子程序执行失败,子程序所分配的所有内存都要被释放;如果子程序执行成功,子程序所分配的内存必须由上层内存管理控制的场景。

/* examplesub-procedure. top_home is upper-level memory home */

int sub_procedure( su_home_t *top_home, ... )

{

su_home_t temphome[1] = {SU_HOME_INIT(temphome) };

... allocations and other processing ...

/* was processingsuccessfull ? */

if (success) {

/* ok -> move registered allocated memory to upper level memory home */

su_home_move( top_home, temphome);

}

/* destroytemporary memory home (and registered allocations) */

/* Note than incase processing was succesfull the memory    */

/* registrationswere already moved to upper level home.     */

su_home_deinit(temphome);

/* returnok/not-ok */

return success;

}

 

测试代码

有关如何使用Sofia提供的宏编写模块测试的示例,请参阅<sofia-sip/tstdef.h>。

这里列出您应该测试的几个点:

  • 冒烟测试
    See that the module compiles, links and executes.
    模块编译,链接并执行。

·   模块API函数需要测试:

o  有效参数

o  无效参数

  • 100%路径覆盖目标
    (如果有一行代码未经测试,就不参判断程序是否工作。)
    对于代码的选定部分,您还是应该把目标定为100%分支/路径覆盖。
    但无论如何都要合理,因为在实践中完全覆盖几乎不可能实现(所以在实践中80%是可以的)。
  • 创建测试来检查假设和/或手段。
    例如:如果代码依赖于某个编译器功能。用没有相应功能编译的测试结果将会失败。

运行模块测试

Sofia SIP是用Automake构建的,Automake内置了单元测试的支持。要为你的模块添加一个自动运行的测试程序,只需要在模块的Makefile.am中添加以下几行(当然,必须自己编写测试程序):

TESTS = test_foo test_bar

check_PROGRAMS = test_foo test_bar

test_foo_SOURCES = foo.c foo.h

test_foo_LDADD = -L. -lmy

test_bar_SOURCES = bar.c bar.h

test_foo_LDADD = -L. -lmy

每个测试程序,都应该在成功时返回零,失败时返回非零错误代码。现在,当你执行"make check"命令时, my_test_foo 和 my_test_bar 将会被编译和运行。Make将打印测试结果的摘要。由于这些测试是由编译系统运行的,所以测试必须是非交互式的(不要提任何问题),并且不要依赖任何不在版本控制系统中的文件。

Sofia SIP的顶层makefile包含一个名为check的递归目标,因此,你可以用一条命令执行所有的测试程序:"cd sofia-sip; make check"。

 

处理GNU Autotools

Sofia-SIP的编译体系是基于GNU auto工具建立的,包括automake、autoconf和libtool。该工具集已成为构建Linux软件的事实上的方式。正因为如此,有很多这些工具相关的公开文档,当然还有很多很多的例子。

可以从 developer.gnome.org找到这些工具集的一份很好的说明文档。Autobook 提供了autoconf和automake的更多详细信息。 GNUmake manual也是一个很好的信息源。

autogen.sh

在顶层目录下,有一个名为autogen.sh的shell脚本。它调用一个叫autoreconf 的便利工具,为我们生成configure脚本。它还有固定的模式位,模式位并未存储在 darcs版本控制系统中。

$ sh autogen.sh

$ ./configure ... with your configureoptions ...

configure.ac

configure.ac文件(旧版本autoconf曾用configure.in)包含了autoconf的主要配置。 configure.ac的内容包含一系列检查宏,检查构建目标模块所需的所有外部库,非标准语言和编译器功能。

m4 files

Sofia-SIP自身的autoconf宏保存在顶层目录下,一个叫m4的文件中。这些宏,连同configure.ac使用到的的所有其它宏,都被一个叫aclocal的工具拷贝到一个模块特定的aclocal.m4文件中。

Contact Sofia-SIPdevelopment team, if you need changes to these files.

如果需要修改这些文件的内容,请联系Sofia-SIP开发团队。

aclocal.m4

aclocal.m4文件包含configure.ac中使用的autoconf宏的定义。这个文件由aclocal生成。

Makefile.am

Makefile.am文件里定义了需要编译的程序和库,还有编译这些目标所需要的源文件。当你执行automake命令后,它创建了Makefile.in文件。

这个文件由模块的开发者提供。

configure

运行configure脚本时,它执行configure.ac中所定义的所有检查动作,并把所有的xxx.in文件转换成相应的xxx文件,所有.in文件中的@FOO@描述的变量,也由configuration处理的结果值替换。例如:Makefile.in中的@srcdir@变量,变成了Makefile 中的源码实际路径(在主源代码树之外编译时格外有用)。

这个脚本由autoconf命令生成。

config.status

这个脚本保存了上一次传递给configre命令的参数表。如果需要,你可以通过以下命令返回上一次给定的configure脚本(通过给定的参数):"./config.status -r" 或者 "./config.status --recheck".

这个文件是由configure脚本生成的。

config.cache

这个文件保存了configure执行过程中各种检查的结果值。在configure执行失败时,你可以删除这个文件,并重新执行configure用于检查。

这个文件由configure脚本生成。

Makefile

Makefile 文件包含包含了编译目标库和程序的实际规则。它是供make工具使用的。Makefile 文件以Makefile.in文件为模板生成的,而Makefile.in文件是autoconf 命令生成的。请确保"make dist"和"make install"这两个目标能正常工作。

这个文件是由config.status和configure脚本生成的。

config.h

这个文件包含了C语言定义的各种可配置的编译开关。

这个文件是由config.status和configure脚本生成的。

sofia-sip/su_configure.h

这个文件包含C语言定义的Sofia SIP UA库的配置。

这个文件是由config.status和configure脚本生成的。

Sofia-SIP模块开发指南相关推荐

  1. ROCKCHIP PWM模块开发指南

    ROCKCHIP PWM模块开发指南 前言 脉宽调制(PWM,Pulse Width Modulation)功能在嵌入式系统中是非常常见的,它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效 ...

  2. 速卖通开放平台 预约交货模块开发指南

    速卖通开放平台,最近新增加一个功能模块,就是:预约交货的功能.简单说,就是让包裹更早的能够上网.可以快速查询什么的,有点像虾皮平台的首公里功能.其实也不算最近,上半年5月份的时候就开始推这个功能了,据 ...

  3. 微赞php.ini_微赞微擎二开笔记开发手册之模块开发指南(九)

    微赞微擎二开笔记开发手册之模块开发指南(九) 阅读本章内容之前请确定已了解微赞工作流程. 本章内容主要介绍如何编写及发布微赞功能模块. 微赞功能模块是微赞系统中扩展的独立功能, 微赞系统提供了相应的机 ...

  4. Xposed模块开发指南

    1.Xposed框架是什么? Xposed框架是一款开源框架,其功能是可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运行. ...

  5. 全志Tina Linux Camera 摄像头模块开发指南 全网最详细版本支持百问网T113-Pro DongshanPI-NezhaD1-H DongshanPI-D1s V853-Pro等开发板

    1 概述 编写目的:介绍camera 模块在sunxi 平台上的开发流程. 适用范围:本文档目前适用于tina3.0 以上具备camera 的硬件平台. 2 模块介绍 2.1 模块功能介绍 用于接收并 ...

  6. Magisk模块开发指南

    BusyBox Magisk整合了功能完整的BusyBox二进制文件(包括对SELinux的完整支持).执行文件位于/data/adb/magisk/busybox.Magisk的BusyBox支持运 ...

  7. 服务器架设笔记——Apache模块开发基础知识

    通过上节的例子,我们发现Apache插件开发的一个门槛便是学习它自成体系的一套API.虽然Apache的官网上有对这些API的详细介绍,但是空拿着一些零散的说明书,是很难快速建立起一套可以运行的系统. ...

  8. RK3399PRO-RKNN_DEMO模块开发最新资料下载

    RK3399PRO-RKNN_DEMO模块开发指南V0.1_20181208 概述 本文档主要介绍 Rockchip 处理器内 RKNN_DEMO 的使用方法 1 RKNN_DEMO 运行 1.1 概 ...

  9. 移动IM开发指南3:如何优化登录模块

    <移动IM开发指南>系列文章将会介绍一个IM APP的方方面面,包括技术选型.登陆优化等.此外,本文作者会结合他在网易云信多年iOSIM SDK开发的经验,深度分析实际开发中的各种常见问题 ...

最新文章

  1. Android控件点击圆形阴影反馈解决方案
  2. is_valid校验机制
  3. linux下使用yum安装新版php7.0
  4. mpeg4ip.h:126: error: new declaration ‘char* strcasestr(const char*, const char*)’
  5. java程序_Java程序员必备----Java命令大全
  6. java rmi漏洞工具_学生会私房菜【20200924】Weblogic WLS核心组件反序列化命令执行突破(CVE20182628)漏洞复现...
  7. AngularJs自学心得
  8. 软件定义网络文章列表
  9. 基于JAVA+SpringMVC+MYSQL的火车票订票系统
  10. Apache No installed service named “Apache2.4“的解决办法
  11. sql server 2000 sp3 补丁
  12. MobiCom2019几篇有意思的文章
  13. 软件测试面试两分钟自我介绍
  14. Excel 2010 VBA 入门 058 定时运行程序
  15. mysql分页中offset作用_mysql中分页查询(LIMIT和OFFSET关键字讲解)一语道破天机
  16. 计算机英语带字幕,计算机专业英语听力字幕.doc
  17. 埃里克贝里奇_未来公司客服的标配?苹果Watch智能手表开启新时代
  18. python爬取携程网游记_Python爬虫案例:爬取携程评论
  19. 打开桌面计算机投屏到扩展屏,电脑投屏到电视显示不完全解决办法
  20. 丛林木马(数学 思维

热门文章

  1. HiveSQL面试题18--腾讯面试用户连续签到天数及历史最大连续签到天数问题
  2. 云淡风清——(二)北方情怀
  3. grep排除目录和文件
  4. 察觉用户心理,提升产品的吸引力
  5. git 无法push远程仓库 Note about fast-forwards
  6. 【蓝桥杯嵌入式】比赛笔记(1)基础模块配置
  7. 文本生成(一)【NLP论文复现】Unified Language Model 文本生成从未如此轻松
  8. 苹果收购Beats为WWDC带来亮点 三星Simband爆冷
  9. PyTorch中 tensor.detach() 和 tensor.data 的区别
  10. Oracle 19c使用dbca来搭建物理DG--主rac备rac