前言:一篇好文章的誕生,需要你不斷地搜集資料、整理思路,本站小編為你收集了豐富的socket通信主題范文,僅供參考,歡迎閱讀并收藏。
關(guān)鍵詞:網(wǎng)絡(luò)通信;Tcp/IP;socket
中圖分類號(hào):TP393 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2013)35-8116-02
1 C/S中TCP/IP與Socket
1.1 TCP/IP協(xié)議的優(yōu)點(diǎn)
每種網(wǎng)絡(luò)協(xié)議都有自己的優(yōu)點(diǎn),但是只有TCP/IP允許與Internet完全的連接。TCP/IP是在60年代由麻省理工學(xué)院開(kāi)發(fā)的,即便網(wǎng)絡(luò)遭到了大部分破壞,TCP/IP仍然能夠維持有效的通信。在所有的OS中都集成了TCP/IP協(xié)議,TCP/IP是實(shí)現(xiàn)網(wǎng)際互聯(lián)的基礎(chǔ),同時(shí)TCP/IP協(xié)議也具備了可擴(kuò)展性和可靠性的需求。在現(xiàn)今的網(wǎng)絡(luò)環(huán)境下,能有效地保證點(diǎn)對(duì)點(diǎn)信息的傳輸和安全是網(wǎng)絡(luò)應(yīng)用的需要。
1.2 Socket的作用
Socket通常稱作"套接字",應(yīng)用程序通常通過(guò)"套接字"向網(wǎng)絡(luò)發(fā)出請(qǐng)求或應(yīng)答網(wǎng)絡(luò)請(qǐng)求,Socket是面向C/S模型而設(shè)計(jì)的,針對(duì)客戶和服務(wù)器程序提供不同的socket系統(tǒng)調(diào)用??蛻綦S機(jī)申請(qǐng)一個(gè)socket (相當(dāng)于在電話系統(tǒng)中一個(gè)想打電話的人可以在任何一臺(tái)入網(wǎng)電話上撥號(hào)呼叫),系統(tǒng)為任何有聯(lián)網(wǎng)需求及信息需求的客戶分配一個(gè)socket號(hào);服務(wù)器擁有全局socket號(hào) ,客戶端socket號(hào)相當(dāng)于電話系統(tǒng)中的各個(gè)公共電話,而服務(wù)器端相當(dāng)于總機(jī)電話。
1.3 Socket實(shí)現(xiàn)網(wǎng)絡(luò)通信的基本原理
TCP/IP系統(tǒng)中的端口號(hào)是一個(gè)16位的數(shù)字,它的范圍是0~65535??蛻艉头?wù)器必須事先約定所使用的端口。在C/S開(kāi)發(fā)模式中,網(wǎng)絡(luò)中的各個(gè)節(jié)點(diǎn)都有獨(dú)有的IP地址用來(lái)表示各節(jié)點(diǎn)的身份,服務(wù)器預(yù)留端口作為鏈接的接口,而Socket通過(guò)在它們之間兩兩建立鏈路的方式,來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)中點(diǎn)對(duì)點(diǎn)的通信。
Socket分為客戶端Socket對(duì)象及ServerSocket類對(duì)象,它們類庫(kù)位于包中。ServerSocket用于服務(wù)器端,Socket是建立網(wǎng)絡(luò)連接時(shí)使用的。在連接成功時(shí),應(yīng)用程序兩端都會(huì)產(chǎn)生一個(gè)Socket實(shí)例,通過(guò)這個(gè)實(shí)例,完成所需的會(huì)話。對(duì)于一個(gè)網(wǎng)絡(luò)連接來(lái)說(shuō),套接字是平等的,并沒(méi)有差別,不因?yàn)樵诜?wù)器端或在客戶端而產(chǎn)生不同級(jí)別。不管是Socket還是ServerSocket它們的工作都是通過(guò)Java語(yǔ)言提供的SocketImpl接口及其方法來(lái)完成網(wǎng)絡(luò)編程所需的網(wǎng)絡(luò)通信功能。 這套API使Java程序員不用考慮復(fù)雜的網(wǎng)絡(luò)協(xié)議以及底層的數(shù)據(jù)傳輸方式,而直接用面向?qū)ο蟮乃枷雭?lái)實(shí)現(xiàn)網(wǎng)絡(luò)傳輸。
2 建立C/S基于TCP/IP Socket通信模型
2.1 客戶端Socket通信過(guò)程
Socket是網(wǎng)絡(luò)上運(yùn)行的兩個(gè)程序間雙向通信的一端,它既可以接受請(qǐng)求,也可以發(fā)送請(qǐng)求,利用它可以較為方便地編寫(xiě)網(wǎng)絡(luò)上數(shù)據(jù)的傳遞。在Java中,利用Socket類的方法,就可以實(shí)現(xiàn)兩臺(tái)計(jì)算機(jī)之間的通信??蛻舳薙ocket通信整個(gè)過(guò)程主要有4個(gè)階段:(1)請(qǐng)求階段;(2)建立連接階段;(3)通信階段;(4)關(guān)閉階段。
2.2 TCP Socket C/S模型及工作流程
TCP/IP本身是一個(gè)工業(yè)標(biāo)準(zhǔn),在C/S模式中,主要通過(guò)三次握手,實(shí)現(xiàn)通信:
1):主機(jī)A發(fā)送位碼為syn=1,隨機(jī)產(chǎn)生seq number=1234567的數(shù)據(jù)包到服務(wù)器,主機(jī)B由SYN=1知道,A要求建立聯(lián)機(jī);
2):主機(jī)B收到請(qǐng)求后要確認(rèn)聯(lián)機(jī)信息,向A發(fā)送ack number=(主機(jī)A的seq+1),syn=1,ack=1,隨機(jī)產(chǎn)生seq=7654321的包;
3):主機(jī)A收到后檢查ack number是否正確,即第一次發(fā)送的seq number+1,以及位碼ack是否為1,若正確,主機(jī)A會(huì)再發(fā)送ack number=(主機(jī)B的seq+1),ack=1,主機(jī)B收到后確認(rèn)seq值與ack=1則連接建立成功;
圖1 C/S程序工作流程圖
3 程序?qū)崿F(xiàn)
3.1 C/S模型下基本程序框架
3.1.1 客戶端Socket的實(shí)現(xiàn)
客戶端通過(guò)Socket連接服務(wù)器,主要過(guò)程經(jīng)過(guò)4個(gè)階段,具體代碼如下:
Socket PersonalConnect=new Socket(“服務(wù)器IP”,PORT);
……;//socket、bufferedReader及PrintStream對(duì)象作為線程成員
PersonalConnect.close();
3.1.2 服務(wù)器端ServerSocket的實(shí)現(xiàn)
ServerSocket myTcpServer=new ServerSocket(PORT);//設(shè)定服務(wù)器端口
while(true){//實(shí)現(xiàn)多客戶鏈接
Socket c_s=myTcpServer.accept();
……;//socket、bufferedReader及PrintStream對(duì)象作為線程成員
c_s.close();}
對(duì)于TCP C/S模式中兩端的Java類中,主要有三個(gè)類成員它們分別是Socket對(duì)象、BufferedReader對(duì)象、PrintStream對(duì)象,而服務(wù)器端還有個(gè)ServerSocket對(duì)象。其中Socket及ServerSocket對(duì)象完成連接兩端的請(qǐng)求綁定套接的過(guò)程,而輸入輸出流在套接中進(jìn)行通信。如果一端要表達(dá)消息發(fā)送結(jié)束,則可以關(guān)閉其輸出流,但并不關(guān)閉套接字對(duì)象,這就是“半關(guān)閉”的作用。
3.2 通過(guò)線程管理C/S中的通信
3.2.1 客戶端ClientThread的實(shí)現(xiàn)
ClientThread.java的主要代碼:
public class ClientThread implements Runnable{
……;
public ClientThread(Socket s) throws IOException{
this.personalConnect=s;
……;}
public void run() {……}
}
3.2.2服務(wù)器端ServerThread的實(shí)現(xiàn)
Tcp_Server.java文件的主要代碼:
public static ArrayList socketList = new ArrayList();
//用來(lái)保存客戶端Socket對(duì)象
ServerSocket myTcpServer=new ServerSocket(“服務(wù)器端口“);
Socket c_s = myTcpServer.accept();
socketList.add(c_s);//響應(yīng)客戶端Socket請(qǐng)求,并保存Socket對(duì)象進(jìn)入socketList
new Thread(new ServerThread(c_s)).start(); ServerThread.java文件的主要代碼:
public class ServerThread implements Runnable
{……;//socket、bufferedReader及PrintStream對(duì)象作為線程屬性
public ServerThread(Socket s) throws IOException {
this.c_s = s; }}
利用線程在一個(gè)List結(jié)構(gòu)中實(shí)現(xiàn)多個(gè)不同的Socket對(duì)象的管理即每一個(gè)客戶端通過(guò)線程實(shí)現(xiàn)套接字對(duì)象的管理與通信。Accept()方法用于產(chǎn)生”阻塞”,直到接受到一個(gè)連接,并且返回一個(gè)客戶端的Socket對(duì)象實(shí)例。”阻塞”是一個(gè)術(shù)語(yǔ),它使程序運(yùn)行暫時(shí)”停留”在這個(gè)地方,直到一個(gè)會(huì)話產(chǎn)生,然后程序繼續(xù)。
為了更好地實(shí)現(xiàn)對(duì)于客戶端對(duì)象的管理,實(shí)現(xiàn)對(duì)于其Socket與其消息的管理,我們也可以通過(guò)HaspMap對(duì)象,來(lái)實(shí)現(xiàn)單一客戶K與其多條消息V的關(guān)系映射。實(shí)現(xiàn)“Kclient+Vserver“的形式傳送消息即“用戶名:消息類容”的形式輸入,服務(wù)器端以HaspMap解析讀入的消息對(duì)象,辨識(shí)消息的發(fā)送者與其消息之間的映射關(guān)系,實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)的消息傳輸。
4 結(jié)束語(yǔ)
本文主要介紹的內(nèi)容僅適合于TCP/IP網(wǎng)絡(luò)協(xié)議。Java Socket 可以實(shí)現(xiàn)TCP協(xié)議在兩臺(tái)計(jì)算機(jī)之間建立可靠連接。連接安全可靠,數(shù)據(jù)不會(huì)丟失,Java平臺(tái)還提供了更安全的SSLSocket類,SSLSocket通信是對(duì)SOCKET通信的拓展。在Socket基礎(chǔ)上添加了一層安全性保護(hù),提供了更高的安全性,包括身份驗(yàn)證、數(shù)據(jù)加密以及完整性驗(yàn)證。其中身份驗(yàn)證用于數(shù)字證書(shū)的發(fā)放和應(yīng)用。數(shù)據(jù)加密可以通過(guò)密鑰防止消息傳遞過(guò)程中被別人監(jiān)聽(tīng)而造成的損失。所以Socket編程在網(wǎng)絡(luò)通信中得到了廣泛的應(yīng)用。
參考文獻(xiàn):
【 關(guān)鍵詞 】 Socket通訊;數(shù)據(jù)交互;delphi
USing Socket Communication Technology to Realize Data Security Interaction between
the Systems of Huaian Accumulation Fund Center and Registration Trading Center
Wei Juan 1 Zhao Chun-mei 2
【 Abstract 】 With the development of the electronic administrative affairs, different application systems cause certain difficulties while being developed in the aspects of development environment, system structure and data base, etc. This paper adopts socket communication mode to realize data interaction between the systems of Huaian Accumulation Fund Center and Registration Trading Center. The log processing and image processing specially added also provides a good reference to the interaction of other systems.
【 Keywords 】 socket communication; data interaction; delphi
1 系統(tǒng)間的交互方式
加強(qiáng)淮安市公積金中與淮安市房屋交易登記中心數(shù)據(jù)共享,為社會(huì)公眾及自身提供一體化的高效、優(yōu)質(zhì)、廉潔的管理和服務(wù)的過(guò)程實(shí)現(xiàn)住房公積金貸款“一窗辦結(jié)”、“一次告知,集中收件”的受理原則,“一次審核、全程通用” 的審查原則?;窗彩泄e金中心與淮安市登記中心系統(tǒng)要實(shí)現(xiàn)系統(tǒng)數(shù)據(jù)的共享,但系統(tǒng)架構(gòu)、數(shù)據(jù)庫(kù)不一樣,給數(shù)據(jù)交互帶來(lái)一定難度。
常用的數(shù)據(jù)交互技術(shù)有Socket和WebSevice。
(1)Socket交互,首先建立Socket數(shù)據(jù)交互包,即兩個(gè)系統(tǒng)間建立傳遞數(shù)據(jù)的數(shù)據(jù)包,接受到數(shù)據(jù)包的一端根據(jù)制定的規(guī)則完成交互。該方式優(yōu)點(diǎn)是點(diǎn)對(duì)點(diǎn)之間的數(shù)據(jù)交互安全性高。
(2)WebSevice交互以WebSevice 的方式實(shí)現(xiàn)服務(wù)器間的交互,系統(tǒng)間互為客戶端向服務(wù)器端發(fā)送請(qǐng)求實(shí)現(xiàn)交互。該方式的優(yōu)點(diǎn)是實(shí)時(shí)交互效率高;缺點(diǎn)是由于服務(wù)器端作為客戶端,有可能出現(xiàn)安全問(wèn)題。公積金中心采用C/S架構(gòu)、Oracle數(shù)據(jù)庫(kù),登記中心是C/S架構(gòu)\SQL Server數(shù)據(jù)庫(kù),雙方業(yè)務(wù)系統(tǒng)均在內(nèi)網(wǎng),且對(duì)安全性都有很高的要求,不適合直接互聯(lián),雙方均租用電信專線實(shí)現(xiàn)網(wǎng)絡(luò)互聯(lián)、防火墻等設(shè)備保障系統(tǒng)安全。所有采用了Socket方式實(shí)現(xiàn)數(shù)據(jù)交互。
Socket是建立在傳輸層協(xié)議上的一種套接字規(guī)范,它定義兩臺(tái)計(jì)算機(jī)間進(jìn)行通信的規(guī)范,套接字屏蔽了底層通信軟件和具體操作系統(tǒng)的差異,使得任何兩臺(tái)安裝了TCP協(xié)議軟件和實(shí)現(xiàn)了套接字規(guī)范的計(jì)算機(jī)之間的通信成為可能。Socket通常用來(lái)實(shí)現(xiàn)客戶方和服務(wù)方的連接。客戶程序可以向Socket寫(xiě)請(qǐng)求,服務(wù)器將處理此請(qǐng)求,然后通過(guò)Socket將結(jié)果返回給用戶。
2 制定數(shù)據(jù)交換規(guī)范,統(tǒng)一數(shù)據(jù)交換格式
由于公積金系統(tǒng)和登記中心系統(tǒng)的是在不同的數(shù)據(jù)庫(kù)開(kāi)發(fā)的軟件,我們首先要做的事情是,將數(shù)據(jù)的數(shù)據(jù)映射成為統(tǒng)一的數(shù)據(jù)格式,再由統(tǒng)一的數(shù)據(jù)格式映射成為本系統(tǒng)的數(shù)據(jù)格式。
對(duì)于數(shù)據(jù)交互格式我們制定了兩種交互方式。
(1)對(duì)數(shù)據(jù)統(tǒng)一編碼,對(duì)每個(gè)交互內(nèi)容確定數(shù)據(jù)項(xiàng)名稱、類型、長(zhǎng)度、位置、備注。發(fā)送的信息組合為一個(gè)長(zhǎng)字符串。接受方通過(guò)對(duì)字符串的解碼,提取相應(yīng)的信息,這種模式比較適合交互內(nèi)容較少的信息。
(2)內(nèi)容較多時(shí),可以通過(guò)XML進(jìn)行組織數(shù)據(jù)。
3 基于Delphi的Socket交互的設(shè)計(jì)
在Delphi中,對(duì)Windows Socket進(jìn)行了有效的封裝,使得可以很方便地實(shí)現(xiàn)通信。在Delphi環(huán)境下實(shí)現(xiàn)Socket技術(shù)通訊,服務(wù)器的流程為,首先建立服務(wù)器端的的Socket,當(dāng)檢測(cè)到來(lái)自客戶端的連接請(qǐng)求時(shí),向客戶端發(fā)送收到連接請(qǐng)求的信息,并建立與客戶端之間的連接。當(dāng)完成通信后,服務(wù)器斷開(kāi)與客戶端的Socket連接??蛻舳说牟襟E如下。建立客戶端的Socket,確定要連接的服務(wù)器的主機(jī)名和端口。發(fā)送連接請(qǐng)求到服務(wù)器,并等待服務(wù)器的回饋信息。連接成功后,與服務(wù)器進(jìn)行數(shù)據(jù)的交互。數(shù)據(jù)處理完畢后,關(guān)閉自身的Socket連接,如圖1所示。
3.1 客戶端軟件開(kāi)發(fā)
客戶端軟件主要實(shí)現(xiàn)的功能是向與服務(wù)器端建立Socket連接,發(fā)送消息,并接受返回信息,結(jié)束后斷開(kāi)Socket連接。主要通過(guò)ClientSocket實(shí)現(xiàn)。
一、Java多線程簡(jiǎn)介
將多線程機(jī)制蘊(yùn)含在語(yǔ)言中,是Java的一個(gè)重要特征。所謂線程,是指程序中的一個(gè)執(zhí)行流。在一個(gè)進(jìn)程中,可以有多個(gè)線程。這些線程在操作系統(tǒng)的調(diào)度下并發(fā)執(zhí)行,使得每個(gè)線程都好像在獨(dú)占整個(gè)系統(tǒng)資源。而有了多線程這個(gè)特性,JAVA可以支持多個(gè)程序并發(fā)執(zhí)行。利用Java的多線程編程接口,開(kāi)發(fā)人員可以方便地寫(xiě)出支持多線程的應(yīng)用程序,有效地減少并發(fā)并行程序設(shè)計(jì)的困難,提供程序執(zhí)行效率。
1.線程的創(chuàng)建
有兩種方法可以創(chuàng)建線程。第一種方法是通過(guò)繼承類Thread來(lái)創(chuàng)建線程類。子類重載其mn0方法。實(shí)現(xiàn)方法如下:
classThieadNameextendsThread{
publicvoidrun(){//run是整個(gè)線程類代碼的
入口
…//需要以線程方式運(yùn)行的代碼
}}
第二種方法是建立一個(gè)具有Runnable接口的類。由于Java不支持多繼承性,如果需要類似線程方式運(yùn)行且繼承其他的類,就必須實(shí)現(xiàn)Runnable接口。Runnable接口只有一個(gè)方法run()。在類中實(shí)現(xiàn)此接口的方法如下:
classThieadNameextendsAppletimplementsRunnable{
publicvoidrun(){
…//需要以線程方式運(yùn)行的代碼
}}
2.線程的調(diào)用
如果采用第一種方法,創(chuàng)建的線程類的調(diào)用格式如下:ThreadNametest=newThreadName();//test是線程類
ThreadName的一個(gè)實(shí)例test.start();
start()是線程類的成員函數(shù),用于啟動(dòng)該線程,該線程將自動(dòng)調(diào)用run()方法。
如果采用第二種方法,創(chuàng)建的線程類的調(diào)用格式如下:
ThreadNametest=newThreadName();
Threadth=newThiead(test);/組過(guò)Thread創(chuàng)建個(gè)新的線程
th.start()
3.線程的同步及通信
系統(tǒng)中存在多個(gè)線程時(shí),就需要保證線程的同步及相互通信,以期協(xié)調(diào)工作,避免發(fā)生死鎖。Java提供了二個(gè)標(biāo)準(zhǔn)的Object類方法wait(),notify()和notifyAll(),以及二個(gè)Thread類方法sleep()suspend()和resume(),用于中斷或喚醒線程的執(zhí)行。當(dāng)線程調(diào)用sleep(),wait()或suspend()方法之后線程就會(huì)由可運(yùn)行狀態(tài)進(jìn)入阻塞狀態(tài)(blocked),一旦線程睡眠時(shí)間到或者是其他線程調(diào)用了notify()或resume()方法后,此線程才會(huì)由阻塞狀態(tài)進(jìn)入可運(yùn)行狀態(tài)中,然而一個(gè)線程是否最終占有CPU,取決于系統(tǒng)的調(diào)度策略。Java1.0在solaris版本中實(shí)現(xiàn)的“綠色線程”的調(diào)用策略是讓一個(gè)線程持續(xù)處于執(zhí)行狀態(tài)直到有一個(gè)更高優(yōu)先級(jí)的線程將之打斷,而Windows95和WindowsNT是給每一個(gè)處于可執(zhí)行狀態(tài)的線程分配一個(gè)時(shí)間片,當(dāng)時(shí)間片用完時(shí)系統(tǒng)會(huì)調(diào)用另一個(gè)線程投入運(yùn)行。
二、網(wǎng)間網(wǎng)的Socket通訊機(jī)制
TCP/IP技術(shù)的核心部分是傳輸層(TCP和UDP協(xié)議)、網(wǎng)絡(luò)層(P協(xié)議)和物理層(面向各種物理硬件技術(shù)),能實(shí)現(xiàn)這三層協(xié)議的內(nèi)核可稱之為tcp/ip網(wǎng)絡(luò)操作系統(tǒng)。tcp/ip協(xié)議技術(shù)中的中下層協(xié)議向外提供的只是原始的編程界面,而不是直接的用戶服務(wù),用戶服務(wù)要靠核外的應(yīng)用程序?qū)崿F(xiàn)。應(yīng)用程序和tcp/ip核心協(xié)議關(guān)系如圖2所示。
即網(wǎng)間網(wǎng)應(yīng)用程序能夠直接操作的是TCP/P核心協(xié)議提供的編程界面。由于網(wǎng)絡(luò)中資源、運(yùn)算能力和信息的差異,同時(shí)又由于網(wǎng)間網(wǎng)通信完全是異步的,因此,在基于TCP/P協(xié)議的網(wǎng)間網(wǎng)中,最主要的進(jìn)程間相互作用模型是客戶/服務(wù)器(client/server)模型。客戶和服務(wù)器分別是兩個(gè)應(yīng)用程序(進(jìn)程),客戶向服務(wù)器發(fā)出服務(wù)請(qǐng)求,服務(wù)器作出響應(yīng)。
網(wǎng)間網(wǎng)進(jìn)程通信的關(guān)鍵是要解決進(jìn)程的標(biāo)識(shí)和多傳輸協(xié)議的標(biāo)識(shí)問(wèn)題以及進(jìn)程間相互作用的模式。在網(wǎng)間網(wǎng)中,全局惟一的標(biāo)識(shí)一個(gè)進(jìn)程需要一個(gè)三兀組表示,即用半相關(guān)(half-association)來(lái)描述一個(gè)Socket:
{協(xié)議,本地地址,本地端口號(hào)}
而一個(gè)完整的網(wǎng)間網(wǎng)進(jìn)程通信需要由兩個(gè)進(jìn)程組成(兩個(gè)端進(jìn)程),因此一個(gè)完整的網(wǎng)間網(wǎng)進(jìn)程通信必須用一個(gè)五元組表示,即用相關(guān)(association)來(lái)描述一■個(gè)完整的Socket:{協(xié)議,本地地址,本地端口號(hào),遠(yuǎn)地地址,遠(yuǎn)地端口號(hào)}其中,一個(gè)確定的網(wǎng)間網(wǎng)進(jìn)程通信只能使用同一個(gè)內(nèi)核的高層協(xié)議,不可能通信的一端用TCP而另一端用UDP協(xié)議。故而,兩個(gè)協(xié)議相同的半相關(guān)才能組成一個(gè)合適的相關(guān),兩個(gè)三元組組合起來(lái)是一個(gè)五兀組而非六兀組。每一個(gè)Socket有一個(gè)本地惟一的Socket號(hào),由操作系統(tǒng)分配。一個(gè)本地Socket號(hào),完整地描述了本地進(jìn)程以及與之通信的遠(yuǎn)地進(jìn)程,因此Socket的語(yǔ)義具有網(wǎng)絡(luò)一致性,準(zhǔn)確地描述了網(wǎng)絡(luò)進(jìn)程。因此,Socket的關(guān)鍵是建立客戶和服務(wù)器之間的相關(guān)。
Socket編程界面最早由4BSDUNIX系統(tǒng)提出,其主要目的是解決網(wǎng)間網(wǎng)進(jìn)程通信(IPC)問(wèn)題。故而,Socket系統(tǒng)調(diào)用與UNIX的文件訪問(wèn)有許多類似之處,是對(duì)UNIX輸入輸出的擴(kuò)充。Socket是面向客戶/服務(wù)器模型設(shè)計(jì)的,針對(duì)客戶和服務(wù)器程序提供了不同的Socket系統(tǒng)調(diào)用。服務(wù)器擁有全局公認(rèn)的半相關(guān)Socket,這就保證了任何客戶都可以在網(wǎng)絡(luò)的任何地方隨機(jī)向它發(fā)出聯(lián)接請(qǐng)求和信息請(qǐng)求。Socket的數(shù)據(jù)信息是原始字節(jié)流的形式,通信雙方要在此基礎(chǔ)上進(jìn)行約定的數(shù)據(jù)格式化和解釋等處理(即相同的協(xié)議),然后才能進(jìn)行進(jìn)一步的具體應(yīng)用操作,這也是實(shí)現(xiàn)某種協(xié)議的過(guò)程。基于Socket的這種通信機(jī)制,在網(wǎng)關(guān)(gateway)的作用下可實(shí)現(xiàn)TCP/協(xié)議和其他低級(jí)協(xié)議,如現(xiàn)場(chǎng)總線協(xié)議CAN(ControllerAreaNetwork)協(xié)議的轉(zhuǎn)換,進(jìn)而擴(kuò)展Internet應(yīng)用領(lǐng)域,將實(shí)時(shí)控制功能帶入網(wǎng)絡(luò)。Socket通信機(jī)制提供了兩種通訊方式:有聯(lián)接和無(wú)聯(lián)接方式,分別面向不同的應(yīng)用需求。使用有聯(lián)接方式時(shí),通信鏈路提供了可靠的,全雙工的字節(jié)流服務(wù)。在該方式下,通信雙方必須創(chuàng)建一個(gè)聯(lián)接過(guò)程并建立一條通訊鏈路,以后的網(wǎng)絡(luò)通信操作完全在這一對(duì)進(jìn)程之間進(jìn)行,通信完畢關(guān)閉此聯(lián)接過(guò)程。使用無(wú)聯(lián)接方式時(shí)其系統(tǒng)開(kāi)銷比有聯(lián)接方式小,但通信鏈路提供了不可靠的數(shù)據(jù)報(bào)服務(wù),不能保證信源所傳輸?shù)臄?shù)據(jù)一定能夠到達(dá)信宿。在該方式下,通信雙方不必創(chuàng)建一個(gè)聯(lián)接過(guò)程和建立一條通訊鏈路,網(wǎng)絡(luò)通信操作在不同的主機(jī)和進(jìn)程之間轉(zhuǎn)發(fā)進(jìn)行。
面向客戶/服務(wù)器方式的Socket通信機(jī)制模型如圖3所示。
三、Java多線程在網(wǎng)絡(luò)編程中的應(yīng)用
1.Socket客戶端程序設(shè)計(jì)我們通過(guò)一個(gè)簡(jiǎn)單的應(yīng)用程序?qū)嵗齺?lái)說(shuō)明其通信程序的工作過(guò)程和編程特點(diǎn)。
?X'
importjava.io.;
X
importjava.net.;publicclassJabberServer{publicstaticfinalintPORT=8080;publicstaticvoidmain(String[]args)throwsIOException
{ServerSockets=newServerSocket(PORT)System.out.println(“Started:”s)try
{Socketsocket=s.accept();try
{System.out.println(“Connectionaccepted:”+socket)
DatalnputStreamsi=newDataInputStream(socket.getInputStream());
PrintSteamso=newPrintStream(socket.getOutputStream());while(true){Stringstr=in.readLine();if(str.equal(“END”))break;
System.out.println(“Echoing:”+str);System.out.println(str);
}finally{
System.out.println(“closing"?”);}}}
此應(yīng)用程序的功能是從輸入流中獲取從服務(wù)器方發(fā)來(lái)的數(shù)據(jù),并顯示在屏幕上。Socket類是覆蓋在一個(gè)與平臺(tái)有關(guān)的實(shí)現(xiàn)之上的,只是它把具體的系統(tǒng)細(xì)節(jié)從java程序中屏蔽了。因而在程序開(kāi)始部分引入了java.net包中的所有的Socket類?;诖?,Java程序即可實(shí)現(xiàn)與平臺(tái)的無(wú)關(guān)性。網(wǎng)絡(luò)通信的目的主要是對(duì)網(wǎng)絡(luò)資源的訪問(wèn)和操作。在建立了新的Socket對(duì)象實(shí)體s后,利用outputStream(),getTnputStream()方法建立輸出、輸入流。這樣,訪問(wèn)的網(wǎng)絡(luò)資源的過(guò)程就變成了處理流對(duì)象的過(guò)程,即以數(shù)據(jù)流中的方法讀寫(xiě)應(yīng)用程序端。數(shù)據(jù)流(Stream)可以理解成數(shù)據(jù)的通信途徑,在建立好應(yīng)用程序和資源方的通信通道后,遠(yuǎn)方的數(shù)據(jù)就可以自動(dòng)傳輸過(guò)來(lái)。在程序的結(jié)尾用close()方法關(guān)閉輸入、輸出流和Socket,這樣將釋放所占用的系統(tǒng)資源。
Java語(yǔ)言的Socket通信機(jī)制和UNIX系統(tǒng)的輸入輸出操作(open-read-write-close)相類似。其客戶端基本操作程序的編寫(xiě)概括起來(lái)包括以下四步:①打開(kāi)Socket,即創(chuàng)建一個(gè)Socket對(duì)象實(shí)體:②創(chuàng)建與此Socket聯(lián)接的輸入輸出流;③根據(jù)服務(wù)器的協(xié)議向此Socket寫(xiě)數(shù)據(jù)或從Socket讀數(shù)據(jù):④關(guān)閉輸入、輸出流和Socket。
2.Socket服務(wù)器端程序設(shè)計(jì)基于Java語(yǔ)言的Socket服務(wù)器端基本操作過(guò)程和客戶端過(guò)程相對(duì)應(yīng),其程序的編寫(xiě)也包括以下五步:①打開(kāi)SeneSocket,即創(chuàng)建一個(gè)ServerSocket對(duì)象實(shí)體在指定端口為客戶端請(qǐng)求的Socket服務(wù);②使用ServerSocket類的accept()方法接收來(lái)自客戶端的聯(lián)接請(qǐng)求;③使用新建的Socket對(duì)象創(chuàng)建輸入、輸出流對(duì)象;④通過(guò)對(duì)流對(duì)象的操作完成客戶端的處理請(qǐng)求,并將結(jié)果返回給客戶端;⑤當(dāng)客戶端和服務(wù)器工作結(jié)束時(shí),關(guān)閉輸入、輸出流,用ServerSocket類的close()方法關(guān)閉Sacket。
服務(wù)器通常分為并發(fā)服務(wù)器和重復(fù)服務(wù)器,并發(fā)服務(wù)器只接收客戶請(qǐng)求,不處理請(qǐng)求,客戶請(qǐng)求由它的fork之進(jìn)程處理和響應(yīng);而重復(fù)服務(wù)器接收、處理并響應(yīng)客戶請(qǐng)求,即工作時(shí)只能同時(shí)和一個(gè)客戶程序聯(lián)接。下面給出使用Java的多線程實(shí)現(xiàn)并發(fā)服務(wù)器通信程序。具體地實(shí)現(xiàn)并發(fā)服務(wù)器的思路是:在服務(wù)器的程序中首先創(chuàng)建單個(gè)ServerSocke^并調(diào)用accept()來(lái)等候一個(gè)新連接,然后用accept()返回的Socket新建一個(gè)線程,它只為連接的特定的客戶提供服務(wù)。接著再調(diào)用accept(),等候下一個(gè)親新的連接請(qǐng)求。
(1)服務(wù)器端主線程程序的實(shí)現(xiàn)
?..+x.importjava.io.;
x
importjava.net.;publicclassMultiJabberServer{publicstaticfinalintPORT=8080;publicstaticvoidmain(String[]args)throwsIOException
{ServeiSockets=newServerSocket(PORT);System.out.println(“ServerStarted”)try
{while(true)
{Socketsocket=s.accept()by
{newMyserverHandler(socket)
}catch(IOExceptione){socket.close()
}
}
}finally{
si.close()so.close();s.close();
}
}
(2)服務(wù)器端子線程程序的實(shí)現(xiàn)publicclassMyserverHandlerextendsThread{privateSocketsocket;
privateBufferReaderin;privatePrintWriterout;
publicMyserverHandler(Sockets)throwsIOException
{socket=s;
in=newBufferReader(newInputStreamReader(socket.getlnputStream()));
out=newPrintWriter(newBufferedWriter(newOutStreamWriter(socket.getOutputStream()))true);
start();
publicvoidiun(){
by{
while(true){
Stringstc=in.readLine()if(str.equals(“END”)break;
System.out.println(“Echoing:+str);out.pnintln(str)
}
System.out.Println(“closing"?,,);
}finally{
try{
socket.close();
}catch(IOException。){}
}
1
四、結(jié)語(yǔ)
關(guān)鍵詞:Linux;嵌入式;網(wǎng)絡(luò)編程
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2009)15-3953-02
Applications of Network Programming in Embedded Linux Systems
JIANG Ai-zhen
(Channel 561 in SARFT,NanChang 330046,China)
Abstract: Linux supports a wide range of types of socket addresses and the abstract cluster as a single socket interface, the introduction of the abstract is for the convenience of Internet application programming, as well as web applications to provide data communications between the convenience. In this paper, starting from the basic concepts of socket, introduced the basic model of network programming and the Linux kernel support for the socket.
Key words: Linux; Embedded; Network programming
1 socket概述
在Linux中的網(wǎng)絡(luò)編程是通過(guò)socket套接字接口來(lái)進(jìn)行的,這一抽象的引入是為了方便聯(lián)網(wǎng)應(yīng)用程序的編程,在UNIX的BSD版本第一次使用了這種接口,因此他也叫做BSD套接字。應(yīng)用程序可以使用這種統(tǒng)一接口收發(fā)網(wǎng)絡(luò)上的數(shù)據(jù),網(wǎng)絡(luò)的socket數(shù)據(jù)傳輸是一種特殊的I/O口,socket也是一種文件描述符。套接字的設(shè)計(jì)符合Linux的習(xí)慣,在理想情況下,應(yīng)將所有可讀寫(xiě)訪問(wèn)的對(duì)象映射成文件,這樣就可以用普通的文件讀寫(xiě)操作來(lái)處理這些對(duì)象了,使通信中的收發(fā)可以很容易映射成讀寫(xiě)操作。在傳輸協(xié)議的上下文中,由這類讀寫(xiě)操作的對(duì)象就是通信關(guān)系的兩端,他們表示成了套接字。
BSD套接字是一個(gè)通用接口,它支持不同的網(wǎng)絡(luò)結(jié)構(gòu),同時(shí)也是一個(gè)內(nèi)部進(jìn)程間通信機(jī)制。當(dāng)一個(gè)主機(jī)上同時(shí)有多個(gè)應(yīng)用程序在運(yùn)行,他們使用tcp和udp協(xié)議進(jìn)行通信,則傳輸層協(xié)議收到數(shù)據(jù)后將根據(jù)端口和套接口區(qū)分?jǐn)?shù)據(jù)是傳給哪個(gè)應(yīng)用程序。端口是標(biāo)識(shí)傳輸層與應(yīng)用程序的數(shù)據(jù)接口,每個(gè)端口有一個(gè)16位的標(biāo)識(shí)符。套接口是IP地址與端口號(hào)的組合,用來(lái)標(biāo)識(shí)全網(wǎng)范圍內(nèi)的唯一一個(gè)端口,在tep和udP協(xié)議中用來(lái)標(biāo)識(shí)一個(gè)連接,網(wǎng)絡(luò)應(yīng)用程序之間通過(guò)套接口來(lái)實(shí)現(xiàn)通信。套接字是套接口描述字的簡(jiǎn)稱,是整型數(shù)字,它與文件描述符共用一段數(shù)值空間O_65535。應(yīng)用程序中使用套接字來(lái)調(diào)用套接口,套接字可認(rèn)為是指向套接口的指針,就像文件描述符是指向文件的指針一樣。一個(gè)套接字描述了一個(gè)鏈接的一個(gè)端口,一個(gè)socket端點(diǎn)可以用socket地址來(lái)描述,socket地址結(jié)構(gòu)由正地址,端口和使用協(xié)議組成(TCPorUDP),因此兩個(gè)互聯(lián)的進(jìn)程都要有一個(gè)描述他們之間連結(jié)的套接字。我們也可以把套接字看作為是一種特殊的管道,只是這種管道對(duì)于包含的數(shù)據(jù)量沒(méi)有限制。套接字存在于特定的通信域(即地址族)中,只有隸屬于同一地址族的套接字才能建立對(duì)話。Linux支持的協(xié)議族有AF_INET(IPv4協(xié)議)、AF_INET6(IPv6協(xié)議)和AF_UNIX(Unix域協(xié)議)。
Linux支持多種套接字類型,每種套接字類型對(duì)應(yīng)于創(chuàng)建套接字的應(yīng)用程序所希望的通信服務(wù)類型。同一協(xié)議簇可能提供多種服務(wù)類型,比如TCP/IP協(xié)議族提供的虛電路和數(shù)據(jù)報(bào)就是兩種不同的通信服務(wù)類型。TCP/IP中常用的socket類型共有三種,一種是流式socket(SOCK_STREAM),另一種是數(shù)據(jù)報(bào)式socket(SOCK_DGRAM),還有一種是原始socket(SOCK_RAW)。流式socket是一種面向連結(jié)的socket,對(duì)應(yīng)于面向連接的TCP服務(wù)應(yīng)用。數(shù)據(jù)報(bào)式socket是一種無(wú)連接的socket,對(duì)應(yīng)于無(wú)連接的UDP服務(wù)。原始套接字接口容許對(duì)較低層協(xié)議如IP、ICMP直接訪問(wèn),常用于檢驗(yàn)新的協(xié)議實(shí)現(xiàn)或訪問(wèn)現(xiàn)有服務(wù)中的新設(shè)備。
2 網(wǎng)絡(luò)編程基本模式
2.1 客戶機(jī)/服務(wù)器模式
網(wǎng)絡(luò)編程的基本模式是Client/Serve:模式,該模式的建立基于以下兩點(diǎn):
1) 非對(duì)等作用;2)通信完全是異步的客戶機(jī)/服務(wù)器模式在操作過(guò)程中采取的是主動(dòng)請(qǐng)示方式,首先服務(wù)器方要先啟動(dòng),并根據(jù)請(qǐng)示提供相應(yīng)服務(wù)。Server端首先調(diào)用socket創(chuàng)建一個(gè)一定類型socket,然后通過(guò)bind函數(shù)將這個(gè)socket綁定到一個(gè)client知道的端口上,接著server調(diào)用Listen函數(shù)設(shè)置傾聽(tīng)隊(duì)列的長(zhǎng)度,為了接收來(lái)自client端的請(qǐng)求做準(zhǔn)備,然后server調(diào)用accept,開(kāi)始在所綁定的端口傾聽(tīng)來(lái)自client端的連接請(qǐng)求。如果socket被設(shè)置成阻塞方式, accept調(diào)用將被阻塞,進(jìn)程被掛起,直到server收到來(lái)自client的請(qǐng)求后,accept才返回。Client端通過(guò)socket調(diào)用創(chuàng)建一個(gè)一定類型的socket(應(yīng)當(dāng)和server的socket類型相同)。然后調(diào)用connect函數(shù)向server所在的主機(jī)發(fā)出連接請(qǐng)求,連接時(shí),需要指定server所在主機(jī)的IP地址和server傾聽(tīng)的端口號(hào),連接的報(bào)文包含了client端的初始的序號(hào)SYN a和MSS=1460信息(最大數(shù)據(jù)段的大小)。正在傾聽(tīng)來(lái)自client的連接請(qǐng)求的server收到client的連接請(qǐng)求后,server從accept調(diào)用中返回(通常socket是阻塞方式工作的)。server將會(huì)向client端發(fā)送server端的初始序號(hào)SYN b和對(duì)client端的SYN a的確認(rèn)ACK=a+l,還有本端的最大數(shù)據(jù)MSS當(dāng)client端接收到server端的回應(yīng)時(shí),將發(fā)出對(duì)server請(qǐng)求的ACK=b+1。然后client從connect中返回,返回值是一個(gè)打開(kāi)的socket的描述符,這個(gè)描述符和文件的描述符類似,程序可以像使用文件的描述符一樣使用它。稍后,在server端收到client端對(duì)其請(qǐng)求的回應(yīng)時(shí),server將從accept調(diào)用返回,返回值也是一個(gè)socket的描述符。
2.2 面向連接協(xié)議的字節(jié)流套接字編程
字節(jié)流socket采用的是傳輸控制協(xié)議TCP。TCP提供面向連接的流傳輸,面向連接對(duì)可靠性的保證首先是它在進(jìn)行數(shù)據(jù)傳輸前,必須在信源端和信宿端建立連接。在面向鏈接傳輸?shù)拿恳粋€(gè)報(bào)文都需要接收端確認(rèn),未確認(rèn)的報(bào)文被認(rèn)為是出錯(cuò)報(bào)文。字節(jié)流套接字的服務(wù)器進(jìn)程和客戶進(jìn)程在通信前必須先建立連接,建立連接和通信的步驟如下:
1) 服務(wù)進(jìn)程首先調(diào)用Socket()創(chuàng)建一個(gè)字節(jié)流套接字,并調(diào)用bind()將服務(wù)器地址捆扎在該套接字上,接著調(diào)用listen()監(jiān)聽(tīng)連接請(qǐng)求,隨后調(diào)用accept()做好與客戶進(jìn)程建立連接的準(zhǔn)備,無(wú)連接請(qǐng)求時(shí),服務(wù)進(jìn)程被阻塞;
2) 客戶進(jìn)程調(diào)用Socket()創(chuàng)建字節(jié)流套接字,然后調(diào)用connect()向服務(wù)進(jìn)程發(fā)出連接請(qǐng)求;
3) 當(dāng)連接請(qǐng)求到來(lái)后,服務(wù)進(jìn)程被喚醒,生成一個(gè)新的字節(jié)流套接字,并用新套接字同客戶進(jìn)程的套接字建立連接,而服務(wù)進(jìn)程最早生成的套接字則繼續(xù)用于監(jiān)聽(tīng)網(wǎng)絡(luò)上的服務(wù)請(qǐng)求;
4) 服務(wù)進(jìn)程和客戶進(jìn)程通過(guò)調(diào)用read()和write()交換數(shù)據(jù);
5) 服務(wù)進(jìn)程和客戶進(jìn)程通過(guò)調(diào)用close()撤消套接字并中斷連接;當(dāng)選擇SOCK STREAM(字節(jié)流)類型的時(shí),sock()系統(tǒng)調(diào)用中的參數(shù)protocol(協(xié)議)總會(huì)選中TCP,而UDP則一直用作SOCK DGRAM類型的傳輸協(xié)議。
2.3 非連接協(xié)議的數(shù)據(jù)報(bào)套接字編程
數(shù)據(jù)報(bào)式socket采用的是用戶數(shù)據(jù)報(bào)協(xié)議UDP,它是建立在IP協(xié)議之上的,提供無(wú)連接數(shù)據(jù)報(bào)傳輸,主要應(yīng)用在高可靠性、低延遲的局域網(wǎng)上,它的優(yōu)點(diǎn)是高效率低開(kāi)銷,不用建立連接和撤銷連接,缺點(diǎn)是不可靠,報(bào)文丟失后需重發(fā)。數(shù)據(jù)套接字的服務(wù)進(jìn)程客戶進(jìn)程通信前不必建立連接,UDP則一直用作SOCKpGRAM類型的傳輸協(xié)議,通信的步驟如下:
1) 服務(wù)進(jìn)程首先調(diào)用Socket()創(chuàng)建一個(gè)數(shù)據(jù)套接字,并調(diào)用bind將服務(wù)器地址捆扎在該套接字上,然后調(diào)用recvfrom()等待客戶進(jìn)程發(fā)來(lái)的請(qǐng)求;
2) 客戶進(jìn)程在調(diào)用SocketQ創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字后,調(diào)用bindU將客戶機(jī)地址捆扎在此套接字上,接著調(diào)用sendto()向服務(wù)進(jìn)程發(fā)送請(qǐng)求,然后調(diào)用recvfrom()等待服務(wù)進(jìn)程返回該請(qǐng)求的處理結(jié)果;
3) 服務(wù)進(jìn)程在執(zhí)行客戶進(jìn)程所請(qǐng)求的任務(wù)后,調(diào)用sendto()將處理結(jié)果返回給客戶進(jìn)程;
4) 服務(wù)進(jìn)程和客戶進(jìn)程通過(guò)調(diào)用close()撤消套接字;
3 Linux內(nèi)核對(duì)socket的支持
確切地說(shuō),Linux內(nèi)核只提供了一個(gè)與套接字有關(guān)的系統(tǒng)調(diào)用,應(yīng)用程序的所有套接字調(diào)用都會(huì)映射到這個(gè)系統(tǒng)調(diào)用上。在Linux內(nèi)核中的net/socket.c中定義這個(gè)函數(shù)sys_socketcall(int call,unsigned long *args)。 include/asm/unistd.h中會(huì)指派一個(gè)數(shù)字,該數(shù)字會(huì)和arch/i386/kernel/entry.s中的系統(tǒng)調(diào)用一起添加到表格中。通過(guò)調(diào)用中。all參數(shù)可以說(shuō)明所指向的那個(gè)套接字函數(shù),在include/linux/net.h中定義了可接受的參數(shù)SYS_SOCKET, SYS_IND,SYS_CONNECT, SYS_LISTEN等,在用戶空間的函數(shù)庫(kù)中,帶有特定參數(shù)的sys_socketcall調(diào)用會(huì)映射成某個(gè)獨(dú)立函數(shù),在內(nèi)核中若要選中希望調(diào)用的那個(gè)函數(shù),需要在sys_socketcall函數(shù)中用到一條:witch命令如下所示,而在此之前首先要使用copy_from_user()命令將sys_ socketcall()的函數(shù)復(fù)制到一個(gè)向量中,即ensign long a中。
if copy_from user(a,args,nargs(call))
return _EFAULT;
a0=a[0];
al=a[1];
switch(call)
{
case
SYS_SOCKET:
err=sys_socket(a0,al,a[2]);
break;
SYS_BIND:
err=sys_bind(a0,al,a[2]);
break;
SYS_CONNECT:
err=sys_connect(a0,al,a[2]);
break;
為了支持BSD套接字,一個(gè)重要的數(shù)據(jù)結(jié)構(gòu)就是struct socket,它的定義位于
include/linux/net.h中,其定義如下:
struct socket
{
socket statestate;
unsigned long flags;
struct proto_ops *ops;
structmode *inode;
structfasync struct *fasync list;
structfile*file;
structsock*sk;
wait queue head t wait;
shorttype;
unsigned char passcred;};
與早期的內(nèi)核相比,socket結(jié)構(gòu)己經(jīng)稍有簡(jiǎn)化。state中存儲(chǔ)的是套接字狀態(tài)可以取值如下(include/linux/net.h):SS_FREE(不忙)、SSes UNCONNECTED(未連通)、SS_ONNECTING(目前正在連接)、SS_ONNECTED(已連通)、SS_ISCONNECTING(目前正在斷開(kāi)連接)。flags用以同步訪問(wèn),ops指針指向了連通協(xié)議(如tcp或udp)在初始化之后的協(xié)議運(yùn)作。就像Linux中的每個(gè)文件都有一個(gè)mode一樣,每個(gè)BSD套接字也分派了一個(gè)mode o file中存儲(chǔ)了一個(gè)指向該文件結(jié)構(gòu)的指針,這個(gè)結(jié)構(gòu)連接了套接字,因此它可以用與指向套接字。如果有進(jìn)程等待著這個(gè)套接字上的事件,也可以通過(guò)fasync_list找出該進(jìn)程。通過(guò)sk指針可以使用一個(gè)匹配的sock結(jié)構(gòu)。不過(guò),這個(gè)sock結(jié)構(gòu)是由BSD套接字之下、特定于協(xié)議的套接字初始化的,并且連通到這個(gè)指針。字段負(fù)責(zé)根據(jù)用戶空間中的同名套接字調(diào)用存儲(chǔ)第二個(gè)參數(shù),在Linux內(nèi)核include/asm/socket.h中定義了可接受的參數(shù)。
從上面分析可以看出任何時(shí)候通過(guò)一個(gè)socket來(lái)讀寫(xiě)數(shù)據(jù)時(shí),都是在使用一個(gè)系統(tǒng)調(diào)用(system_call)這個(gè)調(diào)用(例如read或write)跨越了用戶空間應(yīng)用程序與內(nèi)核的邊界。另外,在進(jìn)入內(nèi)核之前,您的調(diào)用會(huì)通過(guò)C庫(kù)來(lái)進(jìn)入內(nèi)核中的一個(gè)通用函數(shù)system_call()。從system_call()中,這個(gè)調(diào)用會(huì)進(jìn)入文件系統(tǒng)層,內(nèi)核會(huì)在這兒確定正在處理的是哪種類型的設(shè)備。最后,調(diào)用會(huì)進(jìn)入socket層,數(shù)據(jù)就是在這里進(jìn)行讀取或進(jìn)行排隊(duì)從而通過(guò)socket進(jìn)行傳輸?shù)摹?/p>
4 總結(jié)
每種網(wǎng)絡(luò)協(xié)議都提供網(wǎng)絡(luò)應(yīng)用開(kāi)發(fā)接口,TCP/IP協(xié)議的應(yīng)用開(kāi)發(fā)接口的事實(shí)標(biāo)準(zhǔn)是socket套接口,開(kāi)發(fā)socket的目的是隱藏網(wǎng)絡(luò)底層的復(fù)雜結(jié)構(gòu)和協(xié)議,使編程人員能夠簡(jiǎn)單抽象的對(duì)網(wǎng)絡(luò)進(jìn)行操作。socket面向客戶機(jī)/服務(wù)器模型,針對(duì)客戶機(jī)/服務(wù)器程序提供不同的socket的系統(tǒng)調(diào)用函數(shù),客戶端隨機(jī)申請(qǐng)一個(gè)socket,操作系統(tǒng)為之分配一個(gè)隨機(jī)socket號(hào);服務(wù)器端擁有全局公認(rèn)的socket號(hào),任何客戶都可以向他發(fā)送連接請(qǐng)求和信息請(qǐng)求。進(jìn)程通信以前,雙方必須各自創(chuàng)建一個(gè)端口,否則是沒(méi)有辦法在通信前建立聯(lián)系的,而socket提供了這種進(jìn)程間通信的端口。從網(wǎng)絡(luò)編程的套接字的分析來(lái)看,選擇TCP套接字和選擇UDP套接字編程,在傳輸數(shù)據(jù)時(shí)有著速度、效率和穩(wěn)定性的差別。TCP編程擁有了可靠的數(shù)據(jù)連接,UDP不具有。但是在速度方面,UDP編程確優(yōu)于TCP編程,特別是對(duì)于傳輸短消息?;谶@兩種通信方式優(yōu)缺點(diǎn)的考慮,在后續(xù)編寫(xiě)IDU控制應(yīng)用軟件時(shí),將UDP套接字用于硬件終端對(duì)外廣播本地IP地址,使局域網(wǎng)內(nèi)客戶端軟件識(shí)別某臺(tái)終端設(shè)備,獲取其MAC地址等硬件信息。將TCP套接字用于在客戶端傳輸用戶數(shù)據(jù),對(duì)硬件終端上的硬件設(shè)備進(jìn)行初始化設(shè)置。
參考文獻(xiàn):
[1] Warren W Gay. Linux Socket Programming by Example. Que(R), April 2000.
[2] Jonathan Corbet,Alessandro Rubini,Greg Kroah-Hartman. Linux Device Drivers 3rd Edition. Reilly Media Inc,2005.
[3] 孫瓊,嵌入式Linux應(yīng)用程序開(kāi)發(fā)詳解[M],北京:人民郵電出版社,2006.
關(guān)鍵詞:Windows Socket;TCP/IP;阻塞;非阻塞;異步選擇機(jī)制
中圖分類號(hào):TP393文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2008)09-11598-02
The Implementation of LAN Communication Based on Asynchronous Selection Mechanism
YANG Xiao-yan, BAI Ya-xiu
(Ankang University, Ankang 725000, China)
Abstract: Internet is increasingly universal in our country, customer's needs to network application also increase continuously. For the network condition's fast changing, to network application procedure development personnel, it is very important to develop the highly effective windows network application procedure. The article introduces Windows Socket, Asynchronous Selection Mechanism briefly and the implementation of LAN communication based on the mechanism according to the basic principle of the network correspondence with VC in dail.The method has characteristics of real time and high efficiency, and may be widely applied in C/S structure softwares.
Key words: Windows Socket; TCP/IP; Blocking; Non-blocking; Asynchronous Selection Mechanism
1 引言
Internet在我國(guó)日益普及,用戶對(duì)網(wǎng)絡(luò)應(yīng)用的需求也不斷增長(zhǎng),提高網(wǎng)絡(luò)程序的效率就顯得相當(dāng)重要。網(wǎng)絡(luò)由一系列協(xié)議組成,TCP/IP協(xié)議是當(dāng)今異種機(jī)互聯(lián)的工業(yè)標(biāo)準(zhǔn),它支持不同廠家、不同操作系統(tǒng)的計(jì)算機(jī)之間的通信。TCP/IP協(xié)議族的分層結(jié)構(gòu)中的傳輸層為相互通信的主機(jī)提供了端到端的通信能力。其中,TCP協(xié)議向應(yīng)用層提供可靠的數(shù)據(jù)連接,它保證進(jìn)程間數(shù)據(jù)傳輸?shù)恼_、有序和不重復(fù)。UDP協(xié)議僅僅為應(yīng)用層提供數(shù)據(jù)報(bào)的分組發(fā)送服務(wù),數(shù)據(jù)傳輸?shù)目煽啃灾荒芡ㄟ^(guò)應(yīng)用層來(lái)保證。TCP和UDP的主要差別在于可靠性,TCP高度可用,需要大量功能開(kāi)銷,而UDP是簡(jiǎn)單、高效。由于是在局域網(wǎng)中實(shí)現(xiàn)通信,為了達(dá)到簡(jiǎn)單、實(shí)時(shí)、高效的目的,在介紹Windwins Socket,異步選擇機(jī)制,網(wǎng)絡(luò)通信原理等相關(guān)知識(shí)的基礎(chǔ)上,根據(jù)基于數(shù)據(jù)報(bào)套接字(UDP協(xié)議)的編程步驟,詳細(xì)探討了在VC中基于異步選擇機(jī)制的局域網(wǎng)通信的實(shí)現(xiàn)。
2 Windows Socket及異步選擇機(jī)制
2.1 Windows Socket
套接字(Socket)是建立在傳輸層協(xié)議(主要是TCP和UDP)上的一種套接字規(guī)范,最初是由美國(guó)加州Berkley大學(xué)提出,它定義了兩臺(tái)計(jì)算機(jī)間進(jìn)行通信的規(guī)范,套接字屏蔽了底層通信軟件和具體操作系統(tǒng)的差異,使得任何兩臺(tái)安裝了TCP協(xié)議軟件和實(shí)現(xiàn)了套接字規(guī)范的計(jì)算機(jī)之間的通信成為可能。Windows Scoket是UNIX操作系統(tǒng)下的Bakeley Socket應(yīng)用程序開(kāi)發(fā)接口在Windows環(huán)境下的實(shí)現(xiàn)。Windows Socket規(guī)范主要有WinSock1.1和WinSock2兩個(gè)版本,它們保持了和Bekeley Socket函數(shù)的兼容性,并做出了重要擴(kuò)充。這些擴(kuò)充主要是增加了一些異步請(qǐng)求函數(shù)和對(duì)網(wǎng)絡(luò)事件的異步選擇機(jī)制,使之更適合Windows平臺(tái)消息驅(qū)動(dòng)的特性。
2.2 異步選擇機(jī)制
Windows Socket在兩種模式下執(zhí)行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,執(zhí)行操作的Winsock函數(shù)會(huì)一直等待下去,不會(huì)立即返回程序(將控制權(quán)交還給程序)。Windows Sockets為了支持Windows消息驅(qū)動(dòng)機(jī)制,使應(yīng)用程序開(kāi)發(fā)者能夠方便的處理網(wǎng)絡(luò)通信,它對(duì)網(wǎng)絡(luò)事件采用了基于消息的異步存取策略。Windows Sockets的異步選擇函數(shù)WSAAsyncSelect()提供了消息機(jī)制的網(wǎng)絡(luò)事件選擇,當(dāng)使用它登記的網(wǎng)絡(luò)事件發(fā)生時(shí),Windows應(yīng)用程序相應(yīng)的窗口函數(shù)將收到一個(gè)消息,消息中指示了發(fā)生的網(wǎng)絡(luò)事件,以及與事件相關(guān)的一些信息。這個(gè)函數(shù)自動(dòng)設(shè)置套接字為非阻塞模式。
Int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg)
S,標(biāo)識(shí)了請(qǐng)求事件通知的套接字描述符。
hWnd,標(biāo)識(shí)當(dāng)一個(gè)網(wǎng)絡(luò)事件發(fā)生時(shí),接收消息的窗口的句柄
wMsg,網(wǎng)絡(luò)事件發(fā)生時(shí),窗口接收到的消息(這里指的是一個(gè)自定義的消息。)
lEvent,指定應(yīng)用感興趣的網(wǎng)絡(luò)事件。
3 網(wǎng)絡(luò)通信的基本原理
網(wǎng)絡(luò)通信實(shí)質(zhì)上是網(wǎng)絡(luò)中的不同主機(jī)進(jìn)程之間的相互通信(可以把同機(jī)進(jìn)程通信看作其中的特例)問(wèn)題。與單純的同機(jī)通信相比,網(wǎng)絡(luò)通信需要解決以下三個(gè)問(wèn)題。第一,標(biāo)識(shí)網(wǎng)絡(luò)中的進(jìn)程。在同一主機(jī)上,不同進(jìn)程可以用進(jìn)程標(biāo)識(shí)符來(lái)標(biāo)識(shí),而在網(wǎng)絡(luò)通信中則是利用端口號(hào)來(lái)標(biāo)識(shí)。端口是TCP和UDP與應(yīng)用程序打交道的訪問(wèn)點(diǎn),是TCP/IP協(xié)議軟件的一部份。TCP/IP協(xié)議規(guī)定了一些標(biāo)準(zhǔn)保留端口,主要提供給服務(wù)器進(jìn)程使用,用戶進(jìn)程可以申請(qǐng)使用非保留端口,其端口的標(biāo)識(shí)符在本機(jī)種具有唯一性。因此可以利用端口號(hào)作為網(wǎng)絡(luò)中進(jìn)程本身的標(biāo)識(shí)。第二,多重協(xié)議的識(shí)別。網(wǎng)絡(luò)中的兩個(gè)進(jìn)程必須使用協(xié)議來(lái)相互通信,而網(wǎng)絡(luò)協(xié)議有多種,這就要求進(jìn)程能夠在眾多的協(xié)議中作出選擇。原因在于不同的協(xié)議地址格式不同、工作方式不同(比如面向連接與無(wú)連接),協(xié)議端口分配是相互獨(dú)立的。綜合以上兩點(diǎn),在網(wǎng)絡(luò)中全局地址標(biāo)識(shí)一個(gè)本地進(jìn)程需要一個(gè)三元組:協(xié)議,本地地址,本地端口號(hào)。而一個(gè)完整的網(wǎng)絡(luò)通信實(shí)例是由通信兩端的進(jìn)程組成,因此需要一個(gè)五元組來(lái)標(biāo)識(shí):協(xié)議,本地地址,本地端口號(hào),異地地址,異地端口號(hào)。這里的本地地址、異地地址是用來(lái)標(biāo)識(shí)計(jì)算機(jī)的,一般是指計(jì)算機(jī)的IP地址。第三,進(jìn)程之間相互作用模式,即應(yīng)用程序相互作用的模式。在網(wǎng)絡(luò)中兩個(gè)應(yīng)用程序間主要的作用模式是客戶機(jī)/服務(wù)器模式,在這種模式中客戶應(yīng)用程序向服務(wù)器程序請(qǐng)求服務(wù),這種方式隱含了在建立客戶機(jī)/服務(wù)器間通信時(shí)的非對(duì)稱性。表1顯示了基于數(shù)據(jù)報(bào)套接字的客戶機(jī)/服務(wù)器編程模型。
4 基于異步選擇機(jī)制的局域網(wǎng)通信的實(shí)現(xiàn)
4.1 實(shí)現(xiàn)思想
根據(jù)數(shù)據(jù)報(bào)套接字的客戶機(jī)/服務(wù)器編程模型,采用Windows Scoket的異步選擇機(jī)制,將服務(wù)器端和客戶端在同一個(gè)程序中實(shí)現(xiàn)。需要通信的計(jì)算機(jī)只要運(yùn)行同樣的程序,通過(guò)輸入對(duì)方的IP地址,或主機(jī)名就可以實(shí)現(xiàn)相互通信。
4.2 在VC中的具體實(shí)現(xiàn)
4.2.1 創(chuàng)建基于對(duì)話框的MFC EXE工程
在應(yīng)用程序類的初始化函數(shù):InitInstance()種調(diào)用如下語(yǔ)句加載套接字庫(kù)進(jìn)行版本協(xié)商。(采用Winsock2版本)
WSAStartup(wVersionRequested,&wsaData);
if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
{WSACleanup();return FALSE;}
在預(yù)編譯頭文件中包含頭文件winsock2.h,并鏈接庫(kù)文件ws2_32.lib.
4.2.2 服務(wù)器端的實(shí)現(xiàn)
m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);//創(chuàng)建數(shù)據(jù)報(bào)套接字
SOCKADDR_IN addrSock;//地址結(jié)構(gòu)體的定義
addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSock.sin_family=AF_INET;
addrSock.sin_port=htons(6000);//端口號(hào)為6000
//將套接字綁定到一個(gè)本地地址和端口上
bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));
//采用異步選擇機(jī)制注冊(cè)網(wǎng)絡(luò)讀取事件
WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ);
#define UM_SOCK WM_USER+1 //自定義消息UM_SOCK
afx_msg void OnSock(WPARAM,LPARAM);//消息響應(yīng)函數(shù)原型聲明
ON_MESSAGE(UM_SOCK,OnSock)//消息映射
//接收數(shù)據(jù)(消息響應(yīng)函數(shù)的實(shí)現(xiàn))
OnSock(WPARAM wParam,LPARAM lParam)
{ switch(LOWORD(lParam))//判斷是否是網(wǎng)絡(luò)讀取事件發(fā)生了。
{case FD_READ:
if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead, &dwFlag,(SOCKADDR*)&addrFrom,&len,NULL,NULL))
{MessageBox("接收數(shù)據(jù)失敗!"); return;}
str.Format("%s說(shuō):%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);
str+="\r\n";
GetDlgItemText(IDC_EDIT_RECV,strTemp);
str+=strTemp;
SetDlgItemText(IDC_EDIT_RECV,str);
break;
}
}
4.2.3 客戶端(實(shí)現(xiàn)數(shù)據(jù)的發(fā)送)
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); //獲取服務(wù)器端的IP地址
SOCKADDR_IN addrTo;// 接收方地址結(jié)構(gòu)體定義
addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(6000);
//將數(shù)據(jù)發(fā)給服務(wù)器
if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0,
(SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))
{MessageBox("發(fā)送數(shù)據(jù)失敗!");return; }
4.3.4 在應(yīng)用類及對(duì)話框類的析構(gòu)函數(shù)中分別終止套接字庫(kù),關(guān)閉套接字
WSACleanup();//終止對(duì)套接字庫(kù)的使用
closesocket(m_socket);//關(guān)閉套接字,釋放與套接字相關(guān)的資源。
4.3.5 程序運(yùn)行效果如圖1所示
<E:\2008學(xué)術(shù)交流\2008學(xué)術(shù)交流第一卷第九期\第1次供稿 54\2網(wǎng)絡(luò)通訊及安全\yxy02.tif>
4.3.6 注意問(wèn)題
編寫(xiě)網(wǎng)絡(luò)通信程序需要特別注意的是:每一臺(tái)機(jī)器內(nèi)部對(duì)變量的字節(jié)存儲(chǔ)順序不同,而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)是一定要統(tǒng)一順序的。所以對(duì)內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序不同的機(jī)器,一定要對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換;在一個(gè)套接字上基于消息請(qǐng)求網(wǎng)絡(luò)事件通知,可以同時(shí)請(qǐng)求多個(gè)網(wǎng)絡(luò)事件,當(dāng)接收到到消息時(shí),需要根據(jù)發(fā)生的網(wǎng)絡(luò)事件作相應(yīng)的處理;網(wǎng)絡(luò)的狀況瞬息萬(wàn)變,在調(diào)用函數(shù)的時(shí)候,應(yīng)對(duì)函數(shù)的返回值進(jìn)行判斷,以便找到出錯(cuò)原因。
5 結(jié)論
在Windows平臺(tái)下程序的運(yùn)行都是基于消息的,如果采用阻塞套接字,就會(huì)由于接收函數(shù)的調(diào)用而導(dǎo)致程序暫停運(yùn)行,影響了程序運(yùn)行的效率。采用異步選擇機(jī)制,接收端和發(fā)送端在同一個(gè)程序中,并且采用數(shù)據(jù)報(bào)套接字實(shí)現(xiàn)了局域網(wǎng)通信,達(dá)到了簡(jiǎn)單、高效、實(shí)時(shí)的目的。在實(shí)現(xiàn)網(wǎng)絡(luò)通信時(shí),除了應(yīng)注意的問(wèn)題外,還應(yīng)了解相關(guān)的網(wǎng)絡(luò)協(xié)議以及程序在Windows平臺(tái)下工作的原理,根據(jù)應(yīng)用的具體需求,才能實(shí)現(xiàn)真正高性能的網(wǎng)絡(luò)通信。
參考文獻(xiàn):
[1] 陳明.實(shí)用網(wǎng)絡(luò)教程[M].北京:清華大學(xué)出版社,2006.1.
[2] 胡志坤,秦業(yè),等.Visual C++通信工程實(shí)例精解[M].北京:機(jī)械工業(yè)出版社,2007.1
[3] 孫小剛,韓冬,等.面向軟件工程的Visual C++網(wǎng)絡(luò)程序開(kāi)發(fā)[M].北京:清華大學(xué)出版社,2004,11.
關(guān)鍵詞:JAVA;SWING;Socket;IM
中圖分類號(hào):TP319文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào)文章編號(hào):1672-7800(2013)012-0091-03
作者簡(jiǎn)介:徐華平(1977-),男,碩士,鹽城師范學(xué)院講師,研究方向?yàn)榻逃浖O(shè)計(jì)及教育信息化。
0引言
由于企業(yè)內(nèi)管理、生產(chǎn)、銷售等各個(gè)環(huán)節(jié)的信息流動(dòng)與傳遞已成為企業(yè)正常生產(chǎn)與運(yùn)轉(zhuǎn)的重要條件,搭建一個(gè)基于企業(yè)內(nèi)部網(wǎng)絡(luò)的即時(shí)通信平臺(tái)的重要性不言而喻。然而,通用的商業(yè)IM軟件依賴于互聯(lián)網(wǎng)接入技術(shù),其信息安全性差。因此,有必要建立一種基于局域網(wǎng)的內(nèi)部即時(shí)通信平臺(tái)。
在各類網(wǎng)絡(luò)客戶端之間的通信機(jī)制的選擇中,基于Socket機(jī)制無(wú)疑是成熟、可靠的選擇。這種機(jī)制透明于各類局域網(wǎng)絡(luò)類型,能夠?yàn)槠髽I(yè)提供一種優(yōu)良、高效、快速的通信機(jī)制?;谏鲜鰞?yōu)點(diǎn),使得基于Socket機(jī)制的網(wǎng)絡(luò)通信軟件無(wú)需對(duì)企業(yè)現(xiàn)有的網(wǎng)絡(luò)硬件設(shè)施進(jìn)行任何變動(dòng),因而具有成本低廉的優(yōu)點(diǎn),能有效降低局域網(wǎng)通信負(fù)荷,提高局域網(wǎng)的使用效率,可以很好地解決企業(yè)內(nèi)部局域網(wǎng)的各種通信需求。
1系統(tǒng)關(guān)鍵技術(shù)分析與選擇
本文重點(diǎn)探討即時(shí)通訊軟件設(shè)計(jì)中實(shí)現(xiàn)系統(tǒng)通訊的關(guān)鍵技術(shù)。在應(yīng)用系統(tǒng)中,客戶端向服務(wù)端發(fā)送請(qǐng)求,等待服務(wù)器返回?cái)?shù)據(jù),再刷新客戶端的數(shù)據(jù),稱之為通訊。在B/S或UE設(shè)備上實(shí)現(xiàn)即時(shí)通信的客戶端,其同步方式是不可行的。此類客戶端一方面處理能力弱,另一方面網(wǎng)絡(luò)會(huì)引起延時(shí),因此,在客戶端之間以及客戶端與服務(wù)器之間的同步協(xié)調(diào)難以做到,通常采用異步方式。在 C/S 網(wǎng)絡(luò)編程中,數(shù)據(jù)的發(fā)送和接收通過(guò)Socket 套接口完成,套接字分為阻塞式和非阻塞式[1]。
通過(guò)對(duì)Java Net框架下GUI技術(shù)、通信協(xié)議等關(guān)鍵技術(shù)的分析,本通信器決定采用如下技術(shù)方案:
(1) Swing技術(shù)開(kāi)發(fā)GUI桌面程序:采用Swing頂層容器、基本組件和事件處理等輕量級(jí)組件構(gòu)建局域網(wǎng)通信系統(tǒng)的程序主要界面。
(2) UDP通信協(xié)議: 鑒于UDP協(xié)議無(wú)鏈接,不可靠傳輸,通信雙方可不保持對(duì)方的狀態(tài),只需配置端口和IP地址即可通信,方便快捷,減少網(wǎng)絡(luò)開(kāi)銷;考慮到UDP協(xié)議不可靠性,該通信器的通信協(xié)議中采用了確認(rèn)與重傳機(jī)制來(lái)保證數(shù)據(jù)傳輸?shù)目煽啃?,采用了?dòng)態(tài)的超時(shí)重傳定時(shí)器值提高了本協(xié)議的適應(yīng)性和靈活性,還使用了在應(yīng)用層對(duì)數(shù)據(jù)進(jìn)行分片的方法來(lái)避免IP層分片的低效率[2]。
(3) 在本系統(tǒng)設(shè)計(jì)采用UDP通信協(xié)議時(shí),基于UDP的Socket編程技術(shù),中提供了兩個(gè)類DatagramSoeket和DatagramPacket用來(lái)支持?jǐn)?shù)據(jù)報(bào)通信。Datagramsocket用來(lái)在程序之間建立傳送數(shù)據(jù)報(bào)的通信連接是數(shù)據(jù)報(bào)通信中的Socket。在數(shù)據(jù)報(bào)實(shí)現(xiàn)C/S通信程序時(shí),無(wú)論在客戶端還是服務(wù)器端,都要首先建立一個(gè)DatagramSoeket對(duì)象,用來(lái)表示數(shù)據(jù)報(bào)通信的端點(diǎn),應(yīng)用程序通過(guò)Socket接收或發(fā)送數(shù)據(jù)報(bào)。DatagramPaeket則用來(lái)表示一個(gè)數(shù)據(jù)報(bào),它是傳輸數(shù)據(jù)的載體,封裝了數(shù)據(jù)、數(shù)據(jù)長(zhǎng)度、數(shù)據(jù)報(bào)地址等信息[3]。
(4)系統(tǒng)采用Derby作為數(shù)據(jù)庫(kù),Derby數(shù)據(jù)庫(kù)是一個(gè)純用Java實(shí)現(xiàn)的內(nèi)存數(shù)據(jù)庫(kù),屬于Apache的開(kāi)源項(xiàng)目。因?yàn)槭怯肑ava實(shí)現(xiàn)的,所以可以在任何平臺(tái)上運(yùn)行。另外一個(gè)特點(diǎn)是體積小、免安裝,只需要幾個(gè)小Jar包就可以運(yùn)行。
2系統(tǒng)總體目標(biāo)與需求分析
2.1設(shè)計(jì)目標(biāo)
(1)使用Swing組件實(shí)現(xiàn)圖形化用戶界面。
(2)使用Socket技術(shù)和UDP協(xié)議通過(guò)IP地址和PORT提供不可靠非連接通信。
(3) 實(shí)現(xiàn)基于線程池的多端口監(jiān)聽(tīng)。
(4) 實(shí)現(xiàn)客戶端間的文字、文件信息共享。
2.2功能需求分析
(1)用戶管理。即時(shí)通訊系統(tǒng)擁有多個(gè)賬戶,允許多個(gè)用戶注冊(cè)。一個(gè)用戶可以注冊(cè)多個(gè)標(biāo)識(shí),注冊(cè)所使用的帳號(hào)類型為字母數(shù)字的組合。注冊(cè)新用戶時(shí)必須填寫(xiě)符合要求的信息,注冊(cè)后只有用戶名與密碼驗(yàn)證成功才能正確登錄。
(2)分組管理。分組管理部分要能夠?qū)崿F(xiàn)分組的添加與刪除,所添加的分組名稱可以是中文也可以是字母數(shù)字的組合,通過(guò)對(duì)分組的有效管理便于更方便地管理好友。
(3)好友管理。用戶可以查詢所有用戶,適當(dāng)選擇加為好友。一個(gè)用戶可以添加多個(gè)用戶為好友,同時(shí)一個(gè)用戶也可以被多個(gè)用戶添加為好友。用戶可以刪除好友,但是用戶只可以將好友放在一個(gè)組中。
(4)即時(shí)通訊。即時(shí)通訊模塊用戶可以與在線的好友進(jìn)行聊天,用戶首先查看好友是否在線,如果在線即可進(jìn)行即時(shí)通訊,并且用戶可以查看與好友的所有聊天記錄。
2.3數(shù)據(jù)庫(kù)設(shè)計(jì)
根據(jù)數(shù)據(jù)邏輯結(jié)構(gòu)設(shè)計(jì)的情況,本系統(tǒng)數(shù)據(jù)庫(kù)的數(shù)據(jù)表共有好友數(shù)據(jù)表、用戶表、分組表3個(gè),和一般數(shù)據(jù)庫(kù)應(yīng)用系統(tǒng)基本類似,這里不再贅述。
3系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)
(1)用戶列表模塊。
該模塊實(shí)現(xiàn)本局域網(wǎng)內(nèi)平臺(tái)中當(dāng)前登錄用戶的列表顯示,并顯示當(dāng)前用戶通過(guò)搜索或好友添加功能添加的用戶。在每個(gè)用戶節(jié)點(diǎn)上右擊鼠標(biāo),會(huì)出現(xiàn)Popup功能菜單項(xiàng),其中菜單項(xiàng)的添加好友和刪除好友都會(huì)控制好友列表節(jié)點(diǎn)的增加和刪除。關(guān)鍵代碼部分如下:
……
add(p,BorderLayout.NORTH);
add(new JScrollPane(table),BorderLayout.CENTER);
setBounds(100,100,200,600);
setVisible(true);
validate();
thread.start();
模塊說(shuō)明:
本模塊主要采用了Swing技術(shù)、多線程技術(shù)和循環(huán)技術(shù)。在系統(tǒng)進(jìn)程啟動(dòng)時(shí),利用多線程技術(shù)啟動(dòng)線程循環(huán)——自動(dòng)檢測(cè)數(shù)據(jù)庫(kù)已有好友數(shù)據(jù)信息,然后使用Swing提供的JTree類對(duì)象生成一個(gè)分層顯示數(shù)據(jù)的視圖即用戶列表。JTree類中的基本對(duì)象叫作結(jié)點(diǎn),它表示在給定層次結(jié)構(gòu)中的數(shù)據(jù)項(xiàng)。樹(shù)以垂直方式顯示數(shù)據(jù),每行顯示一個(gè)節(jié)點(diǎn)。每個(gè)樹(shù)中只有一個(gè)根節(jié)點(diǎn),其他節(jié)點(diǎn)從根節(jié)點(diǎn)引出。除根節(jié)點(diǎn)外,其他節(jié)點(diǎn)分為兩類:一類是帶子節(jié)點(diǎn)的分支節(jié)點(diǎn);另一類是不帶子節(jié)點(diǎn)的葉節(jié)點(diǎn)。樹(shù)節(jié)點(diǎn)由Javax.swing.tree包中的接口TreeNode來(lái)定義的,該接口被DefaultMutableTreeNode類實(shí)現(xiàn)。為了創(chuàng)建一個(gè)樹(shù),使用DefaultMutableTreeNode類為樹(shù)創(chuàng)建節(jié)點(diǎn)。
通過(guò)建立一個(gè)存放用戶姓名的標(biāo)簽對(duì)象Lable,該標(biāo)簽顯示的內(nèi)容由用戶在登錄時(shí)在登陸界面輸入的用戶名信息來(lái)確定,另外創(chuàng)建一個(gè)標(biāo)簽來(lái)存放“在線列表”,然后實(shí)質(zhì)上存放在線列表里面的內(nèi)容則有一個(gè)表格組件來(lái)存放所對(duì)應(yīng)的在線用戶,通過(guò)UDP數(shù)據(jù)報(bào)廣播來(lái)獲取在線的用戶的姓名以及IP地址,然后將數(shù)組的數(shù)據(jù)導(dǎo)入表格組件中,就可以形成在線用戶列表。
(2)聊天功能模塊。
用戶通過(guò)單擊在線用戶列表中的節(jié)點(diǎn)選擇某一用戶頭像,即可觸發(fā)聊天對(duì)話框界面。聊天對(duì)話框的左上部分為顯示聊天記錄。左下方為聊天輸入框。右側(cè)可以顯示對(duì)方的用戶名和IP等信息。本次聊天內(nèi)容在窗口關(guān)閉時(shí),重新打開(kāi)后仍會(huì)保存,一旦整個(gè)系統(tǒng)重啟之后,上次的聊天內(nèi)容才會(huì)清除。
關(guān)鍵代碼如下:
……
con.add(new JScrollPane(inMessage),BorderLayout.CENTER);
con.add(p,BorderLayout.SOUTH);
Thread thread=new Thread(this);
……
模塊說(shuō)明:在用戶聊天界面上分別創(chuàng)建一個(gè)既用于接收信息又用于發(fā)送信息的多行純文本域?qū)ο驤textArea,它們來(lái)分別存放要發(fā)送的信息和聊天信息。outMessage用來(lái)存放所要發(fā)送的數(shù)據(jù),而inMessage用來(lái)存放聊天的信息。
(3)用戶搜索功能模塊。
搜索在線用戶的設(shè)計(jì)是基于廣播數(shù)據(jù)報(bào)的,通過(guò)采用組播地址然后創(chuàng)建廣播套接字,設(shè)置一個(gè)廣播的范圍,在這里設(shè)置的是一個(gè)本地的局域網(wǎng)的范圍。設(shè)置了廣播范圍之后加入廣播組,就可以廣播數(shù)據(jù)報(bào)和接收廣播數(shù)據(jù)報(bào)。
關(guān)鍵代碼如下:
……
group=InetAddress.getByName("10.192.168.0");
socket=new MulticastSocket(port);
socket.setTimeToLive(255);
socket.joinGroup(group);
和多數(shù)通行通信軟件一樣,發(fā)送端在某一端口廣播數(shù)據(jù),接收端在指定的端口或端口范圍內(nèi)偵聽(tīng)并接收廣播數(shù)據(jù)。
DatagramPacket packet=null;
byte data[]=total.getBytes();
packet=new DatagramPacket(data,data.length,group,port);
System.out.println(new String(data));
socket.send(packet);
……
這里的Packet是某一進(jìn)程中待發(fā)送的數(shù)據(jù)報(bào),定義一個(gè)數(shù)組用來(lái)存放,并且設(shè)置了長(zhǎng)度length,還有端口號(hào)port,最后通過(guò)socket在本地的局域網(wǎng)上進(jìn)行廣播數(shù)據(jù)報(bào)。通過(guò)廣播自己的IP地址,讓所有在線的用戶接收到自己的IP地址并且添加到對(duì)方的在線列表中,這樣每一個(gè)用戶的一個(gè)廣播就使得列表可以更新。
(4)點(diǎn)對(duì)點(diǎn)資源共享功能模塊。
如上所述,為了保證系統(tǒng)通信的即時(shí)性,選擇的是使用UDP數(shù)據(jù)報(bào)的一個(gè)點(diǎn)對(duì)點(diǎn)的通信方式,實(shí)現(xiàn)了兩個(gè)客戶端間進(jìn)程間的通信,這里使用DatagramPacket方法創(chuàng)建數(shù)據(jù)報(bào)對(duì)象:
DatagramPacket(b,b.length,address,8604);//發(fā)送端口是8604
DatagramSocket mail=new DatagramSocket();
通過(guò)DatagramPacket為存放指定數(shù)據(jù)的數(shù)據(jù)報(bào),其中包括了指定的數(shù)據(jù),數(shù)據(jù)接收方的信息,并且要明確發(fā)送的目的地址address,同時(shí)指定偵聽(tīng)該通信的主機(jī)端口號(hào)為8604。而在接收的時(shí)候:
byte b[]=new byte[5120];
mail.receive(pack);
構(gòu)建一個(gè)長(zhǎng)度為5 120字節(jié)的數(shù)組用于保存接收到的數(shù)據(jù),同時(shí)設(shè)計(jì)了一個(gè)參數(shù)Pack,可以把收到的通信數(shù)據(jù)報(bào)傳遞給參數(shù)Pack。
4結(jié)語(yǔ)
本通信器界面友好, 雖然與大型復(fù)雜即時(shí)通訊軟件相比在功能上還稍有欠缺, 但是其作為即時(shí)通訊的主體功能已經(jīng)具備,尤其是其難易程度適中,綜合運(yùn)用了Java 面向?qū)ο蟮亩喾N知識(shí),是高等院?!毒W(wǎng)絡(luò)編程》課程的一個(gè)很好的實(shí)例。
當(dāng)然由于諸多條件的制約,以及系統(tǒng)設(shè)計(jì)的定位,該設(shè)計(jì)在文件傳輸多樣化、網(wǎng)絡(luò)適應(yīng)性、通信機(jī)制、數(shù)據(jù)存儲(chǔ)與共享方面仍然存在較多不足,主要有以下幾個(gè)方面:
(1) 實(shí)現(xiàn)多樣化文件傳輸,如:圖片、文檔、音頻、視頻等。
(2) 初期對(duì)系統(tǒng)所采用數(shù)據(jù)庫(kù)的通用性認(rèn)識(shí)不足,后期可以采用Mysql或XML等主流數(shù)據(jù)存儲(chǔ)技術(shù),使得系統(tǒng)具有更強(qiáng)的適應(yīng)性。
(3) 改進(jìn)系統(tǒng)通信協(xié)議,考慮穿透防火墻、異構(gòu)網(wǎng)絡(luò)實(shí)現(xiàn)多局域網(wǎng)之間的通信。
(4) 用戶界面應(yīng)借鑒主流IM軟件,實(shí)現(xiàn)良好的用戶體驗(yàn)。
(5) 如果作為一個(gè)高性能企業(yè)內(nèi)部即時(shí)通信軟件,應(yīng)考慮到大用戶、多并發(fā)的情況,應(yīng)盡可能減輕服務(wù)器負(fù)荷,盡可能地將一些處理、判斷放在客戶端進(jìn)行,節(jié)約服務(wù)器端開(kāi)銷。
參考文獻(xiàn)參考文獻(xiàn):
[1]陳立浩. 基于B/S和C/S的即時(shí)通信系統(tǒng)[J].計(jì)算機(jī)工程, 2009(15).
關(guān)鍵詞:winsock;TCP/IP協(xié)議;計(jì)費(fèi)管理系統(tǒng)
Winsock是一個(gè)ActiveX控件,它為采用客戶機(jī)/服務(wù)器通信機(jī)制的網(wǎng)絡(luò)提供了編制接口,使客戶機(jī)端和服務(wù)器端藉此實(shí)現(xiàn)連接和數(shù)據(jù)交換。它不是一種網(wǎng)絡(luò)協(xié)議,而是一套開(kāi)放的、支持多種協(xié)議的Windows下的網(wǎng)絡(luò)編程接口。Socket實(shí)際在計(jì)算機(jī)中提供了一個(gè)通信端口,可以通過(guò)這個(gè)端口與任何一個(gè)具有Socket接口的計(jì)算機(jī)通信。應(yīng)用程序在網(wǎng)絡(luò)上傳輸,接收的信息都通過(guò)這個(gè)Socket接口來(lái)實(shí)現(xiàn)。
本文主要研究了Winsock的工作原理、編程方法,TCP/IP協(xié)議下計(jì)算機(jī)的工作原理,基于TCP/IP協(xié)議的網(wǎng)絡(luò)編程,服務(wù)器端與客戶機(jī)端程序的設(shè)計(jì)等,通過(guò)解決這些問(wèn)題來(lái)實(shí)現(xiàn)Winsock網(wǎng)絡(luò)按時(shí)收費(fèi)系統(tǒng)的設(shè)計(jì)。
一、系統(tǒng)設(shè)計(jì)方案的研究
我們所設(shè)計(jì)的網(wǎng)絡(luò)計(jì)費(fèi)系統(tǒng)分為三部分,一是在win32平臺(tái)上的線程對(duì)數(shù)據(jù)包的截獲,并且進(jìn)行初步的整理,生成日志文件,作為前臺(tái);而后臺(tái)則是在windows net server上的數(shù)據(jù)庫(kù)管理,直接取得第一部分截取程序生成的日志文件和通過(guò)ftp獲取在Linux服務(wù)器上其它服務(wù)生成的日志文件,對(duì)其分析,并至于以BDE驅(qū)動(dòng)的數(shù)據(jù)庫(kù)文件中。三是計(jì)費(fèi)信息用戶查詢子系統(tǒng),使得用戶能在線通過(guò)權(quán)限查詢到自己各個(gè)時(shí)間段內(nèi)的計(jì)費(fèi)信息和費(fèi)用情況,有利于減輕網(wǎng)絡(luò)管理員的工作負(fù)擔(dān)。
Socket有3種主要類型:流式套接口,數(shù)據(jù)報(bào)套接口和原始套接口。面向連接服務(wù)器處理的請(qǐng)求往往比較復(fù)雜,原理是:服務(wù)器端不斷監(jiān)聽(tīng)客戶端的請(qǐng)求,當(dāng)客戶端向服務(wù)器端發(fā)出連接請(qǐng)求并被服務(wù)器端檢測(cè)到以后,服務(wù)器會(huì)接收客戶端的請(qǐng)求,并建立連接。本文在方案選擇上采用了在網(wǎng)絡(luò)編程中最常用的一種模型--客戶機(jī)/服務(wù)器模型。選取了基于TCP/IP的客戶機(jī)/服務(wù)器模型和面向連接的流式套接字。
二、系統(tǒng)的軟件設(shè)計(jì)
(一)服務(wù)器的設(shè)計(jì)
①在初始化階段調(diào)用WSAStartup(),此函數(shù)在應(yīng)用程序中初始化Windows Sockets DLL ,只有此函數(shù)調(diào)用成功后,應(yīng)用程序才可以再調(diào)用其他Windows Sockets DLL中的API函數(shù)。②建立Socket,初始化WinSock的動(dòng)態(tài)連接庫(kù)后,需要在服務(wù)器端建立一個(gè)監(jiān)聽(tīng)的Socket,為此可以調(diào)用Socket()函數(shù)用來(lái)建立這個(gè)監(jiān)聽(tīng)的Socket,并定義此Socket所使用的通信協(xié)議。③綁定端口,為服務(wù)器端定義的這個(gè)監(jiān)聽(tīng)的Socket指定一個(gè)地址及端口(Port),這樣客戶端才知道待會(huì)要連接哪一個(gè)地址的哪個(gè)端口,為此我們要調(diào)用bind()函數(shù),該函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。④監(jiān)聽(tīng),當(dāng)服務(wù)器端的Socket對(duì)象綁定完成之后,服務(wù)器端必須建立一個(gè)監(jiān)聽(tīng)的隊(duì)列來(lái)接收客戶端的連接請(qǐng)求。listen()函數(shù)使服務(wù)器端的Socket 進(jìn)入監(jiān)聽(tīng)狀態(tài),并設(shè)定可以建立的最大連接數(shù)。⑤服務(wù)器端接受客戶端的連接請(qǐng)求,當(dāng)Client提出連接請(qǐng)求時(shí),Server端hwnd視窗會(huì)收到Winsock Stack送來(lái)我們自定義的一個(gè)消息,這時(shí),我們可以分析lParam,然后調(diào)用相關(guān)的函數(shù)來(lái)處理此事件。⑥結(jié)束 socket 連接,這一過(guò)程可以由服務(wù)器或客戶機(jī)的任一端啟動(dòng),只要調(diào)用closesocket()就可以,而要關(guān)閉Server端監(jiān)聽(tīng)狀態(tài)的socket,同樣也是利用此函數(shù)。
(二)客戶機(jī)的設(shè)計(jì)
①建立客戶端的Socket,客戶端應(yīng)用程序首先也是調(diào)用WSAStartup() 函數(shù)來(lái)與Winsock的動(dòng)態(tài)連接庫(kù)建立關(guān)系,然后同樣調(diào)用socket() 來(lái)建立一個(gè)TCP或UDP socket(相同協(xié)定的 sockets 才能相通,TCP 對(duì) TCP,UDP 對(duì) UDP)。與服務(wù)器端的socket 不同的是,客戶端的socket 可以調(diào)用 bind() 函數(shù),由自己來(lái)指定IP地址及port號(hào)碼;但是也可以不調(diào)用 bind(),而由 Winsock來(lái)自動(dòng)設(shè)定IP地址及port號(hào)碼。②提出連接申請(qǐng),客戶端的Socket使用connect()函數(shù)來(lái)提出與服務(wù)器端的Socket建立連接的申請(qǐng),函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。
三、基于TCP/IP協(xié)議的網(wǎng)絡(luò)編程
TCP/IP協(xié)議實(shí)際上就是在物理網(wǎng)上的一組完整的網(wǎng)絡(luò)協(xié)議。其中TCP是提供傳輸層服務(wù),而IP則是提供網(wǎng)絡(luò)層服務(wù)。
TCP/IP協(xié)議的核心部分是傳輸層協(xié)議(TCP、UDP),網(wǎng)絡(luò)層協(xié)議(IP)和物理接口層,這三層通常是在操作系統(tǒng)內(nèi)核中實(shí)現(xiàn),因此用戶一般不涉及。編程時(shí),編程界面有兩種形式:一是由內(nèi)核心直接提供的系統(tǒng)調(diào)用;二是使用以庫(kù)函數(shù)方式提供的各種函數(shù)。前者為核內(nèi)實(shí)現(xiàn),后者為核外實(shí)現(xiàn)。用戶服務(wù)要通過(guò)核外的應(yīng)用程序才能實(shí)現(xiàn),所以要使用套接字(socket)來(lái)實(shí)現(xiàn)。
四、系統(tǒng)測(cè)試包括測(cè)試儀器和軟件調(diào)試測(cè)試儀器
Agilent網(wǎng)絡(luò)測(cè)試儀表和AutoRunner自動(dòng)化軟件測(cè)試工具。軟件調(diào)試:由于本系統(tǒng)要實(shí)現(xiàn)的功能比較多,所以在編寫(xiě)程序和調(diào)試時(shí)出現(xiàn)了許多問(wèn)題,不過(guò)經(jīng)過(guò)多次反復(fù)的調(diào)試后還是能把題目所要實(shí)現(xiàn)的功能都能實(shí)現(xiàn)了。
五、結(jié)束語(yǔ)
基于Winsock的編程,可以比較容易的實(shí)現(xiàn)按時(shí)收費(fèi)系統(tǒng)的設(shè)計(jì)。利用Winsock編程的系統(tǒng)界面友好、操作簡(jiǎn)便、功能全面。這也就說(shuō)明Winsock編程的優(yōu)點(diǎn),它將會(huì)成為軟件開(kāi)發(fā)的重要工具!
參考文獻(xiàn):
【關(guān)鍵詞】I/O NIO BIO SOCKET通信 Java 多線程
1 引言
傳統(tǒng)BIO技術(shù)在Socket通信中,系統(tǒng)需要為每一個(gè)鏈接建立一個(gè)線程去處理其請(qǐng)求,隨著客戶端的并發(fā)量不斷增加后,會(huì)導(dǎo)致線程數(shù)量的增加嚴(yán)重影響系統(tǒng)的性能。由于并發(fā)量的增加有可能導(dǎo)致服務(wù)器宕機(jī),嚴(yán)重影響到用戶在使用過(guò)程中的良好體驗(yàn)。為解決傳統(tǒng)BIO的不足,Java 中提供了新的API----NIO和NIO2來(lái)解決由于BIO技術(shù)帶來(lái)的系統(tǒng)瓶頸問(wèn)題。在NIO中系統(tǒng)不再為每一個(gè)用戶請(qǐng)求注冊(cè)一個(gè)線程,而是通過(guò)通道將每一個(gè)鏈接都注冊(cè)到多路復(fù)用器上,通過(guò)多路復(fù)用器對(duì)注冊(cè)在其上的鏈接進(jìn)行輪詢檢查,發(fā)現(xiàn)有鏈接請(qǐng)求才會(huì)開(kāi)啟線程對(duì)其進(jìn)行處理。NIO只在有連接請(qǐng)求時(shí)selector才會(huì)不斷輪詢檢查通道IO操作是否完成,與NIO技術(shù)不同的是AIO技術(shù)是異非步阻塞的。AIO中不再需要多路復(fù)用器,而是由異步非阻塞通道直接操作read和write方法。在客戶端讀寫(xiě)請(qǐng)求發(fā)出后不再等待服務(wù)器的響應(yīng),而是處理完成后由操作系統(tǒng)來(lái)通知應(yīng)用程序。AIO與NIO這兩種技術(shù)都極大地改變了傳統(tǒng)I/O流的不足。
2 Socket基本通信原理介紹
Socket是網(wǎng)絡(luò)通信中的其中一方,用來(lái)接收網(wǎng)絡(luò)通信中雙方其中一方的請(qǐng)求,方便的對(duì)雙方的數(shù)據(jù)進(jìn)行傳輸。Socket通信分有連接的和無(wú)連接,面向連接的Socket通信與面向非連接的Socket通信相比有更高的可靠性和更有效的數(shù)據(jù)傳輸。本文基于有連接的套接字傳輸。
3 BIO、NIO、AIO技術(shù)比較
3.1 BIO技術(shù)簡(jiǎn)介
BIO技術(shù)同步并且阻塞,在這種情況下,服務(wù)器需要為每一個(gè)連接開(kāi)啟一個(gè)線程,只要有客戶端有請(qǐng)求服務(wù)器就需要開(kāi)啟線程去進(jìn)行處理。從客戶端傳來(lái)的每個(gè)請(qǐng)求,服務(wù)器都需要為其創(chuàng)建相應(yīng)的線程去處理其請(qǐng)求。在BIO中,由于其線程的開(kāi)銷很大,適合于運(yùn)用在并發(fā)量小的場(chǎng)景下。其基本模型如圖1所示。
BIO網(wǎng)絡(luò)通信基本步驟:
3.1.1 服務(wù)低端
(1)創(chuàng)建ServerSocket并綁定監(jiān)聽(tīng)端口;
(2)創(chuàng)建Socket用來(lái)接收客戶端請(qǐng)求;
(3)創(chuàng)建輸入輸出流用來(lái)接收客戶端輸入或向客戶端輸出數(shù)據(jù);
(4)關(guān)閉輸入輸出流等系統(tǒng)資源。
3.1.2 客戶端
(1)創(chuàng)建Socket綁定IP地址及端口;
(2)創(chuàng)建輸入輸出流用來(lái)接收服務(wù)器端相應(yīng)或向服務(wù)器端發(fā)送數(shù)據(jù);
(3)關(guān)閉輸入輸出流等系統(tǒng)資源。
3.2 NIO技術(shù)簡(jiǎn)介
NIO技術(shù)即New IO技術(shù),NIO技術(shù)由很多類和組件構(gòu)成,其最重要的由channel、Buffer、和Selectors三個(gè)核心部分組成。
Channel:Java NIO中的通道類似于流,但又不完全相同。既可以從通道中讀取數(shù)據(jù)到Buffer也可以將數(shù)據(jù)從Buffer寫(xiě)入通道中。其中SocketChannel和ServerSocketChannel是NIO中提供的用來(lái)解決Socket通信中的服務(wù)器性能問(wèn)題的。SocketChannel通過(guò)TCP協(xié)議來(lái)讀取網(wǎng)絡(luò)中的數(shù)據(jù),ServerSocketChannel用來(lái)接收鏈接來(lái)的請(qǐng)求以供服務(wù)器相應(yīng)。
Buffer:Buffer是用來(lái)為數(shù)據(jù)提供緩沖區(qū)的。在NIO技術(shù)中,所有的數(shù)據(jù)都必須經(jīng)過(guò)緩沖區(qū)。緩沖區(qū)本質(zhì)上為一塊可讀可寫(xiě)的內(nèi)存塊,NIO中提供了不同數(shù)據(jù)類型的緩沖區(qū)來(lái)處理不同的數(shù)據(jù)請(qǐng)求,和一些基本的方法來(lái)操作緩沖區(qū)中的數(shù)據(jù)。
Selector:Selector是單線程來(lái)處理多個(gè)鏈接請(qǐng)求的關(guān)鍵。在Socket通信中,如果將多個(gè)鏈接請(qǐng)求注冊(cè)到多路復(fù)用器上,就可以用一個(gè)線程來(lái)處理多個(gè)鏈接請(qǐng)求,這樣就提高了Socket通信的效率。NIO通信中的網(wǎng)絡(luò)模型
如圖2所示。
NIO通信基本步驟:
3.2.1 服務(wù)器端
(1)創(chuàng)建多路復(fù)用器Selector 用來(lái)選擇通道;
(2)創(chuàng)建服務(wù)器端通道ServerSocketChannel;
(3)為多路復(fù)用器上注冊(cè)ServerSocketChannel用來(lái)將數(shù)據(jù)通過(guò)通道讀寫(xiě);
(4)申請(qǐng)Buffer存儲(chǔ)數(shù)據(jù);
(5)多路選擇器通過(guò)其key值輪詢檢查通道讀寫(xiě)B(tài)uffer中的數(shù)據(jù)完成其通信過(guò)程。
3.2.2 客舳
(1)創(chuàng)建IP地址和端口號(hào)對(duì)應(yīng)的SocketChannel ;
(2)將SocketChannel設(shè)置為非阻塞模式;
(3)創(chuàng)建多路復(fù)用器Selector注冊(cè)SocketChannel到多路選擇器;
(4)多路選擇器輪詢檢查通道從Buffer中讀寫(xiě)數(shù)據(jù)。
在NIO網(wǎng)絡(luò)通信模型中,客戶端向服務(wù)器端發(fā)起鏈接請(qǐng)求,客戶端將數(shù)據(jù)寫(xiě)入服務(wù)器端Buffer中,然后通過(guò)channel來(lái)從Buffer讀取數(shù)據(jù)或向Buffer寫(xiě)入數(shù)據(jù),對(duì)于通道中的數(shù)據(jù)必須經(jīng)過(guò)選擇器來(lái)向服務(wù)器端的線程發(fā)起請(qǐng)求。而每一個(gè)多路選擇器對(duì)應(yīng)一個(gè)線程模型,這樣一來(lái),只有當(dāng)鏈接請(qǐng)求有效時(shí)服務(wù)器才為客戶端開(kāi)啟線程處理數(shù)據(jù)。對(duì)于同步阻塞的NIO模型中客戶端在向服務(wù)器端發(fā)送完數(shù)據(jù)后會(huì)不斷詢問(wèn)I/O操作是否就緒才能進(jìn)行下一步的操作。此模型中由于不需要為每一個(gè)鏈接請(qǐng)求創(chuàng)建一個(gè)線程,大大減少了線程之間的切換帶來(lái)的巨大開(kāi)銷。提高了I/O的效率,使得在socket網(wǎng)絡(luò)通信更加高效。但由于NIO在鏈接請(qǐng)求中會(huì)不斷詢問(wèn)I/O操作是否完成,其適合運(yùn)用在短鏈接且并發(fā)量大的場(chǎng)合下。
3.3 AIO技術(shù)簡(jiǎn)介
AIO即為異步非阻塞IO,與NIO不同的是,在這種模式下,客戶端發(fā)起一個(gè)鏈接請(qǐng)求后不在詢問(wèn)服務(wù)器端的I/O操作是否完成便立即返回。在使用過(guò)程中只需直接調(diào)用異步的read和write方法來(lái)讀寫(xiě)數(shù)據(jù),在讀寫(xiě)過(guò)程完畢后由操作系統(tǒng)主動(dòng)通知應(yīng)用程序讀寫(xiě)操作是否完成。由AIO的讀寫(xiě)過(guò)程可以看出,因?yàn)樵谧x寫(xiě)完成后客戶端不需要再詢問(wèn)服務(wù)器端是完成了I/O操作,所以AIO非常適合于運(yùn)用在那些并發(fā)量大且長(zhǎng)連接的請(qǐng)求。AIO模型如圖3所示。
AIO中有幾個(gè)比較重要的類:
AsynchronousServerSocket:用來(lái)創(chuàng)建服務(wù)器端的ServerSocket并綁定地址監(jiān)聽(tīng)端口。
AsynchronousSocketChannel:Socket在異步非阻塞通信中的應(yīng)用,用來(lái)表示一個(gè)連接請(qǐng)求,并用來(lái)在通信過(guò)程中傳遞數(shù)據(jù)。
AsynchronousChannelGroup:異步通道的分組管理,目的是問(wèn)了資源共享。創(chuàng)建AsynchronousChannelGroup時(shí)需要為其綁定一個(gè)線程執(zhí)行器對(duì)象,這個(gè)線程池主要完成兩個(gè)任務(wù):處理I/O事件和派發(fā)CompletionHandler。在創(chuàng)建AsynchronousServerSocket時(shí)需要為其綁定一個(gè)AsynchronousChannelGroup。通過(guò)AsynchronousServerSocket創(chuàng)建的AsynchronousChannelGroup將屬于同一組,共享其中資源。
CompletionHandler:用于定義在異步IO操作完成后的回調(diào)接口。
3.3.1 服務(wù)器端
(1)創(chuàng)建AsynchronousChannelGroup;
(2)創(chuàng)建AsynchronousServerSocketChannel并將它綁定在AsynchronousChannelGroup上;
(3)為AsynchronousServerSocketChannel對(duì)象綁定端口號(hào);
(4)調(diào)用accept()接收客戶端請(qǐng)求實(shí)現(xiàn)CompletionHandler接口調(diào)用讀寫(xiě)方法進(jìn)行讀寫(xiě)數(shù)據(jù)。
3.3.2 客戶端
(1)創(chuàng)建AsynchronousSocketChannel;
(2)綁定IP地址和端口號(hào)鏈接服務(wù)低端實(shí)現(xiàn)CompletionHandler接口中方法直接進(jìn)行讀寫(xiě)數(shù)據(jù)。
3.4 各種I/O技g比較分析
BIO:同步阻塞I/O,使用難度簡(jiǎn)單,可靠性低,適用于鏈接并發(fā)量小的架構(gòu)且對(duì)服務(wù)器資源依賴更高。
NIO:同步非阻塞I/O,使用難度復(fù)雜,可靠性高,適用于并發(fā)量大且鏈接較短的場(chǎng)景。
AIO:異步非阻塞I/O,使用難度一般,可靠性高,適用于并發(fā)量大且鏈接長(zhǎng)的場(chǎng)景。
4 結(jié)語(yǔ)
傳統(tǒng)網(wǎng)絡(luò)在IO處理方面存在著性能不足的問(wèn)題,NIO與AIO包的引入解決了傳統(tǒng)IO性能瓶頸問(wèn)題,使的Java在網(wǎng)絡(luò)通信中有了更搞得效率。對(duì)于不同的IO來(lái)說(shuō),它們各自有有不同的應(yīng)用領(lǐng)域。對(duì)于那些并發(fā)量小,數(shù)據(jù)傳輸量小的場(chǎng)景來(lái)說(shuō),傳統(tǒng)的BIO完全可以勝任其工作。相反對(duì)于那些對(duì)性能要求比較高,并發(fā)量大且對(duì)IO的要求比較搞得場(chǎng)合來(lái)說(shuō)應(yīng)該選擇NIO或者BIO技術(shù)。如果客戶端與服務(wù)器在長(zhǎng)連接的情況下選擇AIO相比NIO會(huì)更加高效一些。相反,如果是短連接的應(yīng)用領(lǐng)域,則推薦使用NIO。
參考文獻(xiàn)
[1]范寶德,馬建生.Java非阻塞通訊研究[J].微計(jì)算機(jī)信息,2006,22(12-3):116-119.
[2]劉邦桂,李正凡.用Java實(shí)現(xiàn)流式Socket通訊[J].華東交通大學(xué)學(xué)報(bào),2007,24(05):110-112.
[3]封瑋,周世平.基于Java NIO的非阻塞通信的研究與實(shí)現(xiàn)[J].計(jì)算機(jī)系統(tǒng)應(yīng)用,2004(09):32-35.
[5]任小強(qiáng),陳金鷹,李文彬,胡波.網(wǎng)絡(luò)通信之Java Socket多線程通信[J].信息通信,2015(06):206-207.
作者簡(jiǎn)介
王少輝(1992-),男,山西省平順縣人。碩士學(xué)位?,F(xiàn)為山東大學(xué)(威海)機(jī)電與信息工程學(xué)院學(xué)生。主要研究方向?yàn)殡娐放c系統(tǒng)。
陳曉鵬(1992-),男,天津市人。碩士學(xué)位?,F(xiàn)為山東大學(xué)(威海)機(jī)電與信息工程學(xué)院學(xué)生。主要研究方向?yàn)殡娐放c系統(tǒng)。
關(guān)鍵詞:語(yǔ)音集群通信;移動(dòng)網(wǎng)絡(luò);SSL Socket;opus
中圖分類號(hào):TP393 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2016)27-0014-04
Abstract: The paper analyzes the specific business demands and technical requirements of the industry users in the voice cluster communication, and gives a solution of voice cluster communication system for the mobile network environment. It designs the underlying transmission framework based on SSL Socket secure link and data transmission format, puts forward the basic method to establish interactive applications, according to the features of mobile network environment, it analyzes and uses the reasonable audio codec library, designs audio mixing algorithm, solves the core problem of the voice cluster communication system.
Key words: voice cluster communication; mobile network; SSL Socket; opus
隨著公共網(wǎng)絡(luò)服務(wù)和多媒體通信技術(shù)的發(fā)展,利用移動(dòng)終端進(jìn)行語(yǔ)音實(shí)時(shí)通信已成為民用市場(chǎng)或行業(yè)應(yīng)用的熱門(mén)業(yè)務(wù)[1]。相比較傳統(tǒng)的電話語(yǔ)音業(yè)務(wù),基于IP網(wǎng)絡(luò)的語(yǔ)音通信系統(tǒng)具有跨地域、低成本、高質(zhì)量、可定制等優(yōu)勢(shì),且隨著移動(dòng)網(wǎng)絡(luò)建設(shè)的不斷升級(jí),語(yǔ)音通信系統(tǒng)的通話效果和時(shí)延已與電話語(yǔ)音的差別不大。目前,市場(chǎng)上主流的語(yǔ)音通信應(yīng)用有兩類:一是采用頻道、群組或聊天室形式的多人語(yǔ)音通信,如yy語(yǔ)音、qt語(yǔ)音等;二是采用一對(duì)一通話模式的IP網(wǎng)絡(luò)電話,如Skype等。微信作為國(guó)內(nèi)影響力較大的即時(shí)通信軟件,也提供了一對(duì)一的實(shí)時(shí)語(yǔ)音功能。
對(duì)于滿足專有行業(yè)或企業(yè)的語(yǔ)音集群通信業(yè)務(wù)[2],通常還需要考慮以下需求:1)群組通話是集群通信的主要業(yè)務(wù)模式,具有群組信道共享,避免通信互擾等服務(wù)要求;2)對(duì)于室外作業(yè)多采用移動(dòng)網(wǎng)絡(luò),而非連接到有線網(wǎng)絡(luò)共享的AP熱點(diǎn),網(wǎng)絡(luò)質(zhì)量受限。3)VPDN專網(wǎng)服務(wù)支持或其他網(wǎng)絡(luò)安全要求;4)在語(yǔ)音業(yè)務(wù)基礎(chǔ)上,提供文本通信、文件傳輸?shù)榷ㄖ乒δ埽?)對(duì)語(yǔ)音、文本、共享文件等關(guān)鍵信息的私有存儲(chǔ)或全業(yè)務(wù)存儲(chǔ)。因此,在設(shè)計(jì)開(kāi)發(fā)基于移動(dòng)網(wǎng)絡(luò)的語(yǔ)音集群通信系統(tǒng)時(shí),應(yīng)充分考慮行業(yè)應(yīng)用特點(diǎn),預(yù)留必要的業(yè)務(wù)應(yīng)用接口。
1 基本傳輸框架
語(yǔ)音集群通信系統(tǒng)基于標(biāo)準(zhǔn)的客戶端-服務(wù)器通信模型設(shè)計(jì)。客戶端與服務(wù)器之間的數(shù)據(jù)傳輸采用TCP/UDP兩種協(xié)議,分別適用不同的網(wǎng)絡(luò)環(huán)境。當(dāng)移動(dòng)網(wǎng)絡(luò)質(zhì)量較好時(shí),可以根據(jù)設(shè)置使音頻數(shù)據(jù)通過(guò)TCP通道傳輸,保證通話語(yǔ)音的完整性和可靠性;當(dāng)網(wǎng)絡(luò)條件較差時(shí),使用UDP通道傳輸可以避免TCP傳輸多次握手導(dǎo)致的網(wǎng)絡(luò)擁塞,實(shí)現(xiàn)低延時(shí)傳輸,保證系統(tǒng)的可用性。語(yǔ)音通信過(guò)程中的控制信令和通信信令則是基于TCP協(xié)議可靠傳輸[3]。
1.1 構(gòu)建安全通道
安全套接層(Secure Socket Layer,SSL)是Netscape公司研發(fā)的用于在IP網(wǎng)絡(luò)上實(shí)現(xiàn)數(shù)據(jù)安全傳輸?shù)膶S袇f(xié)議,通過(guò)數(shù)據(jù)加密技術(shù)確保數(shù)據(jù)在網(wǎng)絡(luò)傳輸過(guò)程中不會(huì)被截取及竊聽(tīng)。SSL及其后續(xù)發(fā)展的傳輸層安全(Transport Layer Security,TLS)提供了傳輸層的數(shù)據(jù)完整性保護(hù),包括身份認(rèn)證、協(xié)商加密算法、交換加密密鑰等[4]。OpenSSL是互聯(lián)網(wǎng)上適用性最廣泛的SSL密碼庫(kù)之一,提供了多種編程語(yǔ)言的庫(kù)支持。本系統(tǒng)引入OpenSSL 1.0.2方法庫(kù),將TCP Socket替換為SSL Socket,對(duì)傳輸層協(xié)議進(jìn)行鏈路安全保護(hù)[5]。
SSL Socket的建立流程是:
1)客戶端請(qǐng)求建立SSL Socket連接,并向服務(wù)器發(fā)送SSL版本、加密參數(shù)等必要信息。服務(wù)器返回自身的SSL版本、加密參數(shù)、安全證書(shū)等必要信息。客戶端向服務(wù)器提供認(rèn)證證書(shū)。
2)客戶端驗(yàn)證服務(wù)器證書(shū)后,生成pre-master secret,并用公鑰加密后發(fā)送給服務(wù)器。服務(wù)器對(duì)證書(shū)進(jìn)行認(rèn)證,通過(guò)后用私鑰解密pre-master secret,并生成master secret。
3)通信雙方通過(guò)master secret生成會(huì)話密鑰,完成SSL Socket的創(chuàng)建,之后的通信數(shù)據(jù)將通過(guò)會(huì)話密鑰加密傳輸。在本系統(tǒng)中,會(huì)話密鑰采用TLSv1 AES256-SHA加密。
由于UDP是面向無(wú)連接的傳輸協(xié)議,為保證數(shù)據(jù)傳輸安全,在發(fā)送函數(shù)sendto()之前加入encrypt(),這里可采用與TLSv1 AES256-SHA強(qiáng)度相當(dāng)?shù)募用芩惴ㄟM(jìn)行數(shù)據(jù)加密。接收側(cè)在recvfrom()之后加入decrypt(),進(jìn)行數(shù)據(jù)解密。
1.2 數(shù)據(jù)傳輸格式
本系統(tǒng)的數(shù)據(jù)報(bào)文格式如圖1所示。客戶端與服務(wù)器之間遵循此格式發(fā)送交互信息,其中報(bào)文頭Ptrfix包含1個(gè)字節(jié)的類型信息,區(qū)別信令數(shù)據(jù)或語(yǔ)音數(shù)據(jù),提供長(zhǎng)度信息進(jìn)行組包校驗(yàn)。載荷部分Data為實(shí)際交互數(shù)據(jù),采用TLSv1 AES256-SHA或其他算法加密。由于UDP協(xié)議僅用于發(fā)送語(yǔ)音數(shù)據(jù),其數(shù)據(jù)傳輸格式為圖1的Payload部分,最大長(zhǎng)度為UDP分片的理論最大長(zhǎng)度65507B[6]。
2 應(yīng)用流程
2.1 用戶接入
本系統(tǒng)的接入流程如圖2所示,主要包括:
1)建立連接:客戶端向服務(wù)器請(qǐng)求建立用于控制信令和TCP數(shù)據(jù)通信的SSL Socket長(zhǎng)連接。
2)版本確認(rèn):客戶端與服務(wù)器相互發(fā)送版本信息,包括軟件版本、操作系統(tǒng)版本、其他備注信息等,雙方根據(jù)版本判斷系統(tǒng)訪問(wèn)的兼容性。
3)用戶登錄:客戶端向服務(wù)器發(fā)送登錄認(rèn)證信息,包括登錄賬戶、登錄密碼,或是用于登錄認(rèn)證的證書(shū)信息。
4)密鑰交換:該步驟為可選項(xiàng),由于SSL Socket完成了TCP連接的密鑰交換,如系統(tǒng)支持UDP傳輸,則需要服務(wù)器發(fā)送UDP加密的密鑰信息給客戶端。
5)群組信息:服務(wù)器向請(qǐng)求登錄的客戶端發(fā)送群組的狀態(tài)信息,包括群組ID、名稱、描述、拓?fù)潢P(guān)系,以及群組中用戶的在線狀況和當(dāng)前狀態(tài)。
6)用戶信息:服務(wù)器向其他客戶端發(fā)送新登錄用戶的狀態(tài)信息,通知該用戶已上線。
7)心跳信息:客戶端完成基本登錄流程,通過(guò)發(fā)送周期性的心跳報(bào)文,維持長(zhǎng)連接狀態(tài)。根據(jù)實(shí)際網(wǎng)絡(luò)狀況可調(diào)整心跳報(bào)文的發(fā)送周期,如部署地域的網(wǎng)絡(luò)狀態(tài)較好,可采用3至5秒的發(fā)送間隔。
2.2 信息響應(yīng)
作為集群通信系統(tǒng),信息響應(yīng)的基本單位是群組。每個(gè)群組的數(shù)據(jù)處理和信息狀態(tài)相對(duì)于其他群組保持獨(dú)立,即群組之間的數(shù)據(jù)和資源是隔離的,這里通過(guò)建立用戶信息的HashMap結(jié)構(gòu)hmUsers和群組信息的HashMap結(jié)構(gòu)hmGroups進(jìn)行數(shù)據(jù)調(diào)用和處理。調(diào)用用戶或群組對(duì)象時(shí),需通過(guò)ReadWriteLock進(jìn)行資源鎖定,保護(hù)線程安全[7]??紤]到多線程存在資源切換和鎖定的開(kāi)銷,在設(shè)計(jì)上主要對(duì)不同類型或分組的任務(wù)建立線程,如圖3所示。其中:
1)接收線程主要響應(yīng)網(wǎng)卡資源,監(jiān)聽(tīng)服務(wù)端口,接收由客戶端發(fā)送的業(yè)務(wù)數(shù)據(jù)或信令信息,如果是心跳消息,則直接返回響應(yīng);否則,將通過(guò)hmUsers和hmGourps查找所屬群組,并將數(shù)據(jù)推送到指定群組的消息緩沖區(qū)中。
2)處理線程主要響應(yīng)和實(shí)現(xiàn)具體業(yè)務(wù)。每個(gè)群組對(duì)象在實(shí)例化過(guò)程中都會(huì)創(chuàng)建本群組的處理線程和數(shù)據(jù)緩沖區(qū),通過(guò)提取緩沖區(qū)中的數(shù)據(jù)并解析,線程將信令或其他業(yè)務(wù)數(shù)據(jù)交給業(yè)務(wù)響應(yīng)函數(shù)處理,語(yǔ)音數(shù)據(jù)交給數(shù)據(jù)響應(yīng)函數(shù)處理,并將處理后的數(shù)據(jù)發(fā)送給指定客戶端。在處理線程中,可分模塊響應(yīng)語(yǔ)音業(yè)務(wù)以外的請(qǐng)求,對(duì)預(yù)留接口進(jìn)行功能實(shí)現(xiàn)。
3)存儲(chǔ)線程主要響應(yīng)數(shù)據(jù)I/O操作。本系統(tǒng)對(duì)獨(dú)占性的I/O操作采用單線程序列化處理,即所有群組的語(yǔ)音數(shù)據(jù)通過(guò)統(tǒng)一的緩沖序列順序解碼、混音和寫(xiě)入文件,降低線程切換的開(kāi)銷。為提高I/O讀寫(xiě)效率,語(yǔ)音數(shù)據(jù)不會(huì)立即寫(xiě)入文件,而是積累時(shí)長(zhǎng)2秒(可根據(jù)實(shí)際情況配置)的數(shù)據(jù)包后批量寫(xiě)入。
3 語(yǔ)音處理
3.1 音頻編解碼器選型
目前,國(guó)內(nèi)4G LTE網(wǎng)絡(luò)建設(shè)日趨完善,基于移動(dòng)網(wǎng)絡(luò)的語(yǔ)音通信技術(shù)已擺脫GSM時(shí)期小于16kbps的窄帶傳輸限制。在音頻編解碼技術(shù)選型上,更多考慮的是適應(yīng)16kbps到64kbps區(qū)間的高品質(zhì)音頻編解碼算法。
本系統(tǒng)主要對(duì)Opus、Speex、AMR-WB和G.722.1四種編解碼方案進(jìn)行比較。Opus是在Skype的SILK編解碼器和的CELT編解碼器基礎(chǔ)上發(fā)展的開(kāi)源編解碼方案,已形成RFC 6716標(biāo)準(zhǔn),具有靈活的帶寬適應(yīng)性。Speex是基于CELP發(fā)展的音頻編解碼方案,但根據(jù)Speex官網(wǎng)提示,Opus的性能已在各方面優(yōu)于Speex。AMR-WB(G.722.2)被廣泛應(yīng)用在WCDMA的話音業(yè)務(wù)上,其VBR特性可以較好的適應(yīng)網(wǎng)絡(luò)帶寬變化。G.722.1提供了優(yōu)于G.722的24kbps和32kbps音頻編碼壓縮。
根據(jù)Opus-Codec給出的音頻編碼質(zhì)量統(tǒng)計(jì)對(duì)比(圖4),以及兩份Google組織的主觀評(píng)測(cè)報(bào)告[9][10],在同等比特率條件下,Opus編碼能夠達(dá)到或超過(guò)AMR-WB、G.722.1的編碼質(zhì)量。且考慮到國(guó)內(nèi)各地區(qū)移動(dòng)網(wǎng)絡(luò)的傳輸質(zhì)量差異較大,支持6kbps到510kbps比特率和VBR技術(shù)的Opus編解碼器可為不同網(wǎng)絡(luò)環(huán)境提供更穩(wěn)定的語(yǔ)音通話效果。
3.2 Opus編解碼實(shí)例
Opus是基于C語(yǔ)言編寫(xiě)的音頻編解碼器,Windows平臺(tái)可直接加載Opus源代碼進(jìn)行函數(shù)調(diào)用,Android平臺(tái)還需要對(duì)源代碼進(jìn)行NDK編譯,生成供Java語(yǔ)言調(diào)用的so庫(kù)。
使用Opus進(jìn)行語(yǔ)音編碼的代碼片段如下:
OpusEncoder *opus_enc = opus_encoder_create(16000,1,OPUS_APPLICATION_VOIP, &err);
…
opus_encoder_ctl(opus_enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
opus_encoder_ctl(opus_enc, OPUS_SET_BITRATE(24000));
opus_encoder_ctl(opus_enc, OPUS_SET_VBR(1));
opus_encoder_ctl(opus_enc, OPUS_SET_FORCE_CHANNELS(1));
opus_encoder_ctl(opus_enc, OPUS_SET_DTX(0));
…
while (total_length - input_pos > frame_bytes) { //僅處理采集片段,而非長(zhǎng)期占用線程
…
get_data(input_buf, input_pos);
num = opus_encode(opus_enc, input_buf, frame_size, output_buf, max_output_size);
send_data(output_buf, num);
… }
…
opus_encoder_destroy(opus_enc);
3.3 混音流程
多路語(yǔ)音的數(shù)據(jù)混音是語(yǔ)音集群通信的核心功能。通過(guò)混音算法和調(diào)平參數(shù),同一群組內(nèi)的多路通話語(yǔ)音將根據(jù)逐幀數(shù)據(jù)的時(shí)間戳進(jìn)行合幀處理,形成一路語(yǔ)音數(shù)據(jù),送入聲卡緩沖區(qū)進(jìn)行放音。不論語(yǔ)音數(shù)據(jù)是以8位、16位或浮點(diǎn)形式存儲(chǔ),在多路混音時(shí)僅做邏輯疊加運(yùn)算將會(huì)導(dǎo)致數(shù)據(jù)越界,必須先通過(guò)調(diào)平參數(shù)進(jìn)行數(shù)據(jù)調(diào)整,之后根據(jù)疊加系數(shù)按比例縮減,保證合幀后的語(yǔ)音數(shù)據(jù)不會(huì)越界[11][12]。
混音操作的代碼片段如下:
mixlocker.lock();
…
foreach(audio_source as, mixlist) {
source_buf = as.getbuf();
level = volume_level * generate_adjust(); // 設(shè)置調(diào)平參數(shù)
…
for (unsigned int i = 0; i < sample_num; i++)
mix_buf[i] += source_buf[i] * level;
… }
…
for (unsigned int i = 0; i < sample_num; i++) // 數(shù)據(jù)越界保護(hù)
mix_buf[i] = bound(mix_buf[i], -32768, 32767);
…
mixlocker.lock();
4 實(shí)驗(yàn)與分析
本系統(tǒng)分別在10M專線、聯(lián)通4G、移動(dòng)4G、電信4G等網(wǎng)絡(luò)條件下進(jìn)行測(cè)試,測(cè)試結(jié)果如表1所示。
在不同網(wǎng)絡(luò)環(huán)境下,語(yǔ)音數(shù)據(jù)傳輸?shù)钠骄訒r(shí)在可接受范圍,UDP丟包率小于2.2%,網(wǎng)絡(luò)抖動(dòng)小于20ms。通過(guò)主觀測(cè)試,語(yǔ)音通話連貫,無(wú)連續(xù)丟幀,可懂性良好。因此,語(yǔ)音集群通信系統(tǒng)在傳輸延遲、網(wǎng)絡(luò)抖動(dòng)等方面可以較好滿足語(yǔ)音集群和實(shí)時(shí)通信要求。在編碼處理方面,優(yōu)于國(guó)際電聯(lián)G.711語(yǔ)音編碼的90kbps帶寬占用。
5 結(jié)束語(yǔ)
本文主要根據(jù)移動(dòng)網(wǎng)絡(luò)環(huán)境下的語(yǔ)音集群通信需求,給出一套可行的系統(tǒng)解決方案,并對(duì)其中底層鏈路、應(yīng)用構(gòu)建、核心編碼處理等關(guān)鍵環(huán)節(jié)進(jìn)行詳細(xì)闡述。在實(shí)際應(yīng)用過(guò)程中,不同領(lǐng)域的業(yè)務(wù)單位可能對(duì)系統(tǒng)的數(shù)據(jù)存儲(chǔ)、用戶負(fù)載、網(wǎng)絡(luò)安全有更高的業(yè)務(wù)需求,可以通過(guò)合理配置磁盤(pán)陣列、負(fù)載均衡、安全網(wǎng)關(guān)、服務(wù)器等硬件設(shè)備或軟件服務(wù)來(lái)實(shí)現(xiàn)。
參考文獻(xiàn):
[1] 2015年中國(guó)移動(dòng)語(yǔ)音社交應(yīng)用行業(yè)研究報(bào)告[R]. 上海: iResearch, 2015:19-23
[2] 王芳. 數(shù)字集群通信系統(tǒng)的構(gòu)成及功能[J]. 電信網(wǎng)技術(shù), 2005(2):9-12.
[3] Chauncey D, Kuliner M. Secure wireless communications system and related method: WO, US7987363[P]. 2011.
[4] 曾強(qiáng). 網(wǎng)絡(luò)安全協(xié)議SSL原理及應(yīng)用[D]. 天津:天津大學(xué), 2005.
[5] 秦貞虎. 基于OpenSSL開(kāi)發(fā)的聊天工具的設(shè)計(jì)與實(shí)現(xiàn)[D]. 成都:電子科技大學(xué), 2013.
[6] 李一鳴, 任勇毛, 李俊. 基于UDP的傳輸協(xié)議性能比較與分析[J]. 計(jì)算機(jī)應(yīng)用研究, 2010, 27(10):3906-3910.
[7] 趙興. 基于VoIP技術(shù)的無(wú)線語(yǔ)音通信系統(tǒng)設(shè)計(jì)[D].長(zhǎng)沙: 湖南大學(xué), 2011.
[8] opus-codec. Quality vs Bitrate[EB/OL]. http:///comparison/.
[9] opus-codec. Google listening tests[EB/OL]. http:///comparison/GoogleTest1.pdf.
[10] opus-codec. Google listening tests[EB/OL]. http:///comparison/GoogleTest2.pdf.