前言:一篇好文章的誕生,需要你不斷地搜集資料、整理思路,本站小編為你收集了豐富的socket通信主題范文,僅供參考,歡迎閱讀并收藏。
關(guān)鍵詞:網(wǎng)絡(luò)通信;Tcp/IP;socket
中圖分類號:TP393 文獻(xiàn)標(biāo)識碼:A 文章編號: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é)院開發(fā)的,即便網(wǎng)絡(luò)遭到了大部分破壞,TCP/IP仍然能夠維持有效的通信。在所有的OS中都集成了TCP/IP協(xié)議,TCP/IP是實(shí)現(xiàn)網(wǎng)際互聯(lián)的基礎(chǔ),同時TCP/IP協(xié)議也具備了可擴(kuò)展性和可靠性的需求。在現(xiàn)今的網(wǎng)絡(luò)環(huán)境下,能有效地保證點(diǎn)對點(diǎn)信息的傳輸和安全是網(wǎng)絡(luò)應(yīng)用的需要。
1.2 Socket的作用
Socket通常稱作"套接字",應(yīng)用程序通常通過"套接字"向網(wǎng)絡(luò)發(fā)出請求或應(yīng)答網(wǎng)絡(luò)請求,Socket是面向C/S模型而設(shè)計的,針對客戶和服務(wù)器程序提供不同的socket系統(tǒng)調(diào)用。客戶隨機(jī)申請一個socket (相當(dāng)于在電話系統(tǒng)中一個想打電話的人可以在任何一臺入網(wǎng)電話上撥號呼叫),系統(tǒng)為任何有聯(lián)網(wǎng)需求及信息需求的客戶分配一個socket號;服務(wù)器擁有全局socket號 ,客戶端socket號相當(dāng)于電話系統(tǒng)中的各個公共電話,而服務(wù)器端相當(dāng)于總機(jī)電話。
1.3 Socket實(shí)現(xiàn)網(wǎng)絡(luò)通信的基本原理
TCP/IP系統(tǒng)中的端口號是一個16位的數(shù)字,它的范圍是0~65535??蛻艉头?wù)器必須事先約定所使用的端口。在C/S開發(fā)模式中,網(wǎng)絡(luò)中的各個節(jié)點(diǎn)都有獨(dú)有的IP地址用來表示各節(jié)點(diǎn)的身份,服務(wù)器預(yù)留端口作為鏈接的接口,而Socket通過在它們之間兩兩建立鏈路的方式,來實(shí)現(xiàn)網(wǎng)絡(luò)中點(diǎn)對點(diǎn)的通信。
Socket分為客戶端Socket對象及ServerSocket類對象,它們類庫位于包中。ServerSocket用于服務(wù)器端,Socket是建立網(wǎng)絡(luò)連接時使用的。在連接成功時,應(yīng)用程序兩端都會產(chǎn)生一個Socket實(shí)例,通過這個實(shí)例,完成所需的會話。對于一個網(wǎng)絡(luò)連接來說,套接字是平等的,并沒有差別,不因為在服務(wù)器端或在客戶端而產(chǎn)生不同級別。不管是Socket還是ServerSocket它們的工作都是通過Java語言提供的SocketImpl接口及其方法來完成網(wǎng)絡(luò)編程所需的網(wǎng)絡(luò)通信功能。 這套API使Java程序員不用考慮復(fù)雜的網(wǎng)絡(luò)協(xié)議以及底層的數(shù)據(jù)傳輸方式,而直接用面向?qū)ο蟮乃枷雭韺?shí)現(xiàn)網(wǎng)絡(luò)傳輸。
2 建立C/S基于TCP/IP Socket通信模型
2.1 客戶端Socket通信過程
Socket是網(wǎng)絡(luò)上運(yùn)行的兩個程序間雙向通信的一端,它既可以接受請求,也可以發(fā)送請求,利用它可以較為方便地編寫網(wǎng)絡(luò)上數(shù)據(jù)的傳遞。在Java中,利用Socket類的方法,就可以實(shí)現(xiàn)兩臺計算機(jī)之間的通信。客戶端Socket通信整個過程主要有4個階段:(1)請求階段;(2)建立連接階段;(3)通信階段;(4)關(guān)閉階段。
2.2 TCP Socket C/S模型及工作流程
TCP/IP本身是一個工業(yè)標(biāo)準(zhǔn),在C/S模式中,主要通過三次握手,實(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收到請求后要確認(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會再發(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)
客戶端通過Socket連接服務(wù)器,主要過程經(jīng)過4個階段,具體代碼如下:
Socket PersonalConnect=new Socket(“服務(wù)器IP”,PORT);
……;//socket、bufferedReader及PrintStream對象作為線程成員
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對象作為線程成員
c_s.close();}
對于TCP C/S模式中兩端的Java類中,主要有三個類成員它們分別是Socket對象、BufferedReader對象、PrintStream對象,而服務(wù)器端還有個ServerSocket對象。其中Socket及ServerSocket對象完成連接兩端的請求綁定套接的過程,而輸入輸出流在套接中進(jìn)行通信。如果一端要表達(dá)消息發(fā)送結(jié)束,則可以關(guān)閉其輸出流,但并不關(guān)閉套接字對象,這就是“半關(guān)閉”的作用。
3.2 通過線程管理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();
//用來保存客戶端Socket對象
ServerSocket myTcpServer=new ServerSocket(“服務(wù)器端口“);
Socket c_s = myTcpServer.accept();
socketList.add(c_s);//響應(yīng)客戶端Socket請求,并保存Socket對象進(jìn)入socketList
new Thread(new ServerThread(c_s)).start(); ServerThread.java文件的主要代碼:
public class ServerThread implements Runnable
{……;//socket、bufferedReader及PrintStream對象作為線程屬性
public ServerThread(Socket s) throws IOException {
this.c_s = s; }}
利用線程在一個List結(jié)構(gòu)中實(shí)現(xiàn)多個不同的Socket對象的管理即每一個客戶端通過線程實(shí)現(xiàn)套接字對象的管理與通信。Accept()方法用于產(chǎn)生”阻塞”,直到接受到一個連接,并且返回一個客戶端的Socket對象實(shí)例?!弊枞笔且粋€術(shù)語,它使程序運(yùn)行暫時”停留”在這個地方,直到一個會話產(chǎn)生,然后程序繼續(xù)。
為了更好地實(shí)現(xiàn)對于客戶端對象的管理,實(shí)現(xiàn)對于其Socket與其消息的管理,我們也可以通過HaspMap對象,來實(shí)現(xiàn)單一客戶K與其多條消息V的關(guān)系映射。實(shí)現(xiàn)“Kclient+Vserver“的形式傳送消息即“用戶名:消息類容”的形式輸入,服務(wù)器端以HaspMap解析讀入的消息對象,辨識消息的發(fā)送者與其消息之間的映射關(guān)系,實(shí)現(xiàn)點(diǎn)對點(diǎn)的消息傳輸。
4 結(jié)束語
本文主要介紹的內(nèi)容僅適合于TCP/IP網(wǎng)絡(luò)協(xié)議。Java Socket 可以實(shí)現(xiàn)TCP協(xié)議在兩臺計算機(jī)之間建立可靠連接。連接安全可靠,數(shù)據(jù)不會丟失,Java平臺還提供了更安全的SSLSocket類,SSLSocket通信是對SOCKET通信的拓展。在Socket基礎(chǔ)上添加了一層安全性保護(hù),提供了更高的安全性,包括身份驗證、數(shù)據(jù)加密以及完整性驗證。其中身份驗證用于數(shù)字證書的發(fā)放和應(yīng)用。數(shù)據(jù)加密可以通過密鑰防止消息傳遞過程中被別人監(jiān)聽而造成的損失。所以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ù)共享,為社會公眾及自身提供一體化的高效、優(yōu)質(zhì)、廉潔的管理和服務(wù)的過程實(shí)現(xiàn)住房公積金貸款“一窗辦結(jié)”、“一次告知,集中收件”的受理原則,“一次審核、全程通用” 的審查原則?;窗彩泄e金中心與淮安市登記中心系統(tǒng)要實(shí)現(xiàn)系統(tǒng)數(shù)據(jù)的共享,但系統(tǒng)架構(gòu)、數(shù)據(jù)庫不一樣,給數(shù)據(jù)交互帶來一定難度。
常用的數(shù)據(jù)交互技術(shù)有Socket和WebSevice。
(1)Socket交互,首先建立Socket數(shù)據(jù)交互包,即兩個系統(tǒng)間建立傳遞數(shù)據(jù)的數(shù)據(jù)包,接受到數(shù)據(jù)包的一端根據(jù)制定的規(guī)則完成交互。該方式優(yōu)點(diǎn)是點(diǎn)對點(diǎn)之間的數(shù)據(jù)交互安全性高。
(2)WebSevice交互以WebSevice 的方式實(shí)現(xiàn)服務(wù)器間的交互,系統(tǒng)間互為客戶端向服務(wù)器端發(fā)送請求實(shí)現(xiàn)交互。該方式的優(yōu)點(diǎn)是實(shí)時交互效率高;缺點(diǎn)是由于服務(wù)器端作為客戶端,有可能出現(xiàn)安全問題。公積金中心采用C/S架構(gòu)、Oracle數(shù)據(jù)庫,登記中心是C/S架構(gòu)\SQL Server數(shù)據(jù)庫,雙方業(yè)務(wù)系統(tǒng)均在內(nèi)網(wǎng),且對安全性都有很高的要求,不適合直接互聯(lián),雙方均租用電信專線實(shí)現(xiàn)網(wǎng)絡(luò)互聯(lián)、防火墻等設(shè)備保障系統(tǒng)安全。所有采用了Socket方式實(shí)現(xiàn)數(shù)據(jù)交互。
Socket是建立在傳輸層協(xié)議上的一種套接字規(guī)范,它定義兩臺計算機(jī)間進(jìn)行通信的規(guī)范,套接字屏蔽了底層通信軟件和具體操作系統(tǒng)的差異,使得任何兩臺安裝了TCP協(xié)議軟件和實(shí)現(xiàn)了套接字規(guī)范的計算機(jī)之間的通信成為可能。Socket通常用來實(shí)現(xiàn)客戶方和服務(wù)方的連接。客戶程序可以向Socket寫請求,服務(wù)器將處理此請求,然后通過Socket將結(jié)果返回給用戶。
2 制定數(shù)據(jù)交換規(guī)范,統(tǒng)一數(shù)據(jù)交換格式
由于公積金系統(tǒng)和登記中心系統(tǒng)的是在不同的數(shù)據(jù)庫開發(fā)的軟件,我們首先要做的事情是,將數(shù)據(jù)的數(shù)據(jù)映射成為統(tǒng)一的數(shù)據(jù)格式,再由統(tǒng)一的數(shù)據(jù)格式映射成為本系統(tǒng)的數(shù)據(jù)格式。
對于數(shù)據(jù)交互格式我們制定了兩種交互方式。
(1)對數(shù)據(jù)統(tǒng)一編碼,對每個交互內(nèi)容確定數(shù)據(jù)項名稱、類型、長度、位置、備注。發(fā)送的信息組合為一個長字符串。接受方通過對字符串的解碼,提取相應(yīng)的信息,這種模式比較適合交互內(nèi)容較少的信息。
(2)內(nèi)容較多時,可以通過XML進(jìn)行組織數(shù)據(jù)。
3 基于Delphi的Socket交互的設(shè)計
在Delphi中,對Windows Socket進(jìn)行了有效的封裝,使得可以很方便地實(shí)現(xiàn)通信。在Delphi環(huán)境下實(shí)現(xiàn)Socket技術(shù)通訊,服務(wù)器的流程為,首先建立服務(wù)器端的的Socket,當(dāng)檢測到來自客戶端的連接請求時,向客戶端發(fā)送收到連接請求的信息,并建立與客戶端之間的連接。當(dāng)完成通信后,服務(wù)器斷開與客戶端的Socket連接。客戶端的步驟如下。建立客戶端的Socket,確定要連接的服務(wù)器的主機(jī)名和端口。發(fā)送連接請求到服務(wù)器,并等待服務(wù)器的回饋信息。連接成功后,與服務(wù)器進(jìn)行數(shù)據(jù)的交互。數(shù)據(jù)處理完畢后,關(guān)閉自身的Socket連接,如圖1所示。
3.1 客戶端軟件開發(fā)
客戶端軟件主要實(shí)現(xiàn)的功能是向與服務(wù)器端建立Socket連接,發(fā)送消息,并接受返回信息,結(jié)束后斷開Socket連接。主要通過ClientSocket實(shí)現(xiàn)。
一、Java多線程簡介
將多線程機(jī)制蘊(yùn)含在語言中,是Java的一個重要特征。所謂線程,是指程序中的一個執(zhí)行流。在一個進(jìn)程中,可以有多個線程。這些線程在操作系統(tǒng)的調(diào)度下并發(fā)執(zhí)行,使得每個線程都好像在獨(dú)占整個系統(tǒng)資源。而有了多線程這個特性,JAVA可以支持多個程序并發(fā)執(zhí)行。利用Java的多線程編程接口,開發(fā)人員可以方便地寫出支持多線程的應(yīng)用程序,有效地減少并發(fā)并行程序設(shè)計的困難,提供程序執(zhí)行效率。
1.線程的創(chuàng)建
有兩種方法可以創(chuàng)建線程。第一種方法是通過繼承類Thread來創(chuàng)建線程類。子類重載其mn0方法。實(shí)現(xiàn)方法如下:
classThieadNameextendsThread{
publicvoidrun(){//run是整個線程類代碼的
入口
…//需要以線程方式運(yùn)行的代碼
}}
第二種方法是建立一個具有Runnable接口的類。由于Java不支持多繼承性,如果需要類似線程方式運(yùn)行且繼承其他的類,就必須實(shí)現(xiàn)Runnable接口。Runnable接口只有一個方法run()。在類中實(shí)現(xiàn)此接口的方法如下:
classThieadNameextendsAppletimplementsRunnable{
publicvoidrun(){
…//需要以線程方式運(yùn)行的代碼
}}
2.線程的調(diào)用
如果采用第一種方法,創(chuàng)建的線程類的調(diào)用格式如下:ThreadNametest=newThreadName();//test是線程類
ThreadName的一個實(shí)例test.start();
start()是線程類的成員函數(shù),用于啟動該線程,該線程將自動調(diào)用run()方法。
如果采用第二種方法,創(chuàng)建的線程類的調(diào)用格式如下:
ThreadNametest=newThreadName();
Threadth=newThiead(test);/組過Thread創(chuàng)建個新的線程
th.start()
3.線程的同步及通信
系統(tǒng)中存在多個線程時,就需要保證線程的同步及相互通信,以期協(xié)調(diào)工作,避免發(fā)生死鎖。Java提供了二個標(biāo)準(zhǔn)的Object類方法wait(),notify()和notifyAll(),以及二個Thread類方法sleep()suspend()和resume(),用于中斷或喚醒線程的執(zhí)行。當(dāng)線程調(diào)用sleep(),wait()或suspend()方法之后線程就會由可運(yùn)行狀態(tài)進(jìn)入阻塞狀態(tài)(blocked),一旦線程睡眠時間到或者是其他線程調(diào)用了notify()或resume()方法后,此線程才會由阻塞狀態(tài)進(jìn)入可運(yùn)行狀態(tài)中,然而一個線程是否最終占有CPU,取決于系統(tǒng)的調(diào)度策略。Java1.0在solaris版本中實(shí)現(xiàn)的“綠色線程”的調(diào)用策略是讓一個線程持續(xù)處于執(zhí)行狀態(tài)直到有一個更高優(yōu)先級的線程將之打斷,而Windows95和WindowsNT是給每一個處于可執(zhí)行狀態(tài)的線程分配一個時間片,當(dāng)時間片用完時系統(tǒng)會調(diào)用另一個線程投入運(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)算能力和信息的差異,同時又由于網(wǎng)間網(wǎng)通信完全是異步的,因此,在基于TCP/P協(xié)議的網(wǎng)間網(wǎng)中,最主要的進(jìn)程間相互作用模型是客戶/服務(wù)器(client/server)模型??蛻艉头?wù)器分別是兩個應(yīng)用程序(進(jìn)程),客戶向服務(wù)器發(fā)出服務(wù)請求,服務(wù)器作出響應(yīng)。
網(wǎng)間網(wǎng)進(jìn)程通信的關(guān)鍵是要解決進(jìn)程的標(biāo)識和多傳輸協(xié)議的標(biāo)識問題以及進(jìn)程間相互作用的模式。在網(wǎng)間網(wǎng)中,全局惟一的標(biāo)識一個進(jìn)程需要一個三兀組表示,即用半相關(guān)(half-association)來描述一個Socket:
{協(xié)議,本地地址,本地端口號}
而一個完整的網(wǎng)間網(wǎng)進(jìn)程通信需要由兩個進(jìn)程組成(兩個端進(jìn)程),因此一個完整的網(wǎng)間網(wǎng)進(jìn)程通信必須用一個五元組表示,即用相關(guān)(association)來描述一■個完整的Socket:{協(xié)議,本地地址,本地端口號,遠(yuǎn)地地址,遠(yuǎn)地端口號}其中,一個確定的網(wǎng)間網(wǎng)進(jìn)程通信只能使用同一個內(nèi)核的高層協(xié)議,不可能通信的一端用TCP而另一端用UDP協(xié)議。故而,兩個協(xié)議相同的半相關(guān)才能組成一個合適的相關(guān),兩個三元組組合起來是一個五兀組而非六兀組。每一個Socket有一個本地惟一的Socket號,由操作系統(tǒng)分配。一個本地Socket號,完整地描述了本地進(jìn)程以及與之通信的遠(yuǎn)地進(jìn)程,因此Socket的語義具有網(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)問題。故而,Socket系統(tǒng)調(diào)用與UNIX的文件訪問有許多類似之處,是對UNIX輸入輸出的擴(kuò)充。Socket是面向客戶/服務(wù)器模型設(shè)計的,針對客戶和服務(wù)器程序提供了不同的Socket系統(tǒng)調(diào)用。服務(wù)器擁有全局公認(rèn)的半相關(guān)Socket,這就保證了任何客戶都可以在網(wǎng)絡(luò)的任何地方隨機(jī)向它發(fā)出聯(lián)接請求和信息請求。Socket的數(shù)據(jù)信息是原始字節(jié)流的形式,通信雙方要在此基礎(chǔ)上進(jìn)行約定的數(shù)據(jù)格式化和解釋等處理(即相同的協(xié)議),然后才能進(jìn)行進(jìn)一步的具體應(yīng)用操作,這也是實(shí)現(xiàn)某種協(xié)議的過程。基于Socket的這種通信機(jī)制,在網(wǎng)關(guān)(gateway)的作用下可實(shí)現(xiàn)TCP/協(xié)議和其他低級協(xié)議,如現(xiàn)場總線協(xié)議CAN(ControllerAreaNetwork)協(xié)議的轉(zhuǎn)換,進(jìn)而擴(kuò)展Internet應(yīng)用領(lǐng)域,將實(shí)時控制功能帶入網(wǎng)絡(luò)。Socket通信機(jī)制提供了兩種通訊方式:有聯(lián)接和無聯(lián)接方式,分別面向不同的應(yīng)用需求。使用有聯(lián)接方式時,通信鏈路提供了可靠的,全雙工的字節(jié)流服務(wù)。在該方式下,通信雙方必須創(chuàng)建一個聯(lián)接過程并建立一條通訊鏈路,以后的網(wǎng)絡(luò)通信操作完全在這一對進(jìn)程之間進(jìn)行,通信完畢關(guān)閉此聯(lián)接過程。使用無聯(lián)接方式時其系統(tǒng)開銷比有聯(lián)接方式小,但通信鏈路提供了不可靠的數(shù)據(jù)報服務(wù),不能保證信源所傳輸?shù)臄?shù)據(jù)一定能夠到達(dá)信宿。在該方式下,通信雙方不必創(chuàng)建一個聯(lián)接過程和建立一條通訊鏈路,網(wǎng)絡(luò)通信操作在不同的主機(jī)和進(jìn)程之間轉(zhuǎn)發(fā)進(jìn)行。
面向客戶/服務(wù)器方式的Socket通信機(jī)制模型如圖3所示。
三、Java多線程在網(wǎng)絡(luò)編程中的應(yīng)用
1.Socket客戶端程序設(shè)計我們通過一個簡單的應(yīng)用程序?qū)嵗齺碚f明其通信程序的工作過程和編程特點(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ā)來的數(shù)據(jù),并顯示在屏幕上。Socket類是覆蓋在一個與平臺有關(guān)的實(shí)現(xiàn)之上的,只是它把具體的系統(tǒng)細(xì)節(jié)從java程序中屏蔽了。因而在程序開始部分引入了java.net包中的所有的Socket類?;诖?,Java程序即可實(shí)現(xiàn)與平臺的無關(guān)性。網(wǎng)絡(luò)通信的目的主要是對網(wǎng)絡(luò)資源的訪問和操作。在建立了新的Socket對象實(shí)體s后,利用outputStream(),getTnputStream()方法建立輸出、輸入流。這樣,訪問的網(wǎng)絡(luò)資源的過程就變成了處理流對象的過程,即以數(shù)據(jù)流中的方法讀寫應(yīng)用程序端。數(shù)據(jù)流(Stream)可以理解成數(shù)據(jù)的通信途徑,在建立好應(yīng)用程序和資源方的通信通道后,遠(yuǎn)方的數(shù)據(jù)就可以自動傳輸過來。在程序的結(jié)尾用close()方法關(guān)閉輸入、輸出流和Socket,這樣將釋放所占用的系統(tǒng)資源。
Java語言的Socket通信機(jī)制和UNIX系統(tǒng)的輸入輸出操作(open-read-write-close)相類似。其客戶端基本操作程序的編寫概括起來包括以下四步:①打開Socket,即創(chuàng)建一個Socket對象實(shí)體:②創(chuàng)建與此Socket聯(lián)接的輸入輸出流;③根據(jù)服務(wù)器的協(xié)議向此Socket寫數(shù)據(jù)或從Socket讀數(shù)據(jù):④關(guān)閉輸入、輸出流和Socket。
2.Socket服務(wù)器端程序設(shè)計基于Java語言的Socket服務(wù)器端基本操作過程和客戶端過程相對應(yīng),其程序的編寫也包括以下五步:①打開SeneSocket,即創(chuàng)建一個ServerSocket對象實(shí)體在指定端口為客戶端請求的Socket服務(wù);②使用ServerSocket類的accept()方法接收來自客戶端的聯(lián)接請求;③使用新建的Socket對象創(chuàng)建輸入、輸出流對象;④通過對流對象的操作完成客戶端的處理請求,并將結(jié)果返回給客戶端;⑤當(dāng)客戶端和服務(wù)器工作結(jié)束時,關(guān)閉輸入、輸出流,用ServerSocket類的close()方法關(guān)閉Sacket。
服務(wù)器通常分為并發(fā)服務(wù)器和重復(fù)服務(wù)器,并發(fā)服務(wù)器只接收客戶請求,不處理請求,客戶請求由它的fork之進(jìn)程處理和響應(yīng);而重復(fù)服務(wù)器接收、處理并響應(yīng)客戶請求,即工作時只能同時和一個客戶程序聯(lián)接。下面給出使用Java的多線程實(shí)現(xiàn)并發(fā)服務(wù)器通信程序。具體地實(shí)現(xiàn)并發(fā)服務(wù)器的思路是:在服務(wù)器的程序中首先創(chuàng)建單個ServerSocke^并調(diào)用accept()來等候一個新連接,然后用accept()返回的Socket新建一個線程,它只為連接的特定的客戶提供服務(wù)。接著再調(diào)用accept(),等候下一個親新的連接請求。
(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é)語
關(guān)鍵詞:Linux;嵌入式;網(wǎng)絡(luò)編程
中圖分類號:TP311 文獻(xiàn)標(biāo)識碼:A 文章編號: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ò)編程是通過socket套接字接口來進(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è)計符合Linux的習(xí)慣,在理想情況下,應(yīng)將所有可讀寫訪問的對象映射成文件,這樣就可以用普通的文件讀寫操作來處理這些對象了,使通信中的收發(fā)可以很容易映射成讀寫操作。在傳輸協(xié)議的上下文中,由這類讀寫操作的對象就是通信關(guān)系的兩端,他們表示成了套接字。
BSD套接字是一個通用接口,它支持不同的網(wǎng)絡(luò)結(jié)構(gòu),同時也是一個內(nèi)部進(jìn)程間通信機(jī)制。當(dāng)一個主機(jī)上同時有多個應(yīng)用程序在運(yùn)行,他們使用tcp和udp協(xié)議進(jìn)行通信,則傳輸層協(xié)議收到數(shù)據(jù)后將根據(jù)端口和套接口區(qū)分?jǐn)?shù)據(jù)是傳給哪個應(yīng)用程序。端口是標(biāo)識傳輸層與應(yīng)用程序的數(shù)據(jù)接口,每個端口有一個16位的標(biāo)識符。套接口是IP地址與端口號的組合,用來標(biāo)識全網(wǎng)范圍內(nèi)的唯一一個端口,在tep和udP協(xié)議中用來標(biāo)識一個連接,網(wǎng)絡(luò)應(yīng)用程序之間通過套接口來實(shí)現(xiàn)通信。套接字是套接口描述字的簡稱,是整型數(shù)字,它與文件描述符共用一段數(shù)值空間O_65535。應(yīng)用程序中使用套接字來調(diào)用套接口,套接字可認(rèn)為是指向套接口的指針,就像文件描述符是指向文件的指針一樣。一個套接字描述了一個鏈接的一個端口,一個socket端點(diǎn)可以用socket地址來描述,socket地址結(jié)構(gòu)由正地址,端口和使用協(xié)議組成(TCPorUDP),因此兩個互聯(lián)的進(jìn)程都要有一個描述他們之間連結(jié)的套接字。我們也可以把套接字看作為是一種特殊的管道,只是這種管道對于包含的數(shù)據(jù)量沒有限制。套接字存在于特定的通信域(即地址族)中,只有隸屬于同一地址族的套接字才能建立對話。Linux支持的協(xié)議族有AF_INET(IPv4協(xié)議)、AF_INET6(IPv6協(xié)議)和AF_UNIX(Unix域協(xié)議)。
Linux支持多種套接字類型,每種套接字類型對應(yīng)于創(chuàng)建套接字的應(yīng)用程序所希望的通信服務(wù)類型。同一協(xié)議簇可能提供多種服務(wù)類型,比如TCP/IP協(xié)議族提供的虛電路和數(shù)據(jù)報就是兩種不同的通信服務(wù)類型。TCP/IP中常用的socket類型共有三種,一種是流式socket(SOCK_STREAM),另一種是數(shù)據(jù)報式socket(SOCK_DGRAM),還有一種是原始socket(SOCK_RAW)。流式socket是一種面向連結(jié)的socket,對應(yīng)于面向連接的TCP服務(wù)應(yīng)用。數(shù)據(jù)報式socket是一種無連接的socket,對應(yīng)于無連接的UDP服務(wù)。原始套接字接口容許對較低層協(xié)議如IP、ICMP直接訪問,常用于檢驗新的協(xié)議實(shí)現(xiàn)或訪問現(xiàn)有服務(wù)中的新設(shè)備。
2 網(wǎng)絡(luò)編程基本模式
2.1 客戶機(jī)/服務(wù)器模式
網(wǎng)絡(luò)編程的基本模式是Client/Serve:模式,該模式的建立基于以下兩點(diǎn):
1) 非對等作用;2)通信完全是異步的客戶機(jī)/服務(wù)器模式在操作過程中采取的是主動請示方式,首先服務(wù)器方要先啟動,并根據(jù)請示提供相應(yīng)服務(wù)。Server端首先調(diào)用socket創(chuàng)建一個一定類型socket,然后通過bind函數(shù)將這個socket綁定到一個client知道的端口上,接著server調(diào)用Listen函數(shù)設(shè)置傾聽隊列的長度,為了接收來自client端的請求做準(zhǔn)備,然后server調(diào)用accept,開始在所綁定的端口傾聽來自client端的連接請求。如果socket被設(shè)置成阻塞方式, accept調(diào)用將被阻塞,進(jìn)程被掛起,直到server收到來自client的請求后,accept才返回。Client端通過socket調(diào)用創(chuàng)建一個一定類型的socket(應(yīng)當(dāng)和server的socket類型相同)。然后調(diào)用connect函數(shù)向server所在的主機(jī)發(fā)出連接請求,連接時,需要指定server所在主機(jī)的IP地址和server傾聽的端口號,連接的報文包含了client端的初始的序號SYN a和MSS=1460信息(最大數(shù)據(jù)段的大小)。正在傾聽來自client的連接請求的server收到client的連接請求后,server從accept調(diào)用中返回(通常socket是阻塞方式工作的)。server將會向client端發(fā)送server端的初始序號SYN b和對client端的SYN a的確認(rèn)ACK=a+l,還有本端的最大數(shù)據(jù)MSS當(dāng)client端接收到server端的回應(yīng)時,將發(fā)出對server請求的ACK=b+1。然后client從connect中返回,返回值是一個打開的socket的描述符,這個描述符和文件的描述符類似,程序可以像使用文件的描述符一樣使用它。稍后,在server端收到client端對其請求的回應(yīng)時,server將從accept調(diào)用返回,返回值也是一個socket的描述符。
2.2 面向連接協(xié)議的字節(jié)流套接字編程
字節(jié)流socket采用的是傳輸控制協(xié)議TCP。TCP提供面向連接的流傳輸,面向連接對可靠性的保證首先是它在進(jìn)行數(shù)據(jù)傳輸前,必須在信源端和信宿端建立連接。在面向鏈接傳輸?shù)拿恳粋€報文都需要接收端確認(rèn),未確認(rèn)的報文被認(rèn)為是出錯報文。字節(jié)流套接字的服務(wù)器進(jìn)程和客戶進(jìn)程在通信前必須先建立連接,建立連接和通信的步驟如下:
1) 服務(wù)進(jìn)程首先調(diào)用Socket()創(chuàng)建一個字節(jié)流套接字,并調(diào)用bind()將服務(wù)器地址捆扎在該套接字上,接著調(diào)用listen()監(jiān)聽連接請求,隨后調(diào)用accept()做好與客戶進(jìn)程建立連接的準(zhǔn)備,無連接請求時,服務(wù)進(jìn)程被阻塞;
2) 客戶進(jìn)程調(diào)用Socket()創(chuàng)建字節(jié)流套接字,然后調(diào)用connect()向服務(wù)進(jìn)程發(fā)出連接請求;
3) 當(dāng)連接請求到來后,服務(wù)進(jìn)程被喚醒,生成一個新的字節(jié)流套接字,并用新套接字同客戶進(jìn)程的套接字建立連接,而服務(wù)進(jìn)程最早生成的套接字則繼續(xù)用于監(jiān)聽網(wǎng)絡(luò)上的服務(wù)請求;
4) 服務(wù)進(jìn)程和客戶進(jìn)程通過調(diào)用read()和write()交換數(shù)據(jù);
5) 服務(wù)進(jìn)程和客戶進(jìn)程通過調(diào)用close()撤消套接字并中斷連接;當(dāng)選擇SOCK STREAM(字節(jié)流)類型的時,sock()系統(tǒng)調(diào)用中的參數(shù)protocol(協(xié)議)總會選中TCP,而UDP則一直用作SOCK DGRAM類型的傳輸協(xié)議。
2.3 非連接協(xié)議的數(shù)據(jù)報套接字編程
數(shù)據(jù)報式socket采用的是用戶數(shù)據(jù)報協(xié)議UDP,它是建立在IP協(xié)議之上的,提供無連接數(shù)據(jù)報傳輸,主要應(yīng)用在高可靠性、低延遲的局域網(wǎng)上,它的優(yōu)點(diǎn)是高效率低開銷,不用建立連接和撤銷連接,缺點(diǎn)是不可靠,報文丟失后需重發(fā)。數(shù)據(jù)套接字的服務(wù)進(jìn)程客戶進(jìn)程通信前不必建立連接,UDP則一直用作SOCKpGRAM類型的傳輸協(xié)議,通信的步驟如下:
1) 服務(wù)進(jìn)程首先調(diào)用Socket()創(chuàng)建一個數(shù)據(jù)套接字,并調(diào)用bind將服務(wù)器地址捆扎在該套接字上,然后調(diào)用recvfrom()等待客戶進(jìn)程發(fā)來的請求;
2) 客戶進(jìn)程在調(diào)用SocketQ創(chuàng)建一個數(shù)據(jù)報套接字后,調(diào)用bindU將客戶機(jī)地址捆扎在此套接字上,接著調(diào)用sendto()向服務(wù)進(jìn)程發(fā)送請求,然后調(diào)用recvfrom()等待服務(wù)進(jìn)程返回該請求的處理結(jié)果;
3) 服務(wù)進(jìn)程在執(zhí)行客戶進(jìn)程所請求的任務(wù)后,調(diào)用sendto()將處理結(jié)果返回給客戶進(jìn)程;
4) 服務(wù)進(jìn)程和客戶進(jìn)程通過調(diào)用close()撤消套接字;
3 Linux內(nèi)核對socket的支持
確切地說,Linux內(nèi)核只提供了一個與套接字有關(guān)的系統(tǒng)調(diào)用,應(yīng)用程序的所有套接字調(diào)用都會映射到這個系統(tǒng)調(diào)用上。在Linux內(nèi)核中的net/socket.c中定義這個函數(shù)sys_socketcall(int call,unsigned long *args)。 include/asm/unistd.h中會指派一個數(shù)字,該數(shù)字會和arch/i386/kernel/entry.s中的系統(tǒng)調(diào)用一起添加到表格中。通過調(diào)用中。all參數(shù)可以說明所指向的那個套接字函數(shù),在include/linux/net.h中定義了可接受的參數(shù)SYS_SOCKET, SYS_IND,SYS_CONNECT, SYS_LISTEN等,在用戶空間的函數(shù)庫中,帶有特定參數(shù)的sys_socketcall調(diào)用會映射成某個獨(dú)立函數(shù),在內(nèi)核中若要選中希望調(diào)用的那個函數(shù),需要在sys_socketcall函數(shù)中用到一條:witch命令如下所示,而在此之前首先要使用copy_from_user()命令將sys_ socketcall()的函數(shù)復(fù)制到一個向量中,即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套接字,一個重要的數(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)稍有簡化。state中存儲的是套接字狀態(tài)可以取值如下(include/linux/net.h):SS_FREE(不忙)、SSes UNCONNECTED(未連通)、SS_ONNECTING(目前正在連接)、SS_ONNECTED(已連通)、SS_ISCONNECTING(目前正在斷開連接)。flags用以同步訪問,ops指針指向了連通協(xié)議(如tcp或udp)在初始化之后的協(xié)議運(yùn)作。就像Linux中的每個文件都有一個mode一樣,每個BSD套接字也分派了一個mode o file中存儲了一個指向該文件結(jié)構(gòu)的指針,這個結(jié)構(gòu)連接了套接字,因此它可以用與指向套接字。如果有進(jìn)程等待著這個套接字上的事件,也可以通過fasync_list找出該進(jìn)程。通過sk指針可以使用一個匹配的sock結(jié)構(gòu)。不過,這個sock結(jié)構(gòu)是由BSD套接字之下、特定于協(xié)議的套接字初始化的,并且連通到這個指針。字段負(fù)責(zé)根據(jù)用戶空間中的同名套接字調(diào)用存儲第二個參數(shù),在Linux內(nèi)核include/asm/socket.h中定義了可接受的參數(shù)。
從上面分析可以看出任何時候通過一個socket來讀寫數(shù)據(jù)時,都是在使用一個系統(tǒng)調(diào)用(system_call)這個調(diào)用(例如read或write)跨越了用戶空間應(yīng)用程序與內(nèi)核的邊界。另外,在進(jìn)入內(nèi)核之前,您的調(diào)用會通過C庫來進(jìn)入內(nèi)核中的一個通用函數(shù)system_call()。從system_call()中,這個調(diào)用會進(jìn)入文件系統(tǒng)層,內(nèi)核會在這兒確定正在處理的是哪種類型的設(shè)備。最后,調(diào)用會進(jìn)入socket層,數(shù)據(jù)就是在這里進(jìn)行讀取或進(jìn)行排隊從而通過socket進(jìn)行傳輸?shù)摹?/p>
4 總結(jié)
每種網(wǎng)絡(luò)協(xié)議都提供網(wǎng)絡(luò)應(yīng)用開發(fā)接口,TCP/IP協(xié)議的應(yīng)用開發(fā)接口的事實(shí)標(biāo)準(zhǔn)是socket套接口,開發(fā)socket的目的是隱藏網(wǎng)絡(luò)底層的復(fù)雜結(jié)構(gòu)和協(xié)議,使編程人員能夠簡單抽象的對網(wǎng)絡(luò)進(jìn)行操作。socket面向客戶機(jī)/服務(wù)器模型,針對客戶機(jī)/服務(wù)器程序提供不同的socket的系統(tǒng)調(diào)用函數(shù),客戶端隨機(jī)申請一個socket,操作系統(tǒng)為之分配一個隨機(jī)socket號;服務(wù)器端擁有全局公認(rèn)的socket號,任何客戶都可以向他發(fā)送連接請求和信息請求。進(jìn)程通信以前,雙方必須各自創(chuàng)建一個端口,否則是沒有辦法在通信前建立聯(lián)系的,而socket提供了這種進(jìn)程間通信的端口。從網(wǎng)絡(luò)編程的套接字的分析來看,選擇TCP套接字和選擇UDP套接字編程,在傳輸數(shù)據(jù)時有著速度、效率和穩(wěn)定性的差別。TCP編程擁有了可靠的數(shù)據(jù)連接,UDP不具有。但是在速度方面,UDP編程確優(yōu)于TCP編程,特別是對于傳輸短消息?;谶@兩種通信方式優(yōu)缺點(diǎn)的考慮,在后續(xù)編寫IDU控制應(yīng)用軟件時,將UDP套接字用于硬件終端對外廣播本地IP地址,使局域網(wǎng)內(nèi)客戶端軟件識別某臺終端設(shè)備,獲取其MAC地址等硬件信息。將TCP套接字用于在客戶端傳輸用戶數(shù)據(jù),對硬件終端上的硬件設(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)用程序開發(fā)詳解[M],北京:人民郵電出版社,2006.
關(guān)鍵詞:Windows Socket;TCP/IP;阻塞;非阻塞;異步選擇機(jī)制
中圖分類號:TP393文獻(xiàn)標(biāo)識碼:A文章編號: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在我國日益普及,用戶對網(wǎng)絡(luò)應(yī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ī)之間的通信。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ù)報的分組發(fā)送服務(wù),數(shù)據(jù)傳輸?shù)目煽啃灾荒芡ㄟ^應(yīng)用層來保證。TCP和UDP的主要差別在于可靠性,TCP高度可用,需要大量功能開銷,而UDP是簡單、高效。由于是在局域網(wǎng)中實(shí)現(xiàn)通信,為了達(dá)到簡單、實(shí)時、高效的目的,在介紹Windwins Socket,異步選擇機(jī)制,網(wǎng)絡(luò)通信原理等相關(guān)知識的基礎(chǔ)上,根據(jù)基于數(shù)據(jù)報套接字(UDP協(xié)議)的編程步驟,詳細(xì)探討了在VC中基于異步選擇機(jī)制的局域網(wǎng)通信的實(shí)現(xiàn)。
2 Windows Socket及異步選擇機(jī)制
2.1 Windows Socket
套接字(Socket)是建立在傳輸層協(xié)議(主要是TCP和UDP)上的一種套接字規(guī)范,最初是由美國加州Berkley大學(xué)提出,它定義了兩臺計算機(jī)間進(jìn)行通信的規(guī)范,套接字屏蔽了底層通信軟件和具體操作系統(tǒng)的差異,使得任何兩臺安裝了TCP協(xié)議軟件和實(shí)現(xiàn)了套接字規(guī)范的計算機(jī)之間的通信成為可能。Windows Scoket是UNIX操作系統(tǒng)下的Bakeley Socket應(yīng)用程序開發(fā)接口在Windows環(huán)境下的實(shí)現(xiàn)。Windows Socket規(guī)范主要有WinSock1.1和WinSock2兩個版本,它們保持了和Bekeley Socket函數(shù)的兼容性,并做出了重要擴(kuò)充。這些擴(kuò)充主要是增加了一些異步請求函數(shù)和對網(wǎng)絡(luò)事件的異步選擇機(jī)制,使之更適合Windows平臺消息驅(qū)動的特性。
2.2 異步選擇機(jī)制
Windows Socket在兩種模式下執(zhí)行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,執(zhí)行操作的Winsock函數(shù)會一直等待下去,不會立即返回程序(將控制權(quán)交還給程序)。Windows Sockets為了支持Windows消息驅(qū)動機(jī)制,使應(yīng)用程序開發(fā)者能夠方便的處理網(wǎng)絡(luò)通信,它對網(wǎng)絡(luò)事件采用了基于消息的異步存取策略。Windows Sockets的異步選擇函數(shù)WSAAsyncSelect()提供了消息機(jī)制的網(wǎng)絡(luò)事件選擇,當(dāng)使用它登記的網(wǎng)絡(luò)事件發(fā)生時,Windows應(yīng)用程序相應(yīng)的窗口函數(shù)將收到一個消息,消息中指示了發(fā)生的網(wǎng)絡(luò)事件,以及與事件相關(guān)的一些信息。這個函數(shù)自動設(shè)置套接字為非阻塞模式。
Int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg)
S,標(biāo)識了請求事件通知的套接字描述符。
hWnd,標(biāo)識當(dāng)一個網(wǎng)絡(luò)事件發(fā)生時,接收消息的窗口的句柄
wMsg,網(wǎng)絡(luò)事件發(fā)生時,窗口接收到的消息(這里指的是一個自定義的消息。)
lEvent,指定應(yīng)用感興趣的網(wǎng)絡(luò)事件。
3 網(wǎng)絡(luò)通信的基本原理
網(wǎng)絡(luò)通信實(shí)質(zhì)上是網(wǎng)絡(luò)中的不同主機(jī)進(jìn)程之間的相互通信(可以把同機(jī)進(jìn)程通信看作其中的特例)問題。與單純的同機(jī)通信相比,網(wǎng)絡(luò)通信需要解決以下三個問題。第一,標(biāo)識網(wǎng)絡(luò)中的進(jìn)程。在同一主機(jī)上,不同進(jìn)程可以用進(jìn)程標(biāo)識符來標(biāo)識,而在網(wǎng)絡(luò)通信中則是利用端口號來標(biāo)識。端口是TCP和UDP與應(yīng)用程序打交道的訪問點(diǎn),是TCP/IP協(xié)議軟件的一部份。TCP/IP協(xié)議規(guī)定了一些標(biāo)準(zhǔn)保留端口,主要提供給服務(wù)器進(jìn)程使用,用戶進(jìn)程可以申請使用非保留端口,其端口的標(biāo)識符在本機(jī)種具有唯一性。因此可以利用端口號作為網(wǎng)絡(luò)中進(jìn)程本身的標(biāo)識。第二,多重協(xié)議的識別。網(wǎng)絡(luò)中的兩個進(jìn)程必須使用協(xié)議來相互通信,而網(wǎng)絡(luò)協(xié)議有多種,這就要求進(jìn)程能夠在眾多的協(xié)議中作出選擇。原因在于不同的協(xié)議地址格式不同、工作方式不同(比如面向連接與無連接),協(xié)議端口分配是相互獨(dú)立的。綜合以上兩點(diǎn),在網(wǎng)絡(luò)中全局地址標(biāo)識一個本地進(jìn)程需要一個三元組:協(xié)議,本地地址,本地端口號。而一個完整的網(wǎng)絡(luò)通信實(shí)例是由通信兩端的進(jìn)程組成,因此需要一個五元組來標(biāo)識:協(xié)議,本地地址,本地端口號,異地地址,異地端口號。這里的本地地址、異地地址是用來標(biāo)識計算機(jī)的,一般是指計算機(jī)的IP地址。第三,進(jìn)程之間相互作用模式,即應(yīng)用程序相互作用的模式。在網(wǎng)絡(luò)中兩個應(yīng)用程序間主要的作用模式是客戶機(jī)/服務(wù)器模式,在這種模式中客戶應(yīng)用程序向服務(wù)器程序請求服務(wù),這種方式隱含了在建立客戶機(jī)/服務(wù)器間通信時的非對稱性。表1顯示了基于數(shù)據(jù)報套接字的客戶機(jī)/服務(wù)器編程模型。
4 基于異步選擇機(jī)制的局域網(wǎng)通信的實(shí)現(xiàn)
4.1 實(shí)現(xiàn)思想
根據(jù)數(shù)據(jù)報套接字的客戶機(jī)/服務(wù)器編程模型,采用Windows Scoket的異步選擇機(jī)制,將服務(wù)器端和客戶端在同一個程序中實(shí)現(xiàn)。需要通信的計算機(jī)只要運(yùn)行同樣的程序,通過輸入對方的IP地址,或主機(jī)名就可以實(shí)現(xiàn)相互通信。
4.2 在VC中的具體實(shí)現(xiàn)
4.2.1 創(chuàng)建基于對話框的MFC EXE工程
在應(yīng)用程序類的初始化函數(shù):InitInstance()種調(diào)用如下語句加載套接字庫進(jìn)行版本協(xié)商。(采用Winsock2版本)
WSAStartup(wVersionRequested,&wsaData);
if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
{WSACleanup();return FALSE;}
在預(yù)編譯頭文件中包含頭文件winsock2.h,并鏈接庫文件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ù)報套接字
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);//端口號為6000
//將套接字綁定到一個本地地址和端口上
bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));
//采用異步選擇機(jī)制注冊網(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說:%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)用類及對話框類的析構(gòu)函數(shù)中分別終止套接字庫,關(guān)閉套接字
WSACleanup();//終止對套接字庫的使用
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ǎng)絡(luò)通信程序需要特別注意的是:每一臺機(jī)器內(nèi)部對變量的字節(jié)存儲順序不同,而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)是一定要統(tǒng)一順序的。所以對內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序不同的機(jī)器,一定要對數(shù)據(jù)進(jìn)行轉(zhuǎn)換;在一個套接字上基于消息請求網(wǎng)絡(luò)事件通知,可以同時請求多個網(wǎng)絡(luò)事件,當(dāng)接收到到消息時,需要根據(jù)發(fā)生的網(wǎng)絡(luò)事件作相應(yīng)的處理;網(wǎng)絡(luò)的狀況瞬息萬變,在調(diào)用函數(shù)的時候,應(yīng)對函數(shù)的返回值進(jìn)行判斷,以便找到出錯原因。
5 結(jié)論
在Windows平臺下程序的運(yùn)行都是基于消息的,如果采用阻塞套接字,就會由于接收函數(shù)的調(diào)用而導(dǎo)致程序暫停運(yùn)行,影響了程序運(yùn)行的效率。采用異步選擇機(jī)制,接收端和發(fā)送端在同一個程序中,并且采用數(shù)據(jù)報套接字實(shí)現(xiàn)了局域網(wǎng)通信,達(dá)到了簡單、高效、實(shí)時的目的。在實(shí)現(xiàn)網(wǎng)絡(luò)通信時,除了應(yīng)注意的問題外,還應(yīng)了解相關(guān)的網(wǎng)絡(luò)協(xié)議以及程序在Windows平臺下工作的原理,根據(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ò)程序開發(fā)[M].北京:清華大學(xué)出版社,2004,11.
關(guān)鍵詞:JAVA;SWING;Socket;IM
中圖分類號:TP319文獻(xiàn)標(biāo)識碼:A文章編號文章編號:1672-7800(2013)012-0091-03
作者簡介:徐華平(1977-),男,碩士,鹽城師范學(xué)院講師,研究方向為教育軟件設(shè)計及教育信息化。
0引言
由于企業(yè)內(nèi)管理、生產(chǎn)、銷售等各個環(huán)節(jié)的信息流動與傳遞已成為企業(yè)正常生產(chǎn)與運(yùn)轉(zhuǎn)的重要條件,搭建一個基于企業(yè)內(nèi)部網(wǎng)絡(luò)的即時通信平臺的重要性不言而喻。然而,通用的商業(yè)IM軟件依賴于互聯(lián)網(wǎng)接入技術(shù),其信息安全性差。因此,有必要建立一種基于局域網(wǎng)的內(nèi)部即時通信平臺。
在各類網(wǎng)絡(luò)客戶端之間的通信機(jī)制的選擇中,基于Socket機(jī)制無疑是成熟、可靠的選擇。這種機(jī)制透明于各類局域網(wǎng)絡(luò)類型,能夠為企業(yè)提供一種優(yōu)良、高效、快速的通信機(jī)制?;谏鲜鰞?yōu)點(diǎn),使得基于Socket機(jī)制的網(wǎng)絡(luò)通信軟件無需對企業(yè)現(xiàn)有的網(wǎng)絡(luò)硬件設(shè)施進(jìn)行任何變動,因而具有成本低廉的優(yōu)點(diǎn),能有效降低局域網(wǎng)通信負(fù)荷,提高局域網(wǎng)的使用效率,可以很好地解決企業(yè)內(nèi)部局域網(wǎng)的各種通信需求。
1系統(tǒng)關(guān)鍵技術(shù)分析與選擇
本文重點(diǎn)探討即時通訊軟件設(shè)計中實(shí)現(xiàn)系統(tǒng)通訊的關(guān)鍵技術(shù)。在應(yīng)用系統(tǒng)中,客戶端向服務(wù)端發(fā)送請求,等待服務(wù)器返回數(shù)據(jù),再刷新客戶端的數(shù)據(jù),稱之為通訊。在B/S或UE設(shè)備上實(shí)現(xiàn)即時通信的客戶端,其同步方式是不可行的。此類客戶端一方面處理能力弱,另一方面網(wǎng)絡(luò)會引起延時,因此,在客戶端之間以及客戶端與服務(wù)器之間的同步協(xié)調(diào)難以做到,通常采用異步方式。在 C/S 網(wǎng)絡(luò)編程中,數(shù)據(jù)的發(fā)送和接收通過Socket 套接口完成,套接字分為阻塞式和非阻塞式[1]。
通過對Java Net框架下GUI技術(shù)、通信協(xié)議等關(guān)鍵技術(shù)的分析,本通信器決定采用如下技術(shù)方案:
(1) Swing技術(shù)開發(fā)GUI桌面程序:采用Swing頂層容器、基本組件和事件處理等輕量級組件構(gòu)建局域網(wǎng)通信系統(tǒng)的程序主要界面。
(2) UDP通信協(xié)議: 鑒于UDP協(xié)議無鏈接,不可靠傳輸,通信雙方可不保持對方的狀態(tài),只需配置端口和IP地址即可通信,方便快捷,減少網(wǎng)絡(luò)開銷;考慮到UDP協(xié)議不可靠性,該通信器的通信協(xié)議中采用了確認(rèn)與重傳機(jī)制來保證數(shù)據(jù)傳輸?shù)目煽啃裕捎昧藙討B(tài)的超時重傳定時器值提高了本協(xié)議的適應(yīng)性和靈活性,還使用了在應(yīng)用層對數(shù)據(jù)進(jìn)行分片的方法來避免IP層分片的低效率[2]。
(3) 在本系統(tǒng)設(shè)計采用UDP通信協(xié)議時,基于UDP的Socket編程技術(shù),中提供了兩個類DatagramSoeket和DatagramPacket用來支持?jǐn)?shù)據(jù)報通信。Datagramsocket用來在程序之間建立傳送數(shù)據(jù)報的通信連接是數(shù)據(jù)報通信中的Socket。在數(shù)據(jù)報實(shí)現(xiàn)C/S通信程序時,無論在客戶端還是服務(wù)器端,都要首先建立一個DatagramSoeket對象,用來表示數(shù)據(jù)報通信的端點(diǎn),應(yīng)用程序通過Socket接收或發(fā)送數(shù)據(jù)報。DatagramPaeket則用來表示一個數(shù)據(jù)報,它是傳輸數(shù)據(jù)的載體,封裝了數(shù)據(jù)、數(shù)據(jù)長度、數(shù)據(jù)報地址等信息[3]。
(4)系統(tǒng)采用Derby作為數(shù)據(jù)庫,Derby數(shù)據(jù)庫是一個純用Java實(shí)現(xiàn)的內(nèi)存數(shù)據(jù)庫,屬于Apache的開源項目。因為是用Java實(shí)現(xiàn)的,所以可以在任何平臺上運(yùn)行。另外一個特點(diǎn)是體積小、免安裝,只需要幾個小Jar包就可以運(yùn)行。
2系統(tǒng)總體目標(biāo)與需求分析
2.1設(shè)計目標(biāo)
(1)使用Swing組件實(shí)現(xiàn)圖形化用戶界面。
(2)使用Socket技術(shù)和UDP協(xié)議通過IP地址和PORT提供不可靠非連接通信。
(3) 實(shí)現(xiàn)基于線程池的多端口監(jiān)聽。
(4) 實(shí)現(xiàn)客戶端間的文字、文件信息共享。
2.2功能需求分析
(1)用戶管理。即時通訊系統(tǒng)擁有多個賬戶,允許多個用戶注冊。一個用戶可以注冊多個標(biāo)識,注冊所使用的帳號類型為字母數(shù)字的組合。注冊新用戶時必須填寫符合要求的信息,注冊后只有用戶名與密碼驗證成功才能正確登錄。
(2)分組管理。分組管理部分要能夠?qū)崿F(xiàn)分組的添加與刪除,所添加的分組名稱可以是中文也可以是字母數(shù)字的組合,通過對分組的有效管理便于更方便地管理好友。
(3)好友管理。用戶可以查詢所有用戶,適當(dāng)選擇加為好友。一個用戶可以添加多個用戶為好友,同時一個用戶也可以被多個用戶添加為好友。用戶可以刪除好友,但是用戶只可以將好友放在一個組中。
(4)即時通訊。即時通訊模塊用戶可以與在線的好友進(jìn)行聊天,用戶首先查看好友是否在線,如果在線即可進(jìn)行即時通訊,并且用戶可以查看與好友的所有聊天記錄。
2.3數(shù)據(jù)庫設(shè)計
根據(jù)數(shù)據(jù)邏輯結(jié)構(gòu)設(shè)計的情況,本系統(tǒng)數(shù)據(jù)庫的數(shù)據(jù)表共有好友數(shù)據(jù)表、用戶表、分組表3個,和一般數(shù)據(jù)庫應(yīng)用系統(tǒng)基本類似,這里不再贅述。
3系統(tǒng)的設(shè)計與實(shí)現(xiàn)
(1)用戶列表模塊。
該模塊實(shí)現(xiàn)本局域網(wǎng)內(nèi)平臺中當(dāng)前登錄用戶的列表顯示,并顯示當(dāng)前用戶通過搜索或好友添加功能添加的用戶。在每個用戶節(jié)點(diǎn)上右擊鼠標(biāo),會出現(xiàn)Popup功能菜單項,其中菜單項的添加好友和刪除好友都會控制好友列表節(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();
模塊說明:
本模塊主要采用了Swing技術(shù)、多線程技術(shù)和循環(huán)技術(shù)。在系統(tǒng)進(jìn)程啟動時,利用多線程技術(shù)啟動線程循環(huán)——自動檢測數(shù)據(jù)庫已有好友數(shù)據(jù)信息,然后使用Swing提供的JTree類對象生成一個分層顯示數(shù)據(jù)的視圖即用戶列表。JTree類中的基本對象叫作結(jié)點(diǎn),它表示在給定層次結(jié)構(gòu)中的數(shù)據(jù)項。樹以垂直方式顯示數(shù)據(jù),每行顯示一個節(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)的葉節(jié)點(diǎn)。樹節(jié)點(diǎn)由Javax.swing.tree包中的接口TreeNode來定義的,該接口被DefaultMutableTreeNode類實(shí)現(xiàn)。為了創(chuàng)建一個樹,使用DefaultMutableTreeNode類為樹創(chuàng)建節(jié)點(diǎn)。
通過建立一個存放用戶姓名的標(biāo)簽對象Lable,該標(biāo)簽顯示的內(nèi)容由用戶在登錄時在登陸界面輸入的用戶名信息來確定,另外創(chuàng)建一個標(biāo)簽來存放“在線列表”,然后實(shí)質(zhì)上存放在線列表里面的內(nèi)容則有一個表格組件來存放所對應(yīng)的在線用戶,通過UDP數(shù)據(jù)報廣播來獲取在線的用戶的姓名以及IP地址,然后將數(shù)組的數(shù)據(jù)導(dǎo)入表格組件中,就可以形成在線用戶列表。
(2)聊天功能模塊。
用戶通過單擊在線用戶列表中的節(jié)點(diǎn)選擇某一用戶頭像,即可觸發(fā)聊天對話框界面。聊天對話框的左上部分為顯示聊天記錄。左下方為聊天輸入框。右側(cè)可以顯示對方的用戶名和IP等信息。本次聊天內(nèi)容在窗口關(guān)閉時,重新打開后仍會保存,一旦整個系統(tǒng)重啟之后,上次的聊天內(nèi)容才會清除。
關(guān)鍵代碼如下:
……
con.add(new JScrollPane(inMessage),BorderLayout.CENTER);
con.add(p,BorderLayout.SOUTH);
Thread thread=new Thread(this);
……
模塊說明:在用戶聊天界面上分別創(chuàng)建一個既用于接收信息又用于發(fā)送信息的多行純文本域?qū)ο驤textArea,它們來分別存放要發(fā)送的信息和聊天信息。outMessage用來存放所要發(fā)送的數(shù)據(jù),而inMessage用來存放聊天的信息。
(3)用戶搜索功能模塊。
搜索在線用戶的設(shè)計是基于廣播數(shù)據(jù)報的,通過采用組播地址然后創(chuàng)建廣播套接字,設(shè)置一個廣播的范圍,在這里設(shè)置的是一個本地的局域網(wǎng)的范圍。設(shè)置了廣播范圍之后加入廣播組,就可以廣播數(shù)據(jù)報和接收廣播數(shù)據(jù)報。
關(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)偵聽并接收廣播數(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ù)報,定義一個數(shù)組用來存放,并且設(shè)置了長度length,還有端口號port,最后通過socket在本地的局域網(wǎng)上進(jìn)行廣播數(shù)據(jù)報。通過廣播自己的IP地址,讓所有在線的用戶接收到自己的IP地址并且添加到對方的在線列表中,這樣每一個用戶的一個廣播就使得列表可以更新。
(4)點(diǎn)對點(diǎn)資源共享功能模塊。
如上所述,為了保證系統(tǒng)通信的即時性,選擇的是使用UDP數(shù)據(jù)報的一個點(diǎn)對點(diǎn)的通信方式,實(shí)現(xiàn)了兩個客戶端間進(jìn)程間的通信,這里使用DatagramPacket方法創(chuàng)建數(shù)據(jù)報對象:
DatagramPacket(b,b.length,address,8604);//發(fā)送端口是8604
DatagramSocket mail=new DatagramSocket();
通過DatagramPacket為存放指定數(shù)據(jù)的數(shù)據(jù)報,其中包括了指定的數(shù)據(jù),數(shù)據(jù)接收方的信息,并且要明確發(fā)送的目的地址address,同時指定偵聽該通信的主機(jī)端口號為8604。而在接收的時候:
byte b[]=new byte[5120];
mail.receive(pack);
構(gòu)建一個長度為5 120字節(jié)的數(shù)組用于保存接收到的數(shù)據(jù),同時設(shè)計了一個參數(shù)Pack,可以把收到的通信數(shù)據(jù)報傳遞給參數(shù)Pack。
4結(jié)語
本通信器界面友好, 雖然與大型復(fù)雜即時通訊軟件相比在功能上還稍有欠缺, 但是其作為即時通訊的主體功能已經(jīng)具備,尤其是其難易程度適中,綜合運(yùn)用了Java 面向?qū)ο蟮亩喾N知識,是高等院?!毒W(wǎng)絡(luò)編程》課程的一個很好的實(shí)例。
當(dāng)然由于諸多條件的制約,以及系統(tǒng)設(shè)計的定位,該設(shè)計在文件傳輸多樣化、網(wǎng)絡(luò)適應(yīng)性、通信機(jī)制、數(shù)據(jù)存儲與共享方面仍然存在較多不足,主要有以下幾個方面:
(1) 實(shí)現(xiàn)多樣化文件傳輸,如:圖片、文檔、音頻、視頻等。
(2) 初期對系統(tǒng)所采用數(shù)據(jù)庫的通用性認(rèn)識不足,后期可以采用Mysql或XML等主流數(shù)據(jù)存儲技術(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)良好的用戶體驗。
(5) 如果作為一個高性能企業(yè)內(nèi)部即時通信軟件,應(yīng)考慮到大用戶、多并發(fā)的情況,應(yīng)盡可能減輕服務(wù)器負(fù)荷,盡可能地將一些處理、判斷放在客戶端進(jìn)行,節(jié)約服務(wù)器端開銷。
參考文獻(xiàn)參考文獻(xiàn):
[1]陳立浩. 基于B/S和C/S的即時通信系統(tǒng)[J].計算機(jī)工程, 2009(15).
關(guān)鍵詞:winsock;TCP/IP協(xié)議;計費(fèi)管理系統(tǒng)
Winsock是一個ActiveX控件,它為采用客戶機(jī)/服務(wù)器通信機(jī)制的網(wǎng)絡(luò)提供了編制接口,使客戶機(jī)端和服務(wù)器端藉此實(shí)現(xiàn)連接和數(shù)據(jù)交換。它不是一種網(wǎng)絡(luò)協(xié)議,而是一套開放的、支持多種協(xié)議的Windows下的網(wǎng)絡(luò)編程接口。Socket實(shí)際在計算機(jī)中提供了一個通信端口,可以通過這個端口與任何一個具有Socket接口的計算機(jī)通信。應(yīng)用程序在網(wǎng)絡(luò)上傳輸,接收的信息都通過這個Socket接口來實(shí)現(xiàn)。
本文主要研究了Winsock的工作原理、編程方法,TCP/IP協(xié)議下計算機(jī)的工作原理,基于TCP/IP協(xié)議的網(wǎng)絡(luò)編程,服務(wù)器端與客戶機(jī)端程序的設(shè)計等,通過解決這些問題來實(shí)現(xiàn)Winsock網(wǎng)絡(luò)按時收費(fèi)系統(tǒng)的設(shè)計。
一、系統(tǒng)設(shè)計方案的研究
我們所設(shè)計的網(wǎng)絡(luò)計費(fèi)系統(tǒng)分為三部分,一是在win32平臺上的線程對數(shù)據(jù)包的截獲,并且進(jìn)行初步的整理,生成日志文件,作為前臺;而后臺則是在windows net server上的數(shù)據(jù)庫管理,直接取得第一部分截取程序生成的日志文件和通過ftp獲取在Linux服務(wù)器上其它服務(wù)生成的日志文件,對其分析,并至于以BDE驅(qū)動的數(shù)據(jù)庫文件中。三是計費(fèi)信息用戶查詢子系統(tǒng),使得用戶能在線通過權(quán)限查詢到自己各個時間段內(nèi)的計費(fèi)信息和費(fèi)用情況,有利于減輕網(wǎng)絡(luò)管理員的工作負(fù)擔(dān)。
Socket有3種主要類型:流式套接口,數(shù)據(jù)報套接口和原始套接口。面向連接服務(wù)器處理的請求往往比較復(fù)雜,原理是:服務(wù)器端不斷監(jiān)聽客戶端的請求,當(dāng)客戶端向服務(wù)器端發(fā)出連接請求并被服務(wù)器端檢測到以后,服務(wù)器會接收客戶端的請求,并建立連接。本文在方案選擇上采用了在網(wǎng)絡(luò)編程中最常用的一種模型--客戶機(jī)/服務(wù)器模型。選取了基于TCP/IP的客戶機(jī)/服務(wù)器模型和面向連接的流式套接字。
二、系統(tǒng)的軟件設(shè)計
(一)服務(wù)器的設(shè)計
①在初始化階段調(diào)用WSAStartup(),此函數(shù)在應(yīng)用程序中初始化Windows Sockets DLL ,只有此函數(shù)調(diào)用成功后,應(yīng)用程序才可以再調(diào)用其他Windows Sockets DLL中的API函數(shù)。②建立Socket,初始化WinSock的動態(tài)連接庫后,需要在服務(wù)器端建立一個監(jiān)聽的Socket,為此可以調(diào)用Socket()函數(shù)用來建立這個監(jiān)聽的Socket,并定義此Socket所使用的通信協(xié)議。③綁定端口,為服務(wù)器端定義的這個監(jiān)聽的Socket指定一個地址及端口(Port),這樣客戶端才知道待會要連接哪一個地址的哪個端口,為此我們要調(diào)用bind()函數(shù),該函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。④監(jiān)聽,當(dāng)服務(wù)器端的Socket對象綁定完成之后,服務(wù)器端必須建立一個監(jiān)聽的隊列來接收客戶端的連接請求。listen()函數(shù)使服務(wù)器端的Socket 進(jìn)入監(jiān)聽狀態(tài),并設(shè)定可以建立的最大連接數(shù)。⑤服務(wù)器端接受客戶端的連接請求,當(dāng)Client提出連接請求時,Server端hwnd視窗會收到Winsock Stack送來我們自定義的一個消息,這時,我們可以分析lParam,然后調(diào)用相關(guān)的函數(shù)來處理此事件。⑥結(jié)束 socket 連接,這一過程可以由服務(wù)器或客戶機(jī)的任一端啟動,只要調(diào)用closesocket()就可以,而要關(guān)閉Server端監(jiān)聽狀態(tài)的socket,同樣也是利用此函數(shù)。
(二)客戶機(jī)的設(shè)計
①建立客戶端的Socket,客戶端應(yīng)用程序首先也是調(diào)用WSAStartup() 函數(shù)來與Winsock的動態(tài)連接庫建立關(guān)系,然后同樣調(diào)用socket() 來建立一個TCP或UDP socket(相同協(xié)定的 sockets 才能相通,TCP 對 TCP,UDP 對 UDP)。與服務(wù)器端的socket 不同的是,客戶端的socket 可以調(diào)用 bind() 函數(shù),由自己來指定IP地址及port號碼;但是也可以不調(diào)用 bind(),而由 Winsock來自動設(shè)定IP地址及port號碼。②提出連接申請,客戶端的Socket使用connect()函數(shù)來提出與服務(wù)器端的Socket建立連接的申請,函數(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),因此用戶一般不涉及。編程時,編程界面有兩種形式:一是由內(nèi)核心直接提供的系統(tǒng)調(diào)用;二是使用以庫函數(shù)方式提供的各種函數(shù)。前者為核內(nèi)實(shí)現(xiàn),后者為核外實(shí)現(xiàn)。用戶服務(wù)要通過核外的應(yīng)用程序才能實(shí)現(xiàn),所以要使用套接字(socket)來實(shí)現(xiàn)。
四、系統(tǒng)測試包括測試儀器和軟件調(diào)試測試儀器
Agilent網(wǎng)絡(luò)測試儀表和AutoRunner自動化軟件測試工具。軟件調(diào)試:由于本系統(tǒng)要實(shí)現(xiàn)的功能比較多,所以在編寫程序和調(diào)試時出現(xiàn)了許多問題,不過經(jīng)過多次反復(fù)的調(diào)試后還是能把題目所要實(shí)現(xiàn)的功能都能實(shí)現(xiàn)了。
五、結(jié)束語
基于Winsock的編程,可以比較容易的實(shí)現(xiàn)按時收費(fèi)系統(tǒng)的設(shè)計。利用Winsock編程的系統(tǒng)界面友好、操作簡便、功能全面。這也就說明Winsock編程的優(yōu)點(diǎn),它將會成為軟件開發(fā)的重要工具!
參考文獻(xiàn):
【關(guān)鍵詞】I/O NIO BIO SOCKET通信 Java 多線程
1 引言
傳統(tǒng)BIO技術(shù)在Socket通信中,系統(tǒng)需要為每一個鏈接建立一個線程去處理其請求,隨著客戶端的并發(fā)量不斷增加后,會導(dǎo)致線程數(shù)量的增加嚴(yán)重影響系統(tǒng)的性能。由于并發(fā)量的增加有可能導(dǎo)致服務(wù)器宕機(jī),嚴(yán)重影響到用戶在使用過程中的良好體驗。為解決傳統(tǒng)BIO的不足,Java 中提供了新的API----NIO和NIO2來解決由于BIO技術(shù)帶來的系統(tǒng)瓶頸問題。在NIO中系統(tǒng)不再為每一個用戶請求注冊一個線程,而是通過通道將每一個鏈接都注冊到多路復(fù)用器上,通過多路復(fù)用器對注冊在其上的鏈接進(jìn)行輪詢檢查,發(fā)現(xiàn)有鏈接請求才會開啟線程對其進(jìn)行處理。NIO只在有連接請求時selector才會不斷輪詢檢查通道IO操作是否完成,與NIO技術(shù)不同的是AIO技術(shù)是異非步阻塞的。AIO中不再需要多路復(fù)用器,而是由異步非阻塞通道直接操作read和write方法。在客戶端讀寫請求發(fā)出后不再等待服務(wù)器的響應(yīng),而是處理完成后由操作系統(tǒng)來通知應(yīng)用程序。AIO與NIO這兩種技術(shù)都極大地改變了傳統(tǒng)I/O流的不足。
2 Socket基本通信原理介紹
Socket是網(wǎng)絡(luò)通信中的其中一方,用來接收網(wǎng)絡(luò)通信中雙方其中一方的請求,方便的對雙方的數(shù)據(jù)進(jìn)行傳輸。Socket通信分有連接的和無連接,面向連接的Socket通信與面向非連接的Socket通信相比有更高的可靠性和更有效的數(shù)據(jù)傳輸。本文基于有連接的套接字傳輸。
3 BIO、NIO、AIO技術(shù)比較
3.1 BIO技術(shù)簡介
BIO技術(shù)同步并且阻塞,在這種情況下,服務(wù)器需要為每一個連接開啟一個線程,只要有客戶端有請求服務(wù)器就需要開啟線程去進(jìn)行處理。從客戶端傳來的每個請求,服務(wù)器都需要為其創(chuàng)建相應(yīng)的線程去處理其請求。在BIO中,由于其線程的開銷很大,適合于運(yùn)用在并發(fā)量小的場景下。其基本模型如圖1所示。
BIO網(wǎng)絡(luò)通信基本步驟:
3.1.1 服務(wù)低端
(1)創(chuàng)建ServerSocket并綁定監(jiān)聽端口;
(2)創(chuàng)建Socket用來接收客戶端請求;
(3)創(chuàng)建輸入輸出流用來接收客戶端輸入或向客戶端輸出數(shù)據(jù);
(4)關(guān)閉輸入輸出流等系統(tǒng)資源。
3.1.2 客戶端
(1)創(chuàng)建Socket綁定IP地址及端口;
(2)創(chuàng)建輸入輸出流用來接收服務(wù)器端相應(yīng)或向服務(wù)器端發(fā)送數(shù)據(jù);
(3)關(guān)閉輸入輸出流等系統(tǒng)資源。
3.2 NIO技術(shù)簡介
NIO技術(shù)即New IO技術(shù),NIO技術(shù)由很多類和組件構(gòu)成,其最重要的由channel、Buffer、和Selectors三個核心部分組成。
Channel:Java NIO中的通道類似于流,但又不完全相同。既可以從通道中讀取數(shù)據(jù)到Buffer也可以將數(shù)據(jù)從Buffer寫入通道中。其中SocketChannel和ServerSocketChannel是NIO中提供的用來解決Socket通信中的服務(wù)器性能問題的。SocketChannel通過TCP協(xié)議來讀取網(wǎng)絡(luò)中的數(shù)據(jù),ServerSocketChannel用來接收鏈接來的請求以供服務(wù)器相應(yīng)。
Buffer:Buffer是用來為數(shù)據(jù)提供緩沖區(qū)的。在NIO技術(shù)中,所有的數(shù)據(jù)都必須經(jīng)過緩沖區(qū)。緩沖區(qū)本質(zhì)上為一塊可讀可寫的內(nèi)存塊,NIO中提供了不同數(shù)據(jù)類型的緩沖區(qū)來處理不同的數(shù)據(jù)請求,和一些基本的方法來操作緩沖區(qū)中的數(shù)據(jù)。
Selector:Selector是單線程來處理多個鏈接請求的關(guān)鍵。在Socket通信中,如果將多個鏈接請求注冊到多路復(fù)用器上,就可以用一個線程來處理多個鏈接請求,這樣就提高了Socket通信的效率。NIO通信中的網(wǎng)絡(luò)模型
如圖2所示。
NIO通信基本步驟:
3.2.1 服務(wù)器端
(1)創(chuàng)建多路復(fù)用器Selector 用來選擇通道;
(2)創(chuàng)建服務(wù)器端通道ServerSocketChannel;
(3)為多路復(fù)用器上注冊ServerSocketChannel用來將數(shù)據(jù)通過通道讀寫;
(4)申請Buffer存儲數(shù)據(jù);
(5)多路選擇器通過其key值輪詢檢查通道讀寫B(tài)uffer中的數(shù)據(jù)完成其通信過程。
3.2.2 客舳
(1)創(chuàng)建IP地址和端口號對應(yīng)的SocketChannel ;
(2)將SocketChannel設(shè)置為非阻塞模式;
(3)創(chuàng)建多路復(fù)用器Selector注冊SocketChannel到多路選擇器;
(4)多路選擇器輪詢檢查通道從Buffer中讀寫數(shù)據(jù)。
在NIO網(wǎng)絡(luò)通信模型中,客戶端向服務(wù)器端發(fā)起鏈接請求,客戶端將數(shù)據(jù)寫入服務(wù)器端Buffer中,然后通過channel來從Buffer讀取數(shù)據(jù)或向Buffer寫入數(shù)據(jù),對于通道中的數(shù)據(jù)必須經(jīng)過選擇器來向服務(wù)器端的線程發(fā)起請求。而每一個多路選擇器對應(yīng)一個線程模型,這樣一來,只有當(dāng)鏈接請求有效時服務(wù)器才為客戶端開啟線程處理數(shù)據(jù)。對于同步阻塞的NIO模型中客戶端在向服務(wù)器端發(fā)送完數(shù)據(jù)后會不斷詢問I/O操作是否就緒才能進(jìn)行下一步的操作。此模型中由于不需要為每一個鏈接請求創(chuàng)建一個線程,大大減少了線程之間的切換帶來的巨大開銷。提高了I/O的效率,使得在socket網(wǎng)絡(luò)通信更加高效。但由于NIO在鏈接請求中會不斷詢問I/O操作是否完成,其適合運(yùn)用在短鏈接且并發(fā)量大的場合下。
3.3 AIO技術(shù)簡介
AIO即為異步非阻塞IO,與NIO不同的是,在這種模式下,客戶端發(fā)起一個鏈接請求后不在詢問服務(wù)器端的I/O操作是否完成便立即返回。在使用過程中只需直接調(diào)用異步的read和write方法來讀寫數(shù)據(jù),在讀寫過程完畢后由操作系統(tǒng)主動通知應(yīng)用程序讀寫操作是否完成。由AIO的讀寫過程可以看出,因為在讀寫完成后客戶端不需要再詢問服務(wù)器端是完成了I/O操作,所以AIO非常適合于運(yùn)用在那些并發(fā)量大且長連接的請求。AIO模型如圖3所示。
AIO中有幾個比較重要的類:
AsynchronousServerSocket:用來創(chuàng)建服務(wù)器端的ServerSocket并綁定地址監(jiān)聽端口。
AsynchronousSocketChannel:Socket在異步非阻塞通信中的應(yīng)用,用來表示一個連接請求,并用來在通信過程中傳遞數(shù)據(jù)。
AsynchronousChannelGroup:異步通道的分組管理,目的是問了資源共享。創(chuàng)建AsynchronousChannelGroup時需要為其綁定一個線程執(zhí)行器對象,這個線程池主要完成兩個任務(wù):處理I/O事件和派發(fā)CompletionHandler。在創(chuàng)建AsynchronousServerSocket時需要為其綁定一個AsynchronousChannelGroup。通過AsynchronousServerSocket創(chuàng)建的AsynchronousChannelGroup將屬于同一組,共享其中資源。
CompletionHandler:用于定義在異步IO操作完成后的回調(diào)接口。
3.3.1 服務(wù)器端
(1)創(chuàng)建AsynchronousChannelGroup;
(2)創(chuàng)建AsynchronousServerSocketChannel并將它綁定在AsynchronousChannelGroup上;
(3)為AsynchronousServerSocketChannel對象綁定端口號;
(4)調(diào)用accept()接收客戶端請求實(shí)現(xiàn)CompletionHandler接口調(diào)用讀寫方法進(jìn)行讀寫數(shù)據(jù)。
3.3.2 客戶端
(1)創(chuàng)建AsynchronousSocketChannel;
(2)綁定IP地址和端口號鏈接服務(wù)低端實(shí)現(xiàn)CompletionHandler接口中方法直接進(jìn)行讀寫數(shù)據(jù)。
3.4 各種I/O技g比較分析
BIO:同步阻塞I/O,使用難度簡單,可靠性低,適用于鏈接并發(fā)量小的架構(gòu)且對服務(wù)器資源依賴更高。
NIO:同步非阻塞I/O,使用難度復(fù)雜,可靠性高,適用于并發(fā)量大且鏈接較短的場景。
AIO:異步非阻塞I/O,使用難度一般,可靠性高,適用于并發(fā)量大且鏈接長的場景。
4 結(jié)語
傳統(tǒng)網(wǎng)絡(luò)在IO處理方面存在著性能不足的問題,NIO與AIO包的引入解決了傳統(tǒng)IO性能瓶頸問題,使的Java在網(wǎng)絡(luò)通信中有了更搞得效率。對于不同的IO來說,它們各自有有不同的應(yīng)用領(lǐng)域。對于那些并發(fā)量小,數(shù)據(jù)傳輸量小的場景來說,傳統(tǒng)的BIO完全可以勝任其工作。相反對于那些對性能要求比較高,并發(fā)量大且對IO的要求比較搞得場合來說應(yīng)該選擇NIO或者BIO技術(shù)。如果客戶端與服務(wù)器在長連接的情況下選擇AIO相比NIO會更加高效一些。相反,如果是短連接的應(yīng)用領(lǐng)域,則推薦使用NIO。
參考文獻(xiàn)
[1]范寶德,馬建生.Java非阻塞通訊研究[J].微計算機(jī)信息,2006,22(12-3):116-119.
[2]劉邦桂,李正凡.用Java實(shí)現(xiàn)流式Socket通訊[J].華東交通大學(xué)學(xué)報,2007,24(05):110-112.
[3]封瑋,周世平.基于Java NIO的非阻塞通信的研究與實(shí)現(xiàn)[J].計算機(jī)系統(tǒng)應(yīng)用,2004(09):32-35.
[5]任小強(qiáng),陳金鷹,李文彬,胡波.網(wǎng)絡(luò)通信之Java Socket多線程通信[J].信息通信,2015(06):206-207.
作者簡介
王少輝(1992-),男,山西省平順縣人。碩士學(xué)位。現(xiàn)為山東大學(xué)(威海)機(jī)電與信息工程學(xué)院學(xué)生。主要研究方向為電路與系統(tǒng)。
陳曉鵬(1992-),男,天津市人。碩士學(xué)位?,F(xiàn)為山東大學(xué)(威海)機(jī)電與信息工程學(xué)院學(xué)生。主要研究方向為電路與系統(tǒng)。
關(guān)鍵詞:語音集群通信;移動網(wǎng)絡(luò);SSL Socket;opus
中圖分類號:TP393 文獻(xiàn)標(biāo)識碼:A 文章編號: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ā)展,利用移動終端進(jìn)行語音實(shí)時通信已成為民用市場或行業(yè)應(yīng)用的熱門業(yè)務(wù)[1]。相比較傳統(tǒng)的電話語音業(yè)務(wù),基于IP網(wǎng)絡(luò)的語音通信系統(tǒng)具有跨地域、低成本、高質(zhì)量、可定制等優(yōu)勢,且隨著移動網(wǎng)絡(luò)建設(shè)的不斷升級,語音通信系統(tǒng)的通話效果和時延已與電話語音的差別不大。目前,市場上主流的語音通信應(yīng)用有兩類:一是采用頻道、群組或聊天室形式的多人語音通信,如yy語音、qt語音等;二是采用一對一通話模式的IP網(wǎng)絡(luò)電話,如Skype等。微信作為國內(nèi)影響力較大的即時通信軟件,也提供了一對一的實(shí)時語音功能。
對于滿足專有行業(yè)或企業(yè)的語音集群通信業(yè)務(wù)[2],通常還需要考慮以下需求:1)群組通話是集群通信的主要業(yè)務(wù)模式,具有群組信道共享,避免通信互擾等服務(wù)要求;2)對于室外作業(yè)多采用移動網(wǎng)絡(luò),而非連接到有線網(wǎng)絡(luò)共享的AP熱點(diǎn),網(wǎng)絡(luò)質(zhì)量受限。3)VPDN專網(wǎng)服務(wù)支持或其他網(wǎng)絡(luò)安全要求;4)在語音業(yè)務(wù)基礎(chǔ)上,提供文本通信、文件傳輸?shù)榷ㄖ乒δ埽?)對語音、文本、共享文件等關(guān)鍵信息的私有存儲或全業(yè)務(wù)存儲。因此,在設(shè)計開發(fā)基于移動網(wǎng)絡(luò)的語音集群通信系統(tǒng)時,應(yīng)充分考慮行業(yè)應(yīng)用特點(diǎn),預(yù)留必要的業(yè)務(wù)應(yīng)用接口。
1 基本傳輸框架
語音集群通信系統(tǒng)基于標(biāo)準(zhǔn)的客戶端-服務(wù)器通信模型設(shè)計??蛻舳伺c服務(wù)器之間的數(shù)據(jù)傳輸采用TCP/UDP兩種協(xié)議,分別適用不同的網(wǎng)絡(luò)環(huán)境。當(dāng)移動網(wǎng)絡(luò)質(zhì)量較好時,可以根據(jù)設(shè)置使音頻數(shù)據(jù)通過TCP通道傳輸,保證通話語音的完整性和可靠性;當(dāng)網(wǎng)絡(luò)條件較差時,使用UDP通道傳輸可以避免TCP傳輸多次握手導(dǎo)致的網(wǎng)絡(luò)擁塞,實(shí)現(xiàn)低延時傳輸,保證系統(tǒng)的可用性。語音通信過程中的控制信令和通信信令則是基于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é)議,通過數(shù)據(jù)加密技術(shù)確保數(shù)據(jù)在網(wǎng)絡(luò)傳輸過程中不會被截取及竊聽。SSL及其后續(xù)發(fā)展的傳輸層安全(Transport Layer Security,TLS)提供了傳輸層的數(shù)據(jù)完整性保護(hù),包括身份認(rèn)證、協(xié)商加密算法、交換加密密鑰等[4]。OpenSSL是互聯(lián)網(wǎng)上適用性最廣泛的SSL密碼庫之一,提供了多種編程語言的庫支持。本系統(tǒng)引入OpenSSL 1.0.2方法庫,將TCP Socket替換為SSL Socket,對傳輸層協(xié)議進(jìn)行鏈路安全保護(hù)[5]。
SSL Socket的建立流程是:
1)客戶端請求建立SSL Socket連接,并向服務(wù)器發(fā)送SSL版本、加密參數(shù)等必要信息。服務(wù)器返回自身的SSL版本、加密參數(shù)、安全證書等必要信息??蛻舳讼蚍?wù)器提供認(rèn)證證書。
2)客戶端驗證服務(wù)器證書后,生成pre-master secret,并用公鑰加密后發(fā)送給服務(wù)器。服務(wù)器對證書進(jìn)行認(rèn)證,通過后用私鑰解密pre-master secret,并生成master secret。
3)通信雙方通過master secret生成會話密鑰,完成SSL Socket的創(chuàng)建,之后的通信數(shù)據(jù)將通過會話密鑰加密傳輸。在本系統(tǒng)中,會話密鑰采用TLSv1 AES256-SHA加密。
由于UDP是面向無連接的傳輸協(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ù)報文格式如圖1所示??蛻舳伺c服務(wù)器之間遵循此格式發(fā)送交互信息,其中報文頭Ptrfix包含1個字節(jié)的類型信息,區(qū)別信令數(shù)據(jù)或語音數(shù)據(jù),提供長度信息進(jìn)行組包校驗。載荷部分Data為實(shí)際交互數(shù)據(jù),采用TLSv1 AES256-SHA或其他算法加密。由于UDP協(xié)議僅用于發(fā)送語音數(shù)據(jù),其數(shù)據(jù)傳輸格式為圖1的Payload部分,最大長度為UDP分片的理論最大長度65507B[6]。
2 應(yīng)用流程
2.1 用戶接入
本系統(tǒng)的接入流程如圖2所示,主要包括:
1)建立連接:客戶端向服務(wù)器請求建立用于控制信令和TCP數(shù)據(jù)通信的SSL Socket長連接。
2)版本確認(rèn):客戶端與服務(wù)器相互發(fā)送版本信息,包括軟件版本、操作系統(tǒng)版本、其他備注信息等,雙方根據(jù)版本判斷系統(tǒng)訪問的兼容性。
3)用戶登錄:客戶端向服務(wù)器發(fā)送登錄認(rèn)證信息,包括登錄賬戶、登錄密碼,或是用于登錄認(rèn)證的證書信息。
4)密鑰交換:該步驟為可選項,由于SSL Socket完成了TCP連接的密鑰交換,如系統(tǒng)支持UDP傳輸,則需要服務(wù)器發(fā)送UDP加密的密鑰信息給客戶端。
5)群組信息:服務(wù)器向請求登錄的客戶端發(fā)送群組的狀態(tài)信息,包括群組ID、名稱、描述、拓?fù)潢P(guān)系,以及群組中用戶的在線狀況和當(dāng)前狀態(tài)。
6)用戶信息:服務(wù)器向其他客戶端發(fā)送新登錄用戶的狀態(tài)信息,通知該用戶已上線。
7)心跳信息:客戶端完成基本登錄流程,通過發(fā)送周期性的心跳報文,維持長連接狀態(tài)。根據(jù)實(shí)際網(wǎng)絡(luò)狀況可調(diào)整心跳報文的發(fā)送周期,如部署地域的網(wǎng)絡(luò)狀態(tài)較好,可采用3至5秒的發(fā)送間隔。
2.2 信息響應(yīng)
作為集群通信系統(tǒng),信息響應(yīng)的基本單位是群組。每個群組的數(shù)據(jù)處理和信息狀態(tài)相對于其他群組保持獨(dú)立,即群組之間的數(shù)據(jù)和資源是隔離的,這里通過建立用戶信息的HashMap結(jié)構(gòu)hmUsers和群組信息的HashMap結(jié)構(gòu)hmGroups進(jìn)行數(shù)據(jù)調(diào)用和處理。調(diào)用用戶或群組對象時,需通過ReadWriteLock進(jìn)行資源鎖定,保護(hù)線程安全[7]??紤]到多線程存在資源切換和鎖定的開銷,在設(shè)計上主要對不同類型或分組的任務(wù)建立線程,如圖3所示。其中:
1)接收線程主要響應(yīng)網(wǎng)卡資源,監(jiān)聽服務(wù)端口,接收由客戶端發(fā)送的業(yè)務(wù)數(shù)據(jù)或信令信息,如果是心跳消息,則直接返回響應(yīng);否則,將通過hmUsers和hmGourps查找所屬群組,并將數(shù)據(jù)推送到指定群組的消息緩沖區(qū)中。
2)處理線程主要響應(yīng)和實(shí)現(xiàn)具體業(yè)務(wù)。每個群組對象在實(shí)例化過程中都會創(chuàng)建本群組的處理線程和數(shù)據(jù)緩沖區(qū),通過提取緩沖區(qū)中的數(shù)據(jù)并解析,線程將信令或其他業(yè)務(wù)數(shù)據(jù)交給業(yè)務(wù)響應(yīng)函數(shù)處理,語音數(shù)據(jù)交給數(shù)據(jù)響應(yīng)函數(shù)處理,并將處理后的數(shù)據(jù)發(fā)送給指定客戶端。在處理線程中,可分模塊響應(yīng)語音業(yè)務(wù)以外的請求,對預(yù)留接口進(jìn)行功能實(shí)現(xiàn)。
3)存儲線程主要響應(yīng)數(shù)據(jù)I/O操作。本系統(tǒng)對獨(dú)占性的I/O操作采用單線程序列化處理,即所有群組的語音數(shù)據(jù)通過統(tǒng)一的緩沖序列順序解碼、混音和寫入文件,降低線程切換的開銷。為提高I/O讀寫效率,語音數(shù)據(jù)不會立即寫入文件,而是積累時長2秒(可根據(jù)實(shí)際情況配置)的數(shù)據(jù)包后批量寫入。
3 語音處理
3.1 音頻編解碼器選型
目前,國內(nèi)4G LTE網(wǎng)絡(luò)建設(shè)日趨完善,基于移動網(wǎng)絡(luò)的語音通信技術(shù)已擺脫GSM時期小于16kbps的窄帶傳輸限制。在音頻編解碼技術(shù)選型上,更多考慮的是適應(yīng)16kbps到64kbps區(qū)間的高品質(zhì)音頻編解碼算法。
本系統(tǒng)主要對Opus、Speex、AMR-WB和G.722.1四種編解碼方案進(jìn)行比較。Opus是在Skype的SILK編解碼器和的CELT編解碼器基礎(chǔ)上發(fā)展的開源編解碼方案,已形成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)計對比(圖4),以及兩份Google組織的主觀評測報告[9][10],在同等比特率條件下,Opus編碼能夠達(dá)到或超過AMR-WB、G.722.1的編碼質(zhì)量。且考慮到國內(nèi)各地區(qū)移動網(wǎng)絡(luò)的傳輸質(zhì)量差異較大,支持6kbps到510kbps比特率和VBR技術(shù)的Opus編解碼器可為不同網(wǎng)絡(luò)環(huán)境提供更穩(wěn)定的語音通話效果。
3.2 Opus編解碼實(shí)例
Opus是基于C語言編寫的音頻編解碼器,Windows平臺可直接加載Opus源代碼進(jìn)行函數(shù)調(diào)用,Android平臺還需要對源代碼進(jìn)行NDK編譯,生成供Java語言調(diào)用的so庫。
使用Opus進(jìn)行語音編碼的代碼片段如下:
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) { //僅處理采集片段,而非長期占用線程
…
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 混音流程
多路語音的數(shù)據(jù)混音是語音集群通信的核心功能。通過混音算法和調(diào)平參數(shù),同一群組內(nèi)的多路通話語音將根據(jù)逐幀數(shù)據(jù)的時間戳進(jìn)行合幀處理,形成一路語音數(shù)據(jù),送入聲卡緩沖區(qū)進(jìn)行放音。不論語音數(shù)據(jù)是以8位、16位或浮點(diǎn)形式存儲,在多路混音時僅做邏輯疊加運(yùn)算將會導(dǎo)致數(shù)據(jù)越界,必須先通過調(diào)平參數(shù)進(jìn)行數(shù)據(jù)調(diào)整,之后根據(jù)疊加系數(shù)按比例縮減,保證合幀后的語音數(shù)據(jù)不會越界[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í)驗與分析
本系統(tǒng)分別在10M專線、聯(lián)通4G、移動4G、電信4G等網(wǎng)絡(luò)條件下進(jìn)行測試,測試結(jié)果如表1所示。
在不同網(wǎng)絡(luò)環(huán)境下,語音數(shù)據(jù)傳輸?shù)钠骄訒r在可接受范圍,UDP丟包率小于2.2%,網(wǎng)絡(luò)抖動小于20ms。通過主觀測試,語音通話連貫,無連續(xù)丟幀,可懂性良好。因此,語音集群通信系統(tǒng)在傳輸延遲、網(wǎng)絡(luò)抖動等方面可以較好滿足語音集群和實(shí)時通信要求。在編碼處理方面,優(yōu)于國際電聯(lián)G.711語音編碼的90kbps帶寬占用。
5 結(jié)束語
本文主要根據(jù)移動網(wǎng)絡(luò)環(huán)境下的語音集群通信需求,給出一套可行的系統(tǒng)解決方案,并對其中底層鏈路、應(yīng)用構(gòu)建、核心編碼處理等關(guān)鍵環(huán)節(jié)進(jìn)行詳細(xì)闡述。在實(shí)際應(yīng)用過程中,不同領(lǐng)域的業(yè)務(wù)單位可能對系統(tǒng)的數(shù)據(jù)存儲、用戶負(fù)載、網(wǎng)絡(luò)安全有更高的業(yè)務(wù)需求,可以通過合理配置磁盤陣列、負(fù)載均衡、安全網(wǎng)關(guān)、服務(wù)器等硬件設(shè)備或軟件服務(wù)來實(shí)現(xiàn)。
參考文獻(xiàn):
[1] 2015年中國移動語音社交應(yīng)用行業(yè)研究報告[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開發(fā)的聊天工具的設(shè)計與實(shí)現(xiàn)[D]. 成都:電子科技大學(xué), 2013.
[6] 李一鳴, 任勇毛, 李俊. 基于UDP的傳輸協(xié)議性能比較與分析[J]. 計算機(jī)應(yīng)用研究, 2010, 27(10):3906-3910.
[7] 趙興. 基于VoIP技術(shù)的無線語音通信系統(tǒng)設(shè)計[D].長沙: 湖南大學(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.