jphone项目设计介绍
一、源码路径
https://github.com/weiganyi/jphone
二、背景
最开始做这个项目的时候,是由于发现了一个叫PjSIP的开源SIP协议栈,实现上要比OSIP成熟许多,用它来开发SIP应用也更简单方便,我就考虑基于PjSIP来做一个SIP软电话原型。
我选择用C++语言来实现,原因是我想要在这个项目中实现一些通用机制,C++语言比那些解释语言更适于底层开发,同时C++的语言特性也要比C语言丰富许多。
软件界面这块我用VC来做,但是我之前只用过VC6,新的VC版本没有用过,考虑到这只是一个原型产品,界面难看点关系不大,本着界面能用就行的原则,还是使用VC6来做界面。
对于软件配置的存储,采用本地文件和MySQL数据库并行的方式,主要考虑还是想提供多样化的选择。
项目分了两个阶段,第一阶段我把这个SIP软电话原型实现了,也实现了一些基本的通用机制。后来随着自己对软件系统架构理解的积累,我考虑着要把那些通用机制增强成一些通用组件框架,并且能够基于这个组件方便的开发其它的应用程序,这就对组件的通用性提出了更高的要求。第二阶段我进行了代码重构和增强,实现了一个通用的应用程序组件框架。为了验证组件框架的通用性,除了原来基于它实现的SIP软电话原型(单线程模式)外,我还实现了一个日志服务器原型(多线程模式)。
对于操作系统接口的使用,目前直接使用的Windows的接口,后续如果要做多操作系统兼容,可以再对操作系统接口进行封装,这是个工作量问题。
三、功能实现
项目的功能实现分以下三部分:
1、实现通用的应用程序组件框架,封装操作系统的基本接口,不依赖第三方库实现开发其它应用程序所需的各类组件模块,提供方便的使用接口。
2、实现SIP软电话原型,能够通过软件界面操作完成基本通话,支持基本的配置参数,发起呼叫可以通过号码按键或点击最近通话号码的方式进行。
3、实现日志服务器原型,支持最多10个日志客户端的同时连接,并能显示选中连接上接收的日志,支持基本的配置参数。
四、总体设计思路
1、组件框架包含了几十个类,所以设计了一个类的继承体系树,从最基本JObject根类派生出其他各式各样的类。把同一类功能的类封装在一个模块里,对于每个模块,如果有多种类型的类就要定义一个抽象基类来定义它们的接口。
2、通过一系列通用模块来构成应用程序组件框架,如:模块、异常、智能指针、锁、单件、字符串、日志、内存管理、定时器、通信、消息路由、事件、链表、队列、持久化、后台任务、线程管理等。
3、基于应用程序组件框架,通过增加上层的SipUa模块和代理模块就能实现出SIP软电话,通过增加上层的日志服务器模块和代理模块就能实现出日志服务器。
4、为了避免名字污染,对于应用程序组件框架的各个模块,都包含在JFrameWork的名字空间里。在SIP软电话和日志服务器里,要把这个名字空间引用进来。
5、应用程序的配置存储支持Ini文件、Xml文件、MySQL三种方式,根据JDaemon后台任务的配置文件jdaemon_cfg.ini来决定采用哪种方式。
6、组件框架内部定义了进程、线程、模块三个级别。对于SIP软电话和日志服务器进程,包含的线程有:JModuleThread线程、JTimer线程、JPjSipUa线程、JPhoneAgent线程、JLogServerAgent线程、JLogSrvThread线程、JLogMsgThread线程。对于JModuleThread线程,包含的模块有:JStaticMemory模块、JLog模块、JTimer模块、JDaemon模块、JPjSipUa模块。
7、考虑组件框架要支持相对复杂的应用程序,对于线程和模块之间的通信,采用Actor模式的消息串行通信方式,最大限度减少线程和模块之间锁的使用,提高并发性。
8、线程内定义模块这个级别,主要是为了能够通过配置模块优先级来在线程内对模块进行执行调度,实现类似协程的机制来减少线程调度的发生,提高系统性能。
9、一些核心模块采用定义全局单件对象的方式方便访问,并用锁保护共享数据访问。
五、文件及类的设计
JObject.cpp/h:
virtual class JObject
所有类的基类,包含基本数据类型封装和通用字符串处理。
virtual class JModule: public JObject
模块基类,包含初始化和事件处理等接口,供各种模块类继承。
class JException: public JObject
异常处理类,用于异常处理时抛出的异常对象。
virtual class JEventBody: public JObject
事件体基类,包含克隆、序列化、反序列化等接口,可以派生出各种类型的事件体类。
JAutoPtr_T.cpp/h:
template<class TYPE> classJAutoPtrBase: public JObject
智能指针模板基类,包含成员访问接口。
template<class TYPE> class JAutoPtr:public JAutoPtrBase<TYPE>
智能指针模板类,增加赋值操作符。
JAutoPtr.cpp/h:
智能指针模板类函数特化,针对锁类型进行函数特化处理。
JDaemon.cpp/h:
class JDaemonCfg: public JEventBody
后台任务配置类,包含了后台任务类的配置,用于事件通信。
class JDaemon: public JModule
后台任务类,作为一个模块独立运行,目前主要作用于设置和获取系统配置的存储方式,后续可扩展承载其他系统级任务。
JEvent.cpp/h:
class JEvent: public JObject
事件类,包含源地址和目的地址,通过进程名、线程名、模块名三元组共同标明一个地址,并通过事件体指针来包含具体的事件体内容。
JList_T.cpp/h:
template<class Type> class JListItem:public JObject
列表项模板类,存储具体的内容对象,并且保持与其它列表项的链接。
template<class TYPE> classJListIterator: public JObject
列表迭代器模板类,能够对一个列表模板类对象进行迭代访问。
template<class Type> class JList:public JObject
列表模板类,包含对列表的各种类型访问。
template<class Type> class JQueue:public JObject
队列模板类,以列表模板类为基础,实现的一个队列以及相应的队列操作。
JList.cpp/h:
列表项模板类函数特化,针对堆内存、事件类、哈希数据类型进行函数特化处理,列表模板类函数特化,针对持久化记录类型进行函数特化处理。
JLock.cpp/h:
class JLock: public JObject
锁同步对象类,封装了操作系统的临界区对象,提供线程间的互斥访问保护。
JLog.cpp/h:
class JFmt: public JObject
格式类,存储当前日志打印的模块和级别。
class JLogDecorator: public JObject
日志装饰基类,用于日志输出时的装饰模式。
class JLogTimeDecorator: publicJLogDecorator
日志时间装饰类,在日志输出时添加上时间戳。
class JLogOutput: public JObject
日志输出基类,定义了日志输出和装饰的接口。
class JLogOutputLocal: public JLogOutput
日志控制台输出类,向串口控制台输出日志信息。
class JLogOutputFile: public JLogOutput
日志文件输出类,向本地文件输出日志信息。
class JLogOutputRemote: public JLogOutput
日志远端输出类,采用TCP连接远程的日志服务器,向服务器发送日志信息。
class JLogCfg: public JEventBody
日志配置类,包含了日志类的配置,用于事件通信。
class JLog: public JObject
日志类,定义了各种日志输出的操作符,通过模块和级别来控制日志输出,能采用控制台、文件、远程三种方式输出日志,并使用装饰模式来装饰日志信息,可以跟踪记录函数调用序列,同时启动定时器来维护远程日志的TCP连接。
class JLogAutoPtr: publicJAutoPtrBase<JLog>
智能指针模板日志特化类,利用智能指针的构造和自动析构特性来记录函数进入和退出日志信息。
JMemory.cpp/h:
virtual class JMemory: public JObject
内存管理基类,定义了分配、释放、内存越界检查、信息打印等接口。
class JTrunkMemory: public JMemory
块内存管理类,采用预定义不同大小的内存块池的方式来管理内存分配释放。
class JHeapMemory: public JMemory
堆内存管理类,采用线性的内存区来分配和释放内存,弥补超过最大块内存大小的分配需求。
class JStaticMemory: public JModule
静态内存管理类,自己管理内存的分配和释放,内部采用块和堆两种管理模式,对分配的内存增加头尾信息,包含内存分配时的函数调用序列以及校验字段。启动定时器通过头尾校验字段探测内存写越界的情况,还能接受命令打印出未释放的内存信息,包含内存分配时的函数调用序列,供定位内存泄漏问题使用。
JRoute.cpp/h:
class JHash: public JObject
哈希类,提供哈希算法。
class JHashData: public JObject
哈希数据类,存储路由信息及实现相关操作。
class JRoute: public JObject
路由类,维护路由信息,并通过哈希查找路由类型,在进程内共享
JSerialization.cpp/h:
virtual class JPersistence: public JObject
持久化基类,提供持久化接口,对于数据存储持久化采用记录列表类型的参数。
class JIni: public JPersistence
Ini文件持久化类,采用本地Ini文件来存储持久化数据。
class JMySql: public JPersistence
MySQL持久化类,采用MySQL数据库来存储持久化数据,同时数据库访问采用MySQL提供的C库访问接口,不采用VC的ODBC访问方法,保证可移植性。
class JMiniXML: public JPersistence
Xml文件持久化类,采用本地Xml文件来存储持久化数据。
class JSerialization: public JObject
持久化类,根据数据存储方式对持久化数据进行存储和获取。
JSingleton_T.cpp/h:
template<class TYPE> classJSingleton: public JObject
单件模板类,对于进程共享的全局核心对象进行单件化,提供访问接口。单件类通常包括日志类、静态内存管理类、路由类、定时器类、后台任务类、SipUa类、线程管理类等。
JSocket.cpp/h:
virtual class JSocket: public JObject
Socket基类,定义了数据收发接口。
class JUdpSocket: public JSocket
UDP Socket类,实现了UDP数据的收发和维护。
class JTcpSocket: public JSocket
TCP Socket类,实现了TCP数据的收发和维护。
class JCommEngine: public JObject
通信实体类,能够进行消息和事件的收发。对于事件,通过路由表对象查找路由,如果是进程内,就将事件发送到线程对象相应模块的事件队列中去,如果是进程间,就用UDP Socket发送序列化的事件消息,接收事件时再反序列化为事件对象。对于消息,直接通过UDP Socket发送接收消息。
class JCommConnector: public JObject
通信连接器类,用于TCP模式Socket客户端,连接对端服务器,如果成功就返回一个通信实体类进行后续的数据收发。
class JCommAcceptor: public JObject
通信接收器类,用于TCP模式Socket服务器端,监听本地端口,如果发现客户端的连接成功就返回一个通信实体类进行后续的数据收发。
class JCommEngineGroup: public JObject
通信实体组类,维护一组通信实体对象,统一探测他们的数据收发事件。
JString.cpp/h:
class JString: public JObject
字符串类,提供对字符串的各类操作接口。
JThread.cpp/h:
virtual class JThread: public JObject
线程基类,定义了线程的事件处理、运行等接口。
class JModuleThread: public JThread
模块线程类,是组件框架的各模块的运行载体。这种线程内包含多个模块,各模块有独立事件队列来接收处理事件,模块有可配的优先级参数来决定运行策略,各模块共享一个通信实体对象与其他进程通信。
class JAgentThread: public JThread
代理线程类,代表不属于组件框架的外部线程与组件框架进行通信。这种线程不包含内部模块,通过事件队列来接收处理事件,以及通过通信实体对象与其他进程通信。
class JThreadManager: public JObject
线程管理类,管理各种类型的线程,线程类型和数量可以动态注册,创建实际线程来运行管理的各个线程,线程间采用异步模式运行。
JTimer.cpp/h:
class JSysTime: public JEventBody
系统时间类,维护系统时间及提供相应的操作接口,用于事件通信。
class JTimer: public JModule
定时器类,只使用一个操作系统定时器资源,通过本身来管理注册的多个应用定时器。
JSipUa.cpp/h:
class JPjSipUaCfg: public JEventBody
SipUa配置类,包含了SipUa类的配置,用于事件通信。
class JPjSipUaKey: public JEventBody
SipUa按键类,封装了界面的按键对象,用于事件通信。
class JPjSipUaClickContact: publicJEventBody
SipUa按键类,封装了界面的点击拨号对象,用于事件通信。
class JPjSipUaCallStatus: public JEventBody
SipUa按键类,封装了界面的呼叫状态信息对象,用于事件通信。
class JPjSipUaContactList: publicJEventBody
SipUa通话列表类,封装了界面的最近通话号码列表对象,用于事件通信。
class JPjSipCallback: public JEventBody
SipUa回调函数类,在SIP协议栈线程的回调函数发送事件给SipUa线程时使用,用于事件通信。
class JPjSip: public JObject
Sip类,对PjSIP协议栈提供的用户接口进行了封装,提供更易用的C++接口,采用外观模式。
class JPjSipUa: public JModule
SipUa类,负责SIP相关的所有操作,维护配置和内存数据,并调用Sip类完成SIP协议相关处理。
JLogSrv.cpp/h:
class JLogSrvCfg: public JEventBody
日志服务器配置类,包含了日志服务器类的配置,用于事件通信。
class JLogSrvNumber: public JEventBody
日志服务器序号类,封装了日志服务器序号对象,用于事件通信。
class JLogSrvHasNewMsg: public JEventBody
日志服务器信息提示类,封装了日志服务器消息提示对象,用于事件通信。
class JLogSrv: public JObject
日志服务器类,负责日志服务器相关的所有操作,维护配置和内存数据。日志服务器线程类和日志消息线程类操纵这个类完成实际的处理。
class JLogSrvThread: public JThread
日志服务器线程类,负责日志服务器事件的处理,为避免与日志消息收发的干扰采用独立线程。
class JLogMsgThread: public JThread
日志服务器消息类,为每个日志连接建立独立线程来完成日志信息的收发。
JLogSrvAgent.cpp/h:
class JLogServerAgent: public JObject
日志服务器代理类,代表日志服务器界面线程与组件框架线程进行通信,设置获取相应的信息。
JPhoneAgent.cpp/h:
class JPhoneAgent: public JObject
软电话代理类,代表软电话界面线程与组件框架线程进行通信,设置获取相应的信息。