田文武
伴隨著Internet技術在全球范圍內的日益普及,諸多基于互聯網架構的新業務正如雨后春筍般涌現,VoIP(Voice over IP)就是一種典型的互聯網新興技術。和傳統的電路交換通信技術相比,VoIP技術以IP數據包的形式,傳輸通信過程中,產生的所有信令與實時音視頻數據,從而可以實現更為低廉的通訊成本。同時,由于基于開放的IP網絡架構,結合當前最新的處理器芯片和嵌入式操作系統技術,VoIP終端可以實現許多傳統電話無法提供的新應用,如傳真、視頻、數據等,最終為用戶提供更為便利的通信服務。本文所介紹在線電話簿、實時天氣預報功能,就是對VoIP終端產品功能擴展的一次積極嘗試。

圖1 HTTP客戶端分層設計框圖
首先,該功能模塊是基于ARM-LINUX嵌入式平臺開發的,在系統中處于中心地位的是一個獨立的VoIP進程,它包含了VoIP終端主要的功能實現。
HTTP客戶端模塊包含:HTTP客戶端應用接口層,它可以被VoIP進程中各線程共享調用;HTTP客戶端線程,它可以滿足VoIP進程中,其他線程實時并發地訪問遠端HTTP服務器的需要;客戶端API的語法編解碼模塊,它服務于HTTP客戶端API,使其能夠對HTTP報文、XML/XHTML數據等進行高效的解析與構建。HTTP客戶端的功能設計框圖,如圖1所示:
圖1中,MMI(Man-Machine Interface)線程主要負責用戶與VoIP終端的人機交互任務。通過MMI線程,用戶訪問遠端服務器數據的場景有兩種:一種是手動訪問的方式;另一種是自動訪問的方式。對于手動方式,MMI線程可以直接調用HTTP客戶端API函數進行訪問;但這種方式存在如何有效縮短手動訪問時延的問題。另外一種自動訪問的方式,是通過獨立的HTTP客戶端線程來實現的;該方式面臨著另一個問題:如何實現MMI線程與HTTP客戶端線程之間的數據同步問題。上述兩個問題的具體分析與解決,均將在第三節中進行闡述。每次對遠端服務器數據的訪問,無論以手動方式還是自動方式,均遵循相同的基本流程,如圖2所示:

圖2 通過HTTP客戶端API訪問遠端服務器流程圖
上層模塊(MMI線程或HTTP客戶端線程等)傳遞相關的參數給HTTP客戶端API層;
API層調用相應的報文編碼函數根據上層傳遞的參數構建出請求報文;
調用Linux套接字API建立與遠端HTTP服務器的TCP連接;
一旦連接建立成功,發送請求報文,同時等待接收響應報文;
當成功接收到響應報文,HTTP客戶端API層將調用相應的報文解析函數對報文進行解析;
將解析得到的數據返回給上層模塊,并關閉TCP連接。
如果連接建立失敗、響應超時或失敗,同樣要將錯誤的結果返回給上層。
圖2描述了HTTP客戶端API層訪問遠端服務器的過程。調用socket()創建一個socket文件描述符,其內核屬性默認為阻塞模式。當建立連接或接收響應數據時,如果直接調用connect()或recv()對創建的socket描述符進行操作,調用結果返回之前,當前線程會被掛起,該調用方式成為“阻塞調用”。在“阻塞調用”模式下,如果遠端服務器不存在或服務器端不返回數據,connect()/recv()函數就會長時間無法返回,進行該調用的線程將被長時間掛起,而這對于用戶而言顯然是無法接受的。如何解決該問題?
Unix/Linux中,系統調用select()經常在阻塞調用的非阻塞化場合被用到。其函數聲明如下:


select()函數提供了一個fd_set的數據結構,它是一個長整型的數組,每一個數組元素都能與一個打開的文件描述符(如socket描述符)相關聯。當調用select()時,由內核根據I/O狀態修改fd_set的內容,由此來通知執行了select()的進程哪一個socket或文件可讀寫。同時select()還提供了一個timeval結構,在調用時,如果超過了timeval參數所指定的時間長度仍然沒有文件描述符滿足條件,select()就會直接返回。通過timeval參數可以定制符合用戶要求的最大等待時間(建立連接、接收響應數據等)。
非阻塞連接建立過程如下:
A. 建立socket,將socket文件描述符設為非阻塞模式;
B. 調用connect()函數,建立與socket綁定的連接(該調
用會立刻返回);
C. 調用select()檢查socket是否可讀寫;
D. 根據select()返回值判斷connect()的結果:成功、超時或失敗;
E. 將socket重新設置為阻塞模式。
區別于非阻塞連接過程,由于網絡數據傳輸本身的時延效應,需要在不改變socket描述符阻塞屬性的情況下,通過動態輪詢的方式來實現非阻塞的數據接收過程,如圖3所示:

