最開始做這個項目的時候,是由於發現了一個叫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
軟電話代理類,代表軟電話界面線程與組件框架線程進行通信,設置獲取相應的信息。