Sofia-SIP模块开发指南

Sofia-SIP模块开发指南
编程指南
编写可移植代码
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_tp612
如果要使⽤这些数据类型,您必须包含头⽂件<>,它负责引⽤正确的⽂件。如果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> 或 <>
压缩结构体
默认情况下,编译器通常会调整结构体布局以提⾼访问速度。这意味着结构体内的字段会以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
模块相关的主要⽂档⽂件。详细信息请参考。
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语⾔实现数据隐藏呢?最简单的答案就是在头⽂件中声明数据结构体,但不要在头⽂件中定义。在<>头⽂件中,我们
⽤typedef 为struct msg_s结构体声明了⼀种名为 的数据结构,但是它的实际定义内容是放在"msg_internal.h"⽂件⾥的。msg模块之外的程序不能直接访问 结构体,但它们可以通过msg模块在<>接⼝中提供的⽅法函数访问对象。msg模块的实现⽅可以⾃由地改变结构体内部的布局,只需要保证函数接⼝的稳定(对外接⼝不要频繁变更)。
接⼝
抽象接⼝是Sofia采⽤的另⼀种⾯向对象实践。在<>中定义的解析头域相关实现,是抽象接⼝的⼀个良好实例。消息头域 的类型定义,使⽤了两个C结构体:()和()。
抽象接⼝是由 结构⾥的虚函数表实现的。有点像C++典型实现的抽象类和虚函数。对于每个头域的实现,需要初始化函数表以实现相应的功能,如解码、编码、操作头域等等。和C++不同的是:对象类()是⽤⼀个真实的数据结构描述的,其中包含了头域的特有数据,⽐如头域的名字。
[] =
{{
/* hc_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(), sizeof(void*)),
/* hc_params: */  offsetof(, m_params),
/
* hc_kind: */    ,
/* hc_critical:*/ 0
}};
继承和派⽣对象
继承是在Sofia中有限使⽤的⼀种⾯向对象实践。最常见的继承例⼦是的⽤法。许多对象是从派⽣出来的,这意味着它们可以使
⽤<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;
第三种⽅法之所以可⽤,是因为基础对象被处理为⼀元数组。ftd vs ks
模板
Sofia库⾥有⼀些模板是⽤宏实现。其中包括<>⾥定义的hash表,它可⽤于为不同对象定义hash表和访问器;此外还有在<>定义的红⿊树。
内存管理
当为特定任务分配⼤量内存块时,基于主页的内存管理⾮常有⽤。分配是通过内存主页完成的,内存主页保留了每个分配的内存块的引⽤。当内存主页被释放后,将同时释放它所引⽤的所有内存块。这简化了应⽤程序逻辑,因为应⽤程序代码不需要跟踪分配的内存并分别释放每个分配的块。
更多内存管理服务相关的内容,请参考<>的⽂档和 。
上下⽂数据的内存管理
内存主页⽤法的的⼀个典型实例是:在对象中保存⼀个内存页结构(),作为操作上下⽂信息结构的⼀部分。
/* context infostructure */
struct context {
气体处理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 = (NULL, sizeof (struct context));
/* Allocatememory and register it with memory home */
ctx->ctx_other_stuff = (ctx->ctx_home, sizeof(other_t));
... processing and allocating more memory ...
/* Releaseregistered memory areas, home, and context structure */

本文发布于:2024-09-22 18:27:32,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/4/200962.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:模块   对象   内存   函数   定义   结构
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议