圖3 非阻塞數據接收過程
N表示最大輪詢次數,以SELECT_T 代表Select()調用的超時時間,則整個數據接收過程的時間延遲最大為

實際測試表明,通過select()函數實現的非阻塞網絡通信機制,可以實現通信過程中實時性和可靠性的有效平衡。
VoIP進程的每個線程均是基于一種消息或事件驅動的自循環模型設計的。
這種由消息或事件驅動的自循環線程模型,如圖4所示:

圖4 消息/事件驅動的自循環線程模型
在每次循環過程中,線程檢查是否有新的消息傳入或事件發生;若發現新的消息或事件,則進行相應的處理,完成后進入下次循環過程,若當前沒有新的消息或事件,則繼續等待消息/事件的到來,直到定時器超時進入下次循環。
HTTP客戶端線程的特殊之處在于,它一直等待請求消息而不會超時,一旦受到請求消息就立即處理,并返回響應消息給外部請求發送者,然后進入下一個等待周期。
當MMI線程需要自動訪問遠端服務器時,直接向HTTP客戶端消息隊列發送一個請求;處于“等待”狀態的HTTP客戶端線程收到MMI線程的請求消息,會立即提取消息中的服務器地址和HTTP請求相關的參數,然后按照圖2所示的流程通過HTTP客戶端API層與遠端服務器進行交互;當收到HTTP客戶端API層的返回數據時,HTTP客戶端線程就會將這些數據以一定的組織形式,發送到MMI線程的消息隊列,然后重新進入侍候狀態;當MMI線程收到來自HTTP客戶端線程的消息時,該自動訪問過程結束。
3.1.1 手動搜索功能
該功能通過MMI線程直接調用HTTP客戶端API實現,基本流程為:用戶輸入搜索條件;MMI線程調用HTTP客戶端API向遠端服務器發送HTTP搜索請求;如果搜索返回成功,則將搜索結果提供給用戶查看,否則,提示用戶錯誤信息。
3.1.2 自動查詢功能
該功能通過圖5所示的MMI與HTTP客戶端線程的同步機制實現,基本流程,如圖5所示:
在線天氣預報屬于MMI線程中的常駐功能,一旦MMI線程啟動,就會直接觸發它的運行.
檢查在線天氣功能是否開啟,如未開啟,則重置計時器,等待其超時再次檢測功能是否開啟,否則進入下一步;
向HTTP客戶端線程發送信息查詢請求;如客戶端線程成功返回響應消息,則進入下一步,否則重新進行上一步;
將HTTP客戶端線程返回的天氣信息顯示在LCD屏上;
重置定時器;
等待定時器超時,返回第一步再次更新天氣信息。
本文針對當前VoIP終端特色功能擴展的需要,設計了一種輕型、高效的HTTP客戶端,并對該客戶端的設計架構和實現中遇到的關鍵問題進行了闡述。由于采用了相對開放的架構設計,實現了相互獨立的HTTP客戶端接口層、HTTP客戶端線程以及語法編解碼模塊,因此,除了已實現的在線號碼簿和在線天氣信息外,還可以基于該客戶端,進行其他類似的功能擴展。實際測試表明,該客戶端具有實時性高、穩定性強、系統資源開銷少等特點,達到了預期的設計指標。
[1]Wikipedia: Voice over IP[G/OL], http://en.wikipedia.org/wiki/Voice_over_IP, 2011.
[2]Neil Matthew, Richard Stones. Beginning Linux Progrmaming 4th Edition[M]. 北京,人民郵電出版社 ,2010.
[3]宋敬彬, 孫海濱. Linux網絡編程,[M]北京,清華大學出版社, 2010.
[4]謝希仁. 計算機網絡(第5版)[M].北京電子工業出版社,2008.
[5]許信順. 嵌入式 Linux應用編程[M], 北京,機械工業出版社, 2007.
[6]Internet Engineering Task Force, Hypertext Transfer Protocol - HTTP/1.1[S], http://tools.ietf.org/html/rfc2616,1999.