• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 2, 文章 - 73, 評(píng)論 - 60, 引用 - 0
            數(shù)據(jù)加載中……

            [S60] S60中Socket Api的調(diào)用方法[轉(zhuǎn)載]

            翻譯說明
            本文翻譯自英文Wiki部分的Using the sockets API,因?yàn)樵妮^長,所以我將一部分一部分地翻譯,同時(shí)歡迎大家?guī)椭乙黄鹜瓿蛇@個(gè)任務(wù),并且對(duì)我已經(jīng)翻譯的內(nèi)容作修改和指正。

            Contents

            [hide]

                簡介

                通過這篇文章我們想為大家?guī)硪恍㏒ymbian操作系統(tǒng)的有關(guān)sockets API的基本介紹。 本文的讀者應(yīng)該是希望在他們的應(yīng)用程序中增添socket通信功能的Symbian操作系統(tǒng)的開發(fā)者,本文不僅提供了理論介紹,同樣給出了可供實(shí)踐參考的代碼范例。

                本文包含的內(nèi)容有:

                • 概括介紹了有關(guān)socket通信的有關(guān)組件。
                • 概括介紹了socket服務(wù)架構(gòu)以及使用兩個(gè)主要API類RSocketServRSocket的使用。
                • 討論了創(chuàng)建兩個(gè)終端之間進(jìn)行通信的過程。
                • 討論了socket之間通信的不同模式:基于一串?dāng)?shù)據(jù)流的模式以及基于離散消息的模式。
                • 一個(gè)如何使用活動(dòng)對(duì)象來進(jìn)行socket連接的實(shí)踐范例。

                有關(guān)Socket的服務(wù)構(gòu)架

                本文的一個(gè)內(nèi)容是介紹給大家如何將基于Socket服務(wù)的通信功能加入到應(yīng)用程序中來。盡管如此,計(jì)算機(jī)通信系統(tǒng)乃是一個(gè)十分復(fù)雜的系統(tǒng),本文介紹的基于socket服務(wù)的通信仍然是在一個(gè)相對(duì)比較高級(jí)的層次,沒有深入底層探討的話題和技術(shù)。要想讓socket服務(wù)來發(fā)揮作用,許多底層支持軟件將是必須的。

                下圖說明了socket服務(wù)組件在Symbian系統(tǒng)的通信子系統(tǒng)中的哪一層位置,扮演如何一個(gè)角色。

                Image:CommsComponents_CN.gif

                Symbian OS通信系統(tǒng)組件

                首先我們來考慮傳輸層協(xié)議。上圖的Internet互聯(lián)網(wǎng)協(xié)議和紅外協(xié)議,從Symbian 6.0之后支持的藍(lán)牙®無線通訊技術(shù)以及都在這一層中。

                當(dāng)我們談到Internet協(xié)議時(shí),我們其實(shí)包括了一個(gè)隱式的依賴動(dòng)作,那就是向ISP(互聯(lián)網(wǎng)服務(wù)提供商)進(jìn)行撥號(hào)連接。因此,如圖所示我們可以看到Symbian系統(tǒng)提供了撥號(hào)網(wǎng)絡(luò)接入組件。而在本圖中,最重要的的系統(tǒng)組件是電話通信服務(wù)組件。

                最終,我們需要設(shè)計(jì)到一個(gè)硬件設(shè)備,有了硬件我們才可以在選定的網(wǎng)絡(luò)環(huán)境中接收和發(fā)送數(shù)據(jù)。上圖的核心部分就是使用Internet協(xié)議的撥號(hào)接入網(wǎng)絡(luò),并且顯示了串行通信組件在整個(gè)通信系統(tǒng)扮演了如何的角色。串行通信服務(wù)組件通過特定的硬件設(shè)備驅(qū)動(dòng),完成了硬件設(shè)備與它周圍環(huán)境的通信。


                什么是socket?

                那么什么是socket呢? 用一句引自伯克利(Berkeley)UNIX關(guān)于socket實(shí)現(xiàn)的經(jīng)典定義來回答就是“socket就是通信終端”。

                那究竟是什么意思呢?

                一個(gè)socket代表了一條通信‘通道’邏輯上的終端。而實(shí)際上講,socket是物理網(wǎng)絡(luò)地址和邏輯端口號(hào)的一個(gè)集合,而這個(gè)集合可以向另外一個(gè)位置的與他具有相同定義的socket進(jìn)行數(shù)據(jù)傳輸。

                因?yàn)閟ocket是由機(jī)器地址和端口號(hào)來區(qū)分/識(shí)別的,那么在一個(gè)特定的計(jì)算機(jī)網(wǎng)絡(luò)上,每一個(gè)socket都是以此方式被唯一識(shí)別的。這就使得應(yīng)用程序可以唯一地去定位網(wǎng)絡(luò)上的另外一個(gè)位置的socket。

                注意:對(duì)于同一臺(tái)機(jī)器上的兩個(gè)socket,他們是完全具備彼此間進(jìn)行通信的可能的;在這種情況下,兩個(gè)socket具有相同的主機(jī)地址,但是他們擁有不同的端口號(hào)。

                主機(jī)地址和端口號(hào)的組合,對(duì)于不同協(xié)議是不同的。在socket的經(jīng)典應(yīng)用中,網(wǎng)絡(luò)通信使用的是IP(Internet Protocol)協(xié)議,但是實(shí)際上socket是支持很多其它協(xié)議的,對(duì)于這方面的信息稍后會(huì)提到。

                正如我們將會(huì)看到的,不管我們選擇怎樣的通信協(xié)議(傳輸層),我們都可以使用同一種已成熟的socket API來實(shí)現(xiàn)通信。

                協(xié)議模塊

                如上文所述,socket的經(jīng)典應(yīng)用是在TCP/IP協(xié)議的計(jì)算機(jī)網(wǎng)絡(luò)上,使兩個(gè)邏輯端點(diǎn)之間展開通信活動(dòng)。最著名的應(yīng)用TCP/IP的計(jì)算機(jī)網(wǎng)絡(luò),當(dāng)然就是Internet了。

                絕大多數(shù)socket系統(tǒng)的實(shí)現(xiàn)都限定在了TCP/IP網(wǎng)絡(luò)的通信上。

                但是,Symbian系統(tǒng)的socket服務(wù)組件,就實(shí)現(xiàn)了更多的內(nèi)容;不僅如此,它還為其他組件提供了支持模塊插件協(xié)議的基礎(chǔ)構(gòu)架。這就使得Symbian公司和它的開發(fā)伙伴們大大延長了socket服務(wù)組件以及支持socket的應(yīng)用程序的應(yīng)用時(shí)間。

                由于新協(xié)議和傳輸層的引入,支持了新的傳輸‘語言’或協(xié)議的協(xié)議組件,從而使得socket服務(wù)組件可以隨之適應(yīng)新的應(yīng)用環(huán)境。

                隨著Symbian系統(tǒng)第五版的socket服務(wù)組件支持了TCP/IP和紅外協(xié)議的稽核。在Symbian 6.0版的時(shí)候,就增加了藍(lán)牙®無線技術(shù)和短信息服務(wù)插件。

                協(xié)議模塊其實(shí)就是標(biāo)準(zhǔn)的Symbian系統(tǒng)動(dòng)態(tài)鏈接庫(DLL)。他們都有共同的UID2--KUidProtocolModule(0x1000004A)來表示他們的類型,并且擁有特殊的擴(kuò)展名*.PRT。

                一個(gè)關(guān)于經(jīng)典系統(tǒng)的方面,就是socket服務(wù)對(duì)PLP(Psion Link Protocol)協(xié)議也是支持的。PLP被用來進(jìn)行Symbian系統(tǒng)的手機(jī)和運(yùn)行Microsoft Windows的臺(tái)式或筆記本計(jì)算機(jī)之間進(jìn)行通信。PLP的一個(gè)應(yīng)用就是Symbian Connnect - 目前的被用于名為‘PsiWin’的Psion計(jì)算機(jī)。

                socket服務(wù)組件可以以兩種方式加載協(xié)議模塊:

                • 最通常的做法就是,協(xié)議模塊會(huì)在第一個(gè)使用該協(xié)議的socket被打開的時(shí)候進(jìn)行加載。
                • 另外一種做法是,應(yīng)用程序可以顯式地加載協(xié)議模塊。這種做法的一個(gè)好處就在于,當(dāng)協(xié)議加載需要一個(gè)比較長的時(shí)間的時(shí)候,應(yīng)用程序或用戶可以得到相應(yīng)的提示。使用這種方法調(diào)用的API在本文的后面將會(huì)進(jìn)行討論。

                要說明的幾點(diǎn):一個(gè)協(xié)議模塊可以包含多種協(xié)議實(shí)現(xiàn)。比如,在TCPIP.PRT模塊中,就包含了UDP、TCP、ICMP、IP以及DNS協(xié)議的實(shí)現(xiàn)。單個(gè)協(xié)議的實(shí)現(xiàn)可以通過位于\system\data\.的.esk文件進(jìn)行映射。而每個(gè)協(xié)議模塊都有一個(gè).esk文件來指定該模塊所包含的協(xié)議,以及每個(gè)協(xié)議在插件模塊中所處的索引位置。

                傳輸?shù)莫?dú)立性

                上文已經(jīng)提到,socket服務(wù)組件的插件架構(gòu)特性可以使得新的協(xié)議模塊在任何時(shí)間被安裝到一部Symbian系統(tǒng)的手機(jī)當(dāng)中。

                這個(gè)架構(gòu)可以使得socket服務(wù)組件來實(shí)現(xiàn)獨(dú)立傳輸層的概念。借助于提供一個(gè)通用的核心socket API接口,這種架構(gòu)就可以處理所有一般性數(shù)據(jù)傳輸系統(tǒng)的需求,并且通過添加特定協(xié)議的協(xié)議模塊,socket服務(wù)組件就可以被廣大應(yīng)用程序開發(fā)者來給自己的產(chǎn)品增添通信功能,從而省下了大量的開發(fā)通信子系統(tǒng)的時(shí)間。

                隨著時(shí)間的發(fā)展,新的協(xié)議逐步登上歷史舞臺(tái),協(xié)議模塊都將會(huì)為了適應(yīng)socket接口而被重寫。而應(yīng)用程序開發(fā)者,他們只需要增添協(xié)議新近引入的屬性或者動(dòng)作,來支持新的協(xié)議即可,Socket服務(wù)組件便會(huì)使用新的協(xié)議,借助操作系統(tǒng)底層的通信組件,來完成通信機(jī)制,而并不會(huì)影響到上層應(yīng)用程序開發(fā)者的接口和開發(fā)。

                總而言之,socket服務(wù)組件可以讓應(yīng)用程序開發(fā)者在僅僅維護(hù)一套核心API接口的情況下,可以借助操作系統(tǒng)的通信子系統(tǒng)來使用多個(gè)協(xié)議,從而減少了自己的開發(fā)工作量以及開發(fā)時(shí)間。

                “客戶端-服務(wù)器”接口

                Symbian系統(tǒng)的一個(gè)特點(diǎn)就是它具有一個(gè)體積很小的微內(nèi)核(micro-kernel),因此我們只能把必須和硬件設(shè)備交互以及進(jìn)行主機(jī)控制的核心服務(wù)放在內(nèi)核端運(yùn)行。而另外許許多多的系統(tǒng)服務(wù)只能以用戶模式的服務(wù)器線程的形式運(yùn)行,通常被稱為‘系統(tǒng)服務(wù)器’。

                socket服務(wù)組件就是這些‘系統(tǒng)服務(wù)器’中的一個(gè),第三方應(yīng)用程序就借助公開的客戶端API,通過該組件完成通信功能。其中最重要的四個(gè)類為:


                • RSocketServer: 這個(gè)類是用來建立和socket服務(wù)組件之間的連接以及獲取必要的資源的。在客戶端-服務(wù)器架構(gòu)的定義中,該類表示了應(yīng)用程序與socket服務(wù)組件之間建立連接的會(huì)話。所有的其他客戶端接口類,在使用中都需要一個(gè)被打開的本類的實(shí)例來進(jìn)行操作。
                • RSocket: 這個(gè)類表示了一個(gè)socket連接。一個(gè)標(biāo)準(zhǔn)的應(yīng)用程序可能會(huì)在不同時(shí)間的時(shí)候,擁有若干個(gè)RSocket的實(shí)例在同時(shí)進(jìn)行操作。
                • RHostResolver: 這個(gè)類用來提供主機(jī)名稱解析服務(wù)的接口。
                • RNetDatabase: 這個(gè)類用來提供網(wǎng)絡(luò)數(shù)據(jù)庫訪問的接口。

                RSocket, RHostResolver & RNetDatabase 均表示了一個(gè)給定的應(yīng)用程序與socket服務(wù)組件之間進(jìn)行的會(huì)話下的子會(huì)話,而應(yīng)用程序與socket服務(wù)組件之間的會(huì)話就是一個(gè)RSocketServer的實(shí)例。

                sockets服務(wù)器的主要類

                socket服務(wù)組件提供了兩個(gè)主類,供他的客戶端訪問內(nèi)部的API。

                • RSocketServ: 在每個(gè)應(yīng)用程序線程中,只要需要連接socket請求,他就必須使用一個(gè)本類的實(shí)例,來為其他連接(會(huì)話)提供socket服務(wù)。
                • RSocket: 每一個(gè)需要使用socket的應(yīng)用程序線程,同樣也需要一個(gè)或多個(gè)RSocket對(duì)象,這些對(duì)象就是子會(huì)話了。

                下面的兩個(gè)部分將會(huì)介紹會(huì)話和子會(huì)話類(RSocketServ 和 RSocket)的詳細(xì)內(nèi)容。

                使用RSocketServ類

                RSocketServ類扮演了一個(gè)十分重要的角色,因?yàn)樗强蛻舳藨?yīng)用程序與socket服務(wù)組建之間的連接會(huì)話。

                但是,客戶端應(yīng)用程序并不直接使用這個(gè)類來進(jìn)行數(shù)據(jù)的發(fā)送和接收,或者創(chuàng)建一個(gè)遠(yuǎn)程通信端點(diǎn);要完成這些任務(wù)的話,使用的是RSocket類,這個(gè)類將會(huì)在稍后進(jìn)行介紹。

                RSocketServ可以讓客戶端應(yīng)用程序來向socket服務(wù)組件發(fā)起一些查詢,查詢的內(nèi)容包括服務(wù)器支持的協(xié)議個(gè)數(shù)以及支持哪些協(xié)議,每個(gè)支持協(xié)議的具體信息等等。

                希望使用socket的客戶端應(yīng)用程序,都將需要自己創(chuàng)建一個(gè)RSocketServ類的實(shí)例對(duì)象,用這個(gè)對(duì)象來表示該客戶端應(yīng)用程序和socket服務(wù)之間的會(huì)話。每一個(gè)獨(dú)立的socket連接,都是一個(gè)獨(dú)立的RSocket類的實(shí)例對(duì)象??梢哉f,在一個(gè)客戶端應(yīng)用程序中,該程序的RSocketServ類對(duì)象就是所有的RSocket類對(duì)象的容器。

                RSocketServ類的兩個(gè)常用函數(shù)就是Connect()和StandardProtocol()。


                建立一個(gè)連接到sockets服務(wù)的會(huì)話

                使用Connect()方法,應(yīng)用程序就可以建立與socket服務(wù)之間的一個(gè)會(huì)話。它僅僅使用一個(gè)參數(shù)--該會(huì)話所提供的消息通道的個(gè)數(shù)。

                 TInt Connect (TUint aMessageSlots); 

                消息數(shù)參數(shù)被用來限定應(yīng)用程序向socket服務(wù)所同時(shí)并發(fā)的異步操作的請求通道數(shù)。每一個(gè)同步請求都將占用一個(gè)消息通道,并且請求準(zhǔn)備中的異步操作也將占用一個(gè)消息通道。

                一個(gè)普通socket進(jìn)行的讀寫通信操作,都是異步進(jìn)行的,也就是說這樣的操作要占用兩個(gè)消息通道。如果socket也可以進(jìn)行同步操作的話,那么我們其實(shí)并不需要指定過多的消息通道,因?yàn)橥讲僮鞯南⑼ǖ朗怯蓅ocket客戶端-服務(wù)器框架來完成的。對(duì)于你的應(yīng)用程序在同一個(gè)時(shí)間內(nèi)會(huì)使用到多少個(gè)消息通道,這完全是由你來斷定的,而在大多數(shù)情況下,我們要盡可能的減少同時(shí)請求的消息通道數(shù)。

                如果我們不指定任何特定的值,那么系統(tǒng)會(huì)使用一個(gè)默認(rèn)值作為消息通道個(gè)數(shù)的參數(shù):KESockDefaultMessageSlots (0x08)。

                預(yù)載入?yún)f(xié)議模塊

                socket服務(wù)組件載入?yún)f(xié)議協(xié)議模塊的動(dòng)作是動(dòng)態(tài)進(jìn)行的,當(dāng)針對(duì)某一個(gè)協(xié)議的第一個(gè)socket被創(chuàng)建的時(shí)候,該協(xié)議模塊在此時(shí)才會(huì)被載入。盡管如此,載入?yún)f(xié)議仍然是一件比較費(fèi)時(shí)的操作,RSocketServ提供了一個(gè)StartProtocol()函數(shù),來進(jìn)行協(xié)議模塊的預(yù)載入操作,調(diào)用該函數(shù)可以在socket連接請求的時(shí)候節(jié)省載入?yún)f(xié)議模塊的時(shí)間。

                如果你的應(yīng)用程序需要在程序啟動(dòng)之初就載入?yún)f(xié)議模塊,而并非需要連接的時(shí)候才進(jìn)行載入,那么可以使用下面的函數(shù)范例來調(diào)用StartProtocol()方法:

                    void StartProtocol (TUint aFamily, TUint aSockType,
                        TUint aProtocol, TRequestStatus& aStatus);
                        

                StartProtocol()函數(shù)的參數(shù)有:協(xié)議族(例如,KAfInet),使用該協(xié)議的socket類型(例如,KSockStream),協(xié)議族中的協(xié)議標(biāo)示(例如,KProtocolInetTcp),最后一個(gè)參數(shù)是異步調(diào)用的完成狀態(tài)參數(shù)。這些參數(shù)的意義將會(huì)在下面做以簡短介紹。

                請注意,盡管StartProtocol()函數(shù)是一個(gè)異步服務(wù),但是它卻是一個(gè)在操作過程中不能被取消的操作。

                使用RSocket類

                RSocket代表了應(yīng)用程序的一個(gè)socket連接,在一個(gè)應(yīng)用程序中,每一個(gè)socket連接都是一個(gè)單獨(dú)的RSocket的實(shí)例。事實(shí)上,客戶端應(yīng)用程序的代碼中使用更多的是RSocket類而并不是RSocketServ類。

                RSocket是一個(gè)提供了許許多多服務(wù)的體積龐大的類,這些服務(wù)包括:

                • 連接到服務(wù),無論作為客戶端還是服務(wù)端
                • 設(shè)置或者查詢自己的地址,或者查詢遠(yuǎn)程地址
                • 從socket讀取數(shù)據(jù)
                • 向socket寫入數(shù)據(jù)
                • 其他更多...

                在打開任何socket之前,我們必須有一個(gè)激活了的RSocketServ會(huì)話。并且,在上述提到的任何服務(wù)進(jìn)行操作之前,我們要確保socket是打開的。作為打開一個(gè)socket的一部分,RSocket這個(gè)子回話對(duì)象(見上文說明)需要同一個(gè)socket服務(wù)器進(jìn)行連接,這個(gè)服務(wù)器就是一個(gè)RScoketServ類的實(shí)例。

                下面的章節(jié)介紹了RSocket的各種函數(shù),有了這些函數(shù)的介紹和幫助我們就可以寫出基于socket通信的應(yīng)用程序來。

                主機(jī)解析服務(wù)

                什么是主機(jī)解析?

                在一個(gè)由計(jì)算機(jī)組成的網(wǎng)絡(luò)里,獨(dú)立的主機(jī)使用不同的地址格式來判斷各自是誰,是什么。

                例如,你的電子郵件有可能保存在一臺(tái)主機(jī)當(dāng)中,這臺(tái)主機(jī)可能有一個(gè)可讀的地址,比如pop3.freeserve.net。這個(gè)地址盡管對(duì)人來說是可讀的、是一個(gè)具有一定意義的地址,但是對(duì)于網(wǎng)絡(luò)上的計(jì)算機(jī)來說,并沒有任何直接的用處。

                當(dāng)你的郵件客戶端程序嘗試下載你可能會(huì)收到的電子郵件的時(shí)候,你的電腦就會(huì)使用你的電子郵件服務(wù)器的地址(先前舉例的pop3.freeserve.net)去進(jìn)行查詢,將他們相對(duì)應(yīng)的數(shù)字網(wǎng)絡(luò)地址查詢出來。當(dāng)獲得了機(jī)器可讀的數(shù)字網(wǎng)絡(luò)地址,應(yīng)用程序才可能建立起連接。在TCP/IP協(xié)議族中,地址解析轉(zhuǎn)換是由域名解析服務(wù)(Domain Name Service, DNS)進(jìn)行的。

                地址解析服務(wù)的用處有兩個(gè)。首先,它可以讓計(jì)算機(jī)網(wǎng)絡(luò)(在本例中指的是Internet)的用戶可以使用一個(gè)直接的、有意義的、人們可以理解并且可以記住的的地址來指向某一個(gè)網(wǎng)絡(luò)資源。也許你曾經(jīng)見過這樣的網(wǎng)絡(luò)地址212.134.93.203、204.71.202.160,但是一般情況下也許你并不會(huì)使用這樣的數(shù)字地址去訪問網(wǎng)絡(luò),一般情況下你更多使用的是例如www.symbian.com或者www.yahoo.com這樣的地址。

                其次,這種將網(wǎng)絡(luò)物理地址和用戶記憶的網(wǎng)絡(luò)資源地址進(jìn)行分割的服務(wù),達(dá)到了網(wǎng)絡(luò)硬件層進(jìn)行升級(jí)或者替換的情況下并不會(huì)影響到用戶訪問的目的。這種機(jī)制也從另外一種情況下幫助了大的網(wǎng)絡(luò)服務(wù)提供商,比如微軟公司的Hotmail服務(wù),使這些運(yùn)營商可以在世界各地部署本地服務(wù)器,從而讓每一個(gè)用戶獲得更快的訪問速度,無論用戶是在西雅圖或者別的任何地方。

                使用RHostResolver類

                作為客戶端API的一部分,socket服務(wù)組件提供了RHostResolver類,用這個(gè)類我們可以獲得一個(gè)通用的主機(jī)地址解析服務(wù),這項(xiàng)服務(wù)的內(nèi)部會(huì)自己處理相應(yīng)不同協(xié)議的主機(jī)地址解析的細(xì)節(jié)問題。如果我們針對(duì)TCP/IP協(xié)議族而言,那么RHostResolver類扮演的就是客戶端與域名解析服務(wù)(DNS)之間進(jìn)行通信的服務(wù)角色。

                每一個(gè)不同的協(xié)議,都提供了自己的主機(jī)解析服務(wù),這些服務(wù)是作為協(xié)議模塊的一個(gè)標(biāo)準(zhǔn)部分實(shí)現(xiàn)的。這樣的設(shè)計(jì)就使得客戶端可以僅僅訪問RHostResolver類,而并不需要關(guān)心socket使用的是哪一種協(xié)議。

                RHostResolver接口提供了如下幾種功能供客戶端應(yīng)用程序訪問,他們是:

                • 將一個(gè)數(shù)字網(wǎng)絡(luò)地址轉(zhuǎn)換為人所能識(shí)別的包含一定意義的文本表現(xiàn)形式
                • 將人讀地址轉(zhuǎn)換為相對(duì)應(yīng)的機(jī)讀數(shù)字地址
                • 讀取或者設(shè)置本地設(shè)備的主機(jī)名的方法/函數(shù)

                就像是RSocket一樣,RHostResolver類繼承自RSubSessionBase。因此,要想使用RHostResolver類,客戶端應(yīng)用程序就必須先進(jìn)行對(duì)socket服務(wù)組件的服務(wù)器的連接,這個(gè)服務(wù)組件的服務(wù)器就是一個(gè)RSocketServ類的實(shí)例。

                RHostResolver類提供了許多主機(jī)地址解析服務(wù)的函數(shù)/方法,每一個(gè)函數(shù)都提供了兩個(gè)版本的多態(tài)函數(shù)--同步和異步操作。

                請注意,因?yàn)檫@是一個(gè)通用的主機(jī)地址解析接口,但是并不是所有的協(xié)議都提供了主機(jī)地址解析服務(wù),所以有些協(xié)議可能并沒有提供任何主機(jī)地址解析服務(wù)。

                如果客戶端應(yīng)用程序嘗試使用RHostResolver中的函數(shù)去對(duì)一個(gè)不支持主機(jī)地址解析服務(wù)的協(xié)議請求主機(jī)地址解析服務(wù),那么將會(huì)得到錯(cuò)誤代碼KErrNotSupported。

                在進(jìn)行任何主機(jī)地址解析服務(wù)之前,我們要打開一個(gè)RHostResolver類的實(shí)例。正如前面所提到過的,因?yàn)橹鳈C(jī)解析服務(wù)類是一個(gè)子會(huì)話類,所以在調(diào)用RHostResolver::Open()函數(shù)之前,該子會(huì)話類必須關(guān)聯(lián)一個(gè)socket服務(wù)組件的服務(wù)器會(huì)話對(duì)象實(shí)例。

                    TInt Open(RSocketServ& aSocketServer, TUint anAddrFamily,
                        TUint aProtocol);
                        

                下一步,我們將會(huì)根據(jù)上面所示的函數(shù)原形,制定我們希望用哪個(gè)地址類型來解析的主機(jī)地址,地址類型應(yīng)該是和傳遞給RSocket::Open()函數(shù)的參數(shù)一致的。

                最后,我們還需要指定一個(gè)協(xié)議來進(jìn)行主機(jī)地址解析服務(wù)。如果之前選擇的地址類型是協(xié)議無關(guān)類型的,那么我們可以在這里指定KUndefinedProtocol。

                其他的RHostResolver類提供的函數(shù)如下所示:

                    TInt GetByName(const TDesC& aName, TNameEntry& aResult);
                        TInt GetByAddress(const TSockAddr& anAddr, TNameEntry& aResult);
                        TInt GetHostName(TDes& aName);
                        TInt SetHostName(const TDesC& aName); // sync only
                        TInt Next(TNameEntry& aResult);
                        

                這些函數(shù)中的大多數(shù)都是可以見名知意的;不過Next()函數(shù)例外,我們來進(jìn)行一些解釋:對(duì)于有些協(xié)議來說,GetByName()和GetByAddress()函數(shù)可能會(huì)一次找到不止一個(gè)結(jié)果,比如地址假名被允許的時(shí)候。如果這樣的話,我們就需要調(diào)用Next()函數(shù)來返回下一個(gè)地址結(jié)果。

                域名服務(wù)(DNS)

                域名解析服務(wù)(Domain Name Service,DNS)是TCP/IP協(xié)議所提供的主機(jī)解析服務(wù)。

                一個(gè)標(biāo)準(zhǔn)的DNS查詢一般由以下三個(gè)步驟組成:

                • 一個(gè)在某一個(gè)網(wǎng)絡(luò)硬件設(shè)備(例如一塊以太網(wǎng)卡)設(shè)備上運(yùn)行的客戶端應(yīng)用程序,將自己的查詢主機(jī)請求發(fā)送給網(wǎng)絡(luò)上的另外一臺(tái)主機(jī)--DNS服務(wù)器。
                • DNS服務(wù)器將查詢請求進(jìn)行查詢,查詢是在龐大的數(shù)字地址與主機(jī)名稱對(duì)應(yīng)列表中進(jìn)行的,查詢到的結(jié)果將會(huì)被轉(zhuǎn)換成不同的地址格式。
                • DNS服務(wù)器將地址發(fā)送回客戶端。

                請注意,DNS服務(wù)可以將文本格式的地址(例如www.symbian.com)解析為數(shù)值格式地址(例如212.134.93.203),或者將數(shù)值地址(204.71.202.160)解析為文本格式的地址——www.yahoo.com。

                互聯(lián)網(wǎng)服務(wù)提供商一般都提供了很多DNS服務(wù)器(一般都不只一臺(tái))來供他們的客戶使用。如果沒有這些服務(wù)器,那么使用互聯(lián)網(wǎng)對(duì)于普通用戶來說將是一場災(zāi)難。如果沒有DNS的話我們將不得不記住我們感興趣的web站點(diǎn)的32位數(shù)字地址,或者使用十分十分冗長難記的地址去給其他人發(fā)電子郵件。

                這里我們需要注意的重要一點(diǎn)是,實(shí)際上地址轉(zhuǎn)換這項(xiàng)工作并不是客戶端設(shè)備進(jìn)行的,而是待轉(zhuǎn)換地址被發(fā)送到了另外一臺(tái)主機(jī),由另外一臺(tái)主機(jī)進(jìn)行的解析。所以我們在建立一個(gè)使用TPC/IP協(xié)議建立連接的時(shí)候,就必須提供一個(gè)DNS服務(wù)器地址,否則一切連接將幾乎無法進(jìn)行。

                在socket代碼中使用活動(dòng)對(duì)象(active objects)

                計(jì)算機(jī)網(wǎng)絡(luò)通信,在一般情況下都是使用異步操作的。下面我們先放下談?wù)撘丫玫膕ocket通信系統(tǒng),來看看一個(gè)打電話過程是如何進(jìn)行的,這樣會(huì)有助于我們理解下面要討論的問題。

                當(dāng)一個(gè)朋友給你打電話,你的電話機(jī)會(huì)收到電話打入的電信號(hào),它在收到這個(gè)信號(hào)后就開始振鈴,然后你聽到了鈴聲之后就拿起聽筒,開始進(jìn)行通話,直到掛斷電話此次通話結(jié)束。

                當(dāng)?shù)却娫捄艚械臅r(shí)候,我們可以進(jìn)行其他任何事情,并不會(huì)對(duì)我們的生活造成影響。與此的,假如你的朋友給你發(fā)送了一個(gè)是十分困難的問題讓你幫助解決,也許這是一個(gè)相當(dāng)大的難題,你要花一些時(shí)間來考慮或者解決,當(dāng)這個(gè)時(shí)候,你的朋友可以利用你考慮或者解決的時(shí)間,進(jìn)行他自己的其他活動(dòng)。

                上面的電話通信例子,就是一個(gè)很好的一部通信系統(tǒng)的例子。

                當(dāng)我們使用socket來在兩臺(tái)計(jì)算機(jī)之間傳輸數(shù)據(jù)的時(shí)候,我們看到的是一個(gè)類似上面打電話例子的異步模型。

                在一個(gè)使用socket進(jìn)行網(wǎng)絡(luò)通信的應(yīng)用程序中,上述異步通信的事件包括:

                • 連接, 斷開連接以及確認(rèn)請求連接的要求
                • 接受數(shù)據(jù)(因?yàn)槲覀儾⒉恢烙卸嗌贁?shù)據(jù)要發(fā)送過來,所以這個(gè)過程是異步的)
                • 發(fā)出數(shù)據(jù)(因?yàn)閷?duì)于應(yīng)用程序?qū)觼碚f,我們并不知道底層的硬件需要多長時(shí)間才能夠?qū)?shù)據(jù)發(fā)出,所以這個(gè)過程也是異步的)
                • 其他,比如載入?yún)f(xié)議模塊之類的,看似并不是十分明顯的異步操作

                因?yàn)槲覀冃枰趹?yīng)用程序中處理這些異步事件,所以我們需要用到Symbian OS的活動(dòng)對(duì)象(Active objects, AOs)來解決這些問題。

                活動(dòng)對(duì)象的特點(diǎn)有:

                • 使得應(yīng)用程序開發(fā)者可以很容易的控制對(duì)象的生存周期
                • 在一個(gè)單線程程序中完成并非嚴(yán)格意義上的多任務(wù)操作
                • 為Symbian系統(tǒng)提供了效率較高的單線程多任務(wù)解決方案,而并不是真正地使用多線程。

                在Symbian系統(tǒng)中,所有的線程都是通過一個(gè)或者多個(gè)活動(dòng)對(duì)象,使用一個(gè)激活的進(jìn)度管理器來進(jìn)行高效率的外部事件處理。

                一個(gè)活動(dòng)對(duì)象,在一個(gè)時(shí)間內(nèi)只能處理一個(gè)事件源。在實(shí)際情況中,活動(dòng)對(duì)象通常也都是被設(shè)計(jì)為處理一類特定事件的。

                在稍后的代碼示例中,這些代碼因?yàn)橛胁煌男枨笏允褂昧瞬恢挂粋€(gè)活動(dòng)對(duì)象,無論是客戶端還是服務(wù)器程序,都使用了不止三個(gè)活動(dòng)對(duì)象。其中一個(gè)用來處理連接機(jī)制,一個(gè)用來接收數(shù)據(jù),另外一個(gè)用來發(fā)送數(shù)據(jù)。

                下面我們就來看看如合利用活動(dòng)對(duì)象來處理客戶端和服務(wù)器之間進(jìn)行socket流式連接的范例。

                代碼示例: 連接sockets

                下面一部分就是借助代碼的演示來向大家說明如何利用活動(dòng)對(duì)象進(jìn)行socket連接。這寫代碼段是從一個(gè)進(jìn)行監(jiān)聽接入連接的‘服務(wù)器’和發(fā)送連接請求到服務(wù)器的‘客戶端’程序中提取出來的。

                服務(wù)‘監(jiān)聽’類的定義

                下面的代碼是從一個(gè)完整的進(jìn)行‘監(jiān)聽’(listening)的服務(wù)器類定義中取出的一部分。

                class CModel : public CActive {
                        public:
                        void StartEngineL(void);
                        private:
                        void RunL(void);
                        void DoCancel (void);
                        private:
                        RSocketServ iSession;
                        RSocket     iListen, iSocket;
                        CRx*        iRxAO; // 用于接收數(shù)據(jù)的活動(dòng)對(duì)象
                        CTx*        iTxAO; // 用于發(fā)送數(shù)據(jù)的活動(dòng)對(duì)象
                        };

                請注意,在成員變量中有兩個(gè)socket,一個(gè)是用來監(jiān)聽和連接的,而另外一個(gè)是用來處理和客戶端之間進(jìn)行數(shù)據(jù)的傳輸?shù)摹?

                在這個(gè)類的定義中,還有兩個(gè)活動(dòng)對(duì)象,他們是iRxAO和iTxAO。這兩個(gè)活動(dòng)對(duì)象用來在連接到服務(wù)之后異步地、分別地處理數(shù)據(jù)的發(fā)送和接收工作。

                (對(duì)上面已經(jīng)定義的類而言,這個(gè)類僅僅接收一個(gè)客戶端連接,那么請你不要對(duì)自己的創(chuàng)造力作任何限制地去想象和學(xué)習(xí)一下吧,你可以以這個(gè)類定義為基礎(chǔ),將他擴(kuò)展為接收多個(gè)客戶端連接的服務(wù)器吧!)

                下面我們來看看連接過程是如何實(shí)現(xiàn)的。

                做好接收客戶端連接的準(zhǔn)備

                首先,在我們的服務(wù)器沒有進(jìn)行服務(wù)接入請求之前,我們要先創(chuàng)建兩個(gè)socket,創(chuàng)建方法如下所示:

                // Need to use two sockets - one to listen for
                        // an incoming connection.
                        err = iListen.Open(iSession, KAfInet,KSockStream, KUndefinedProtocol);
                        User::LeaveIfError(err);
                        // The second (blank) socket is required to
                        // build the connection & transfer data.
                        err = iSocket.Open(iSession);
                        User::LeaveIfError(err);

                一個(gè)socket叫做iListen,他扮演的就是‘監(jiān)聽者’的角色,用來監(jiān)聽是否有來自客戶端的接入請求。iListen是一個(gè)和協(xié)議流關(guān)聯(lián)的對(duì)象,在本例中這個(gè)協(xié)議就是TCP協(xié)議,因?yàn)槲覀兪褂玫氖荌nternet地址格式。

                另外一個(gè)socket,叫做iSocket,在現(xiàn)在是被構(gòu)造為空socket的,它僅僅在客戶端連接請求的時(shí)候才會(huì)被準(zhǔn)備好進(jìn)入工作狀態(tài)。這個(gè)socket就是用來處理來自客戶端的任何請求,并且進(jìn)行數(shù)據(jù)傳輸工作的。

                那么下面,監(jiān)聽socket就可以去進(jìn)行監(jiān)聽客戶端連接請求的工作了。

                請注意上面例子中使用的兩個(gè)不同的RSocket::Open()函數(shù)的多態(tài)。

                其中第一個(gè),用在iListen成員變量的,它是用來進(jìn)行客戶端請求連接監(jiān)聽的,所以它需要一個(gè)本地地址,只有這樣連接數(shù)據(jù)才能本正確地路由到該對(duì)象。

                要設(shè)定本地地址,我們需要將一個(gè)地址和一個(gè)socket進(jìn)行綁定(bind)操作:

                // Bind the listening socket to the required
                        // port.
                        TInetAddr anyAddrOnPort(KInetAddrAny, KTestPort);
                        iListen.Bind(anyAddrOnPort);

                在本例中,我們并沒有過多考慮socket的網(wǎng)絡(luò)地址,因?yàn)槲覀兪褂玫氖且子诓僮鞯闹鳈C(jī)地址名稱。盡管如此,我們還是需要指定端口號(hào),這樣才能完整確定一個(gè)綁定地址。

                這個(gè)時(shí)候,客戶端就可以通過我們的主機(jī)的Internet主機(jī)地址和端口號(hào)(事先在程序中用#define宏定義好了的KTextPort)向我們的主機(jī)(服務(wù)器)發(fā)送請求了。不過有一點(diǎn),如果我們不向客戶端告知我們的主機(jī)名稱和端口號(hào),那么客戶端將永遠(yuǎn)無法訪問到我們的服務(wù)器。

                還要注意,因?yàn)槲覀兊膕ocket是使用Internet地址格式協(xié)議族進(jìn)行打開操作的,所以我們調(diào)用Bind()函數(shù)時(shí)送入的函數(shù)參數(shù)TSockAddr就是一個(gè)TInetAddr類型的一個(gè)實(shí)例。

                在TInetAddr類中,它除了保存TSockAddr中定義的一般性數(shù)據(jù)值外,還保存了一個(gè)TUint32類型的IP地址數(shù)據(jù)。在協(xié)議族屬性中,TInetAddr類提供的永遠(yuǎn)是KAfInet值,因?yàn)樵撝当硎具@個(gè)地址是一個(gè)TCP/IP地址。

                當(dāng)完成了socket的建立,綁定了監(jiān)聽socket,我們就幾乎完成了所有準(zhǔn)備工作,可以相應(yīng)來自任何客戶端的連接請求。

                下面我們就是需要把接入連接請求創(chuàng)建一個(gè)隊(duì)列,這個(gè)時(shí)候我們需要調(diào)用RScocket::Listen()函數(shù),另外還要注意我們應(yīng)該使用長度為1的隊(duì)列,之后我們看到連接是如何進(jìn)行的時(shí)候,就會(huì)明白這個(gè)隊(duì)列長度是足夠了的。

                void CModel::StartEngineL (void)
                        {
                        …
                        // Listen for incoming connections...
                        iListen.Listen(1);
                        // and accept an incoming connection.
                        // On connection, subsequent data transfer will
                        // occur using the socket iSocket
                        iListen.Accept(iSocket, iStatus);
                        SetActive();
                        ...
                        }

                最后,我們調(diào)用異步函數(shù)RSocket::Accept()來準(zhǔn)備接收客戶端連接請求。

                那么我們再來回顧一下繼承自活動(dòng)對(duì)象CActive類的CModel類,當(dāng)一個(gè)客戶端連接到我們定義的服務(wù)器類的時(shí)候,CModel::RunL()函數(shù)將會(huì)被調(diào)用。

                該函數(shù)被調(diào)用后的過程,請看下一部分。

                處理連接請求

                當(dāng)一個(gè)客戶端連接請求被收到的時(shí)候,最前線的RSocket::Accept()函數(shù)執(zhí)行請求完成,然后活動(dòng)對(duì)象的RunL()函數(shù)將會(huì)被調(diào)用,這一切步驟都是因?yàn)镃Model類是一個(gè)被激活狀態(tài)的活動(dòng)對(duì)象。

                    void CModel::RunL(void)
                        {
                        if (iStatus==KErrNone)
                        {
                        // Connection has been established
                        NotifyEvent(EEventConnected);
                        // Now need to start the receiver AO.
                        iRxAO->RxL(iSocketType);
                        }
                        else // error condition
                        ...
                        }
                        

                那么假設(shè)現(xiàn)在所有步驟都是正常進(jìn)行,那么我們獲得的完成狀態(tài)變量就是KErrNone。在上面的范例代碼中,我們會(huì)向用戶界面層傳遞一個(gè)連接建立成功的消息,然后我們啟動(dòng)活動(dòng)對(duì)象,對(duì)接收到的數(shù)據(jù)進(jìn)行處理,然后連接iSocket進(jìn)行返回?cái)?shù)據(jù)的準(zhǔn)備。

                因?yàn)槲覀冞M(jìn)行操作的是一個(gè)異步系統(tǒng),所以現(xiàn)在因?yàn)榭蛻舳撕头?wù)器是已經(jīng)連接的狀態(tài),那么客戶端可以在任何時(shí)間向服務(wù)器socket發(fā)送數(shù)據(jù)。所以我們需要在接收到數(shù)據(jù)之后,盡可能快地進(jìn)行數(shù)據(jù)的處理。

                有一點(diǎn),在我們進(jìn)行已連接的socket的數(shù)據(jù)發(fā)送的時(shí)候,我們并不會(huì)打開活動(dòng)對(duì)象。數(shù)據(jù)僅僅會(huì)在客戶端程序或者用戶希望發(fā)送數(shù)據(jù)到客戶端的時(shí)候,才進(jìn)行操作。

                使用有連接的socket

                回顧一下我們前面定義的CModel類,我們有一個(gè)成員變量,類型為CRx的iRxAO。

                類CRx是一個(gè)繼承自CActive的類,他也是一個(gè)活動(dòng)對(duì)象。

                CRx類的成員函數(shù)RxL(),定義如下;這個(gè)函數(shù)向連接到我們的服務(wù)器的客戶端發(fā)出了一個(gè)一個(gè)異步請求。

                    void CRx::RxL ( ) //class CRx derived from CActive
                        {
                        // Issue read request
                        iSocket->RecvOneOrMore(iDataBuffer, 0, iStatus, iRecvLen);
                        SetActive();
                        }
                        

                函數(shù)RecvOneOrMore()將會(huì)在稍后,和其他一些讀取以及寫入socket的函數(shù)一同進(jìn)行討論。

                在接入數(shù)據(jù)請求完成的時(shí)候,CRx::RunL()函數(shù)將會(huì)被調(diào)用,完成后返回的內(nèi)容有完成狀態(tài)事件以及新收到的數(shù)據(jù)內(nèi)容。

                那么再來回顧一下CModel類的另外一個(gè)成員變量,類型為CTx的iTxAO。

                類CTx是一個(gè)繼承自CActive的類,他也是一個(gè)活動(dòng)對(duì)象。

                CTx類的成員函數(shù)TxL(),如下所示;他想連接到服務(wù)器的客戶端進(jìn)行了一個(gè)發(fā)送數(shù)據(jù)的一部請求操作。

                    void CTx::TxL (TDesC& aData)
                        {
                        if (!IsActive())
                        {
                        // Take a copy of the data to be sent.
                        iDataBuffer = aData;
                        // Issue write request
                        iSocket->Send(iDataBuffer, 0, iStatus);
                        SetActive();
                        }
                        }
                        

                Send()函數(shù)將會(huì)在稍后,和其他一些讀取以及寫入socket的函數(shù)一同進(jìn)行討論。

                當(dāng)數(shù)據(jù)發(fā)送請求完成的時(shí)候,CTx::RunL()函數(shù)將會(huì)被調(diào)用,同時(shí)返回的內(nèi)容有發(fā)送操作完成的結(jié)果狀態(tài)。

                傳輸數(shù)據(jù)

                現(xiàn)在我們來看看兩臺(tái)網(wǎng)絡(luò)設(shè)備之間,究竟是如何利用socket來進(jìn)行數(shù)據(jù)傳輸?shù)摹?

                如我們以前所知,在socket通信中,數(shù)據(jù)報(bào)通信和數(shù)據(jù)流通信是兩種十分不同的通信方式。

                無論我們使用的是數(shù)據(jù)報(bào)還是數(shù)據(jù)流的傳輸方式,每一個(gè)獨(dú)立的數(shù)據(jù)單元在網(wǎng)絡(luò)通信的兩端被傳輸?shù)臅r(shí)候都有可能經(jīng)過十分不同的路由路徑,因?yàn)樵诰W(wǎng)絡(luò)通信的雙方之間總有著不計(jì)其數(shù)的子網(wǎng)絡(luò),而通信雙方對(duì)數(shù)據(jù)單元的路由方向是無法控制的。這種情況是十分普遍而且正常的,由于數(shù)據(jù)流的傳輸方式也是以數(shù)據(jù)報(bào)形式為基礎(chǔ)的,所以從這個(gè)角度來看的話,二者的路由特點(diǎn)是一致的。


                接收數(shù)據(jù)

                使用無連接的sockets

                下面的函數(shù),是RSocket提供的用來接收無連接的socket的接入數(shù)據(jù)的。

                    void RecvFrom(TDes8& aDesc, TSockAddr& anAddr, TUint flags,
                        TRequestStatus& aStatus);
                        void RecvFrom(TDes8& aDesc, TSockAddr& anAddr, TUint flags,
                        TRequestStatus& aStatus, TSockXfrLength& aLen);
                        

                如果應(yīng)用程序使用的是無連接的socket,那么需要使用RSocket::RecvFrom()這個(gè)這個(gè)方法來讀取從另外一個(gè)遠(yuǎn)程主機(jī)發(fā)送過來的數(shù)據(jù)。

                該函數(shù)的第一個(gè)參數(shù)是一個(gè)字符串,是用來保存接收數(shù)據(jù)的。

                調(diào)用該函數(shù)的程序,會(huì)在一個(gè)完整的數(shù)據(jù)報(bào)接收完成的時(shí)候,得到相應(yīng)的通知。接收數(shù)據(jù)的長度,就是接收字符串的長度。如果接收數(shù)據(jù)報(bào)的長度要比字符串的最大長度更長,那么接收數(shù)據(jù)的末尾將被截去。

                該函書的第二個(gè)參數(shù)是要進(jìn)行接收操作的遠(yuǎn)程主機(jī)的地址。這個(gè)地址需要是一個(gè)根據(jù)socket打開方式定義的協(xié)議格式相匹配的地址。例如,如果打開socket的時(shí)候定義的是TCP/IP協(xié)議,那么這個(gè)地址需要是一個(gè)TInetAddr類型的變量。

                我們會(huì)發(fā)現(xiàn),這個(gè)函數(shù)有兩個(gè)版本的重載,他們都進(jìn)行了同樣的操作,方式也一樣。唯一不同的是,第二個(gè)函數(shù)可以將接收數(shù)據(jù)的長度,顯式地返回給調(diào)用者。

                還有一點(diǎn),一個(gè)單獨(dú)的socket在任何一個(gè)時(shí)間內(nèi),都只有一個(gè)狀態(tài)為等待中的接收操作。

                上面的方法,只能用于無連接(數(shù)據(jù)報(bào))類型的socket連接。

                使用連接的sockets

                下面的函數(shù)是RSocket提供的用來從已經(jīng)連接的socket中讀取數(shù)據(jù)的函數(shù)原形。

                    void Recv(TDes8& aDesc, TUint flags, TRequestStatus& aStatus);
                        void Recv(TDes8& aDesc, TUint flags,
                        TRequestStatus& aStatus,TSockXfrLength& aLen);
                        void RecvOneOrMore(TDes8& aDesc, TUint flags,
                        TRequestStatus& aStatus, TSockXfrLength& aLen);
                        

                如果應(yīng)用程序使用的是已連接的socket,那么應(yīng)該使用上面的函數(shù)來進(jìn)行遠(yuǎn)程主機(jī)的數(shù)據(jù)接收工作。

                和前面的無連接socket類似,這些接收函數(shù)的第一個(gè)參數(shù),仍然是接收數(shù)據(jù)要保存的目標(biāo)字符串變量。

                Recv()函數(shù)會(huì)在目標(biāo)字符串變量被填滿或者連接斷開的時(shí)候完成。在該函數(shù)完成調(diào)用的時(shí)候,讀取數(shù)據(jù)的長度就是字符串的長度,除非在沒有讀取任何數(shù)據(jù)連接就斷開了。

                第二個(gè)Recv()函數(shù)的重載可以顯式地獲取接收數(shù)據(jù)的長度,該長度被保存在了類型為TSockXfrLength的函數(shù)參數(shù)中,這樣的話判斷接收數(shù)據(jù)長度就不必關(guān)聯(lián)接收字符串的長度了。

                最后一個(gè)函數(shù)RecvOneOrMore(),與Recv()不同,這個(gè)函數(shù)是會(huì)在函數(shù)接收到任何數(shù)據(jù)之后立刻返回的。言外之意,調(diào)用RecvOneOrMore()函數(shù)會(huì)接收到1--n個(gè)字節(jié),其中n就是目標(biāo)字符串的長度。同樣地,如果連接被斷開,RecvOneOrMore()函數(shù)仍然會(huì)立刻返回,并且不會(huì)返回任何數(shù)據(jù)。

                雖然是已連接的socket,但是在發(fā)送過程中數(shù)據(jù)流并不一定都是物理上連續(xù)的,盡管從邏輯上看他們是流式的。所以,即便是使用已連接的socket,仍然應(yīng)用程序--socket的調(diào)用者--來進(jìn)行判斷數(shù)據(jù)流的結(jié)束與否,邊界切分等工作。

                注意,由于我們使用的是已連接的socket,那么我們不需要指定接收收據(jù)的socket地址,因?yàn)橐堰B接的socket是在連接動(dòng)作發(fā)生的時(shí)候就已經(jīng)指定好了傳輸目標(biāo)主機(jī)地址信息了。

                在這一部分的前半部分,我們介紹的各種函數(shù)都是具有比較高的復(fù)雜度的,可能對(duì)于應(yīng)用程序開發(fā)者來說并不會(huì)具有特別的吸引力。

                特別地,我們可以注意到所有的函數(shù)都以一個(gè)參數(shù)TUint aFlags作為標(biāo)示作用,到目前為止還沒有對(duì)他進(jìn)行討論。這個(gè)參數(shù)的作用是讓應(yīng)用程序可以選擇特定協(xié)議的指定屬性,以此來設(shè)置協(xié)議接收處理數(shù)據(jù)的方式。

                下面介紹的另外一個(gè)函數(shù)Read(),他將默認(rèn)標(biāo)示參數(shù)設(shè)置為0,并且也去掉了TSockXfrLength類型的參數(shù)。如果使用該函數(shù),那么接收數(shù)據(jù)的長度就只能通過接收目標(biāo)字符串的長度來獲得了。

                     void Read(TDes8& aDesc, TRequestStatus& aStatus);
                        

                除了上述的兩個(gè)例外,這個(gè)Read()函數(shù)的操作效果就基本同Recv()一樣了。

                注意,這個(gè)函數(shù)僅僅在已連接的socket通信中是可以使用的。

                發(fā)送數(shù)據(jù)

                使用未連接的sockets

                下面的函數(shù)是RSocket中用來向未連接的socket發(fā)送數(shù)據(jù)的。

                    void SendTo(const TDesC8& aDesc, TSockAddr& anAddr, TUint flags,
                        TRequestStatus& aStatus);
                        void SendTo(const TDesC8& aDesc, TSockAddr& anAddr, TUint flags,
                        TRequestStatus& aStatus, TSockXfrLength& aLen);
                        

                如果應(yīng)用程序連接的是無連接的socket,那么就要使用RSocket::SendTo()函數(shù)來向遠(yuǎn)程主機(jī)發(fā)送數(shù)據(jù)。

                這個(gè)函數(shù)中的第一個(gè)參數(shù)是包含了要發(fā)送數(shù)據(jù)內(nèi)容的字符串,而要發(fā)送內(nèi)容的長度,則是由字符串的長度決定的。

                當(dāng)數(shù)據(jù)發(fā)送完成的時(shí)候,調(diào)用該函數(shù)的應(yīng)用程序?qū)?huì)得到通知。如果你使用的是帶有TSockXfrLength類型參數(shù)的函數(shù)重載,那么已發(fā)送的數(shù)據(jù)的長度,將會(huì)在完成的時(shí)候被保存在該參數(shù)中。

                第二個(gè)參數(shù)包含了要發(fā)送數(shù)據(jù)的遠(yuǎn)程主機(jī)的地址,這個(gè)地址的格式應(yīng)該符合socket被打開的時(shí)候制定的協(xié)議所支持的地址格式,比如,如果我們選擇了TCP/IP協(xié)議,那么我們就需要使用TInetAddr作為發(fā)送主機(jī)的地址。

                第三個(gè)參數(shù),TUint類型的標(biāo)志位,它是一個(gè)和協(xié)議相關(guān)的位標(biāo)識(shí)符,定義了某些需要向協(xié)議模塊中傳遞參數(shù)的標(biāo)志信息。

                需要注意的是,在一個(gè)socket連接中,在任意時(shí)間最多僅有一個(gè)發(fā)送操作時(shí)處于等待狀態(tài)的。

                上述介紹的函數(shù),僅僅可用于無連接的數(shù)據(jù)報(bào)socket使用。

                使用連接的sockets

                下面的函數(shù),是RSocket提供的用來向一個(gè)已經(jīng)連接的socket發(fā)送數(shù)據(jù)的。

                    void Send(const TDesC8& aDesc, TUint someFlags,
                        TRequestStatus& aStatus);
                        void Send(const TDesC8& aDesc, TUint someFlags,
                        TRequestStatus& aStatus, TSockXfrLength& aLen);
                        

                如果你的應(yīng)用程序使用的是已經(jīng)連接的socket,那么可以使用上面的函數(shù)來向遠(yuǎn)程主機(jī)發(fā)送數(shù)據(jù)。

                和上面類似,該函數(shù)的第一個(gè)參數(shù)是包含了要向遠(yuǎn)程主機(jī)發(fā)送數(shù)據(jù)內(nèi)容的字符串,該字符串的長度就是要發(fā)送數(shù)據(jù)的全部長度。

                Send函數(shù)會(huì)在全部數(shù)據(jù)源發(fā)送完成之后,或者連接斷開之后返回。

                第二個(gè)函數(shù)Send()可以讓調(diào)用者傳遞一個(gè)TSockXfrLength類型的參數(shù)進(jìn)來,以此來確定發(fā)送數(shù)據(jù)的長度,這樣的話傳輸函數(shù)就不必以發(fā)送數(shù)據(jù)的內(nèi)容的字符串長度來作為原數(shù)據(jù)的長度了。

                上面兩種函數(shù)沖在,都提供了一個(gè)TUint someFlags參數(shù),該參數(shù)是用來定義和協(xié)議相關(guān)的標(biāo)示位的,針對(duì)不同協(xié)議會(huì)有不同的協(xié)議標(biāo)示定義。

                正如前面提到的SendTo()函數(shù),上面第二個(gè)方法中的TSockXfrLength類型的參數(shù),會(huì)在異步調(diào)用請求完成的時(shí)候,被賦予已經(jīng)發(fā)送的數(shù)據(jù)的長度。

                請注意,因?yàn)槲覀兪窃谙蛞呀?jīng)連接的socket發(fā)送數(shù)據(jù),所以我們并不需要指定目標(biāo)主機(jī)地址。對(duì)于已經(jīng)連接的socket來說,在socket打開的時(shí)候,遠(yuǎn)程主機(jī)地址就已經(jīng)被指定好了。

                我們目前所提供的函數(shù),可能對(duì)于應(yīng)用程序的開發(fā)者來說還是有些過于復(fù)雜,并且更深入一些。

                對(duì)于下面提供的Write函數(shù)來說,所有的標(biāo)志標(biāo)示符都被去除,他們將使用默認(rèn)值0。另外TSockXfrLength也被去除了,這樣的話,發(fā)送函數(shù)就僅僅從發(fā)送數(shù)據(jù)內(nèi)容的字符串中獲得發(fā)送數(shù)據(jù)的長度了。

                    void Write(const TDesC8& aDesc, TRequestStatus& aStatus);
                        

                除了上面說到的兩個(gè)不同點(diǎn)之外,其它部分都是和Send()函數(shù)幾乎沒有差別的。

                注意,這里提到的發(fā)送數(shù)據(jù)的函數(shù),都僅僅適用于已經(jīng)連接的socket。

                總結(jié)

                本文提供了一些Symbian OS的socket服務(wù)編寫說明,以及如何將通信功能加入到應(yīng)用程序中。

                Socket服務(wù)組件通過兩個(gè)主類RSocketServ和RSocket,提供了一個(gè)近乎標(biāo)準(zhǔn)Socket API的接口。 RSocketServ是連接到sockets服務(wù)的回話進(jìn)程,而RSocket是連接到sockets服務(wù)的子會(huì)話。通過這兩個(gè)類,你可以實(shí)現(xiàn)面向連接或者無連接的socket。主機(jī)解析服務(wù)可以通過RHostResolver類來完成。

                Socket服務(wù)組件的設(shè)計(jì)是基于協(xié)議模塊的,不同的插件模塊實(shí)現(xiàn)了在Socket通信中的不同協(xié)議的細(xì)節(jié)部分。這種設(shè)計(jì)可以使Socket服務(wù)組件可以支持未來的通信協(xié)議,而并不對(duì)服務(wù)組件進(jìn)行升級(jí)。到Symbian OS 6.0為止,被支持的協(xié)議包括 TCP/IP(網(wǎng)絡(luò)控制協(xié)議和互聯(lián)網(wǎng)協(xié)議), IrDA(紅外), SMS(短信) and Bluetooth® (藍(lán)牙無線技術(shù)).

                致謝: 本文是于2005年從www.symbian.com的開發(fā)文章部分引用于此的, 本文的作者是Gavin Meiklejohn。目前指向這篇文章的鏈接已經(jīng)不再有效,所以此處再次發(fā)布這篇十分有價(jià)值的文章。

                posted on 2008-07-30 13:33 郭天文 閱讀(1736) 評(píng)論(0)  編輯 收藏 引用 所屬分類: S60

                国产精品一久久香蕉国产线看观看 | 欧美日韩中文字幕久久久不卡| 丁香久久婷婷国产午夜视频| 久久精品亚洲福利| 亚洲精品无码久久久久去q| 久久精品成人免费网站| 久久综合五月丁香久久激情| 久久精品国产99久久无毒不卡| 久久久精品波多野结衣| 人妻无码久久一区二区三区免费| 国产成人精品久久亚洲高清不卡| 久久亚洲精品无码VA大香大香| 久久国产精品成人免费| 久久人妻无码中文字幕| 一级做a爱片久久毛片| 伊人久久大香线蕉AV色婷婷色 | 色偷偷91久久综合噜噜噜噜| 欧美大香线蕉线伊人久久| 久久久久久久国产免费看| 久久大香香蕉国产| 7777精品伊人久久久大香线蕉| Xx性欧美肥妇精品久久久久久| 99久久99久久精品国产片果冻| 99久久夜色精品国产网站| 久久久久久午夜成人影院| 日韩精品久久久久久久电影| 精品无码人妻久久久久久| 国产精品久久影院| 日产精品久久久一区二区| 亚洲精品无码久久久影院相关影片 | 久久e热在这里只有国产中文精品99| 日韩精品久久久久久免费| 久久精品国产亚洲AV久| 污污内射久久一区二区欧美日韩 | 性欧美大战久久久久久久| 久久99久久无码毛片一区二区| 国产精品久久久久久久久鸭| 日本强好片久久久久久AAA| 性高湖久久久久久久久| 青青草原精品99久久精品66| 久久久久久国产精品无码下载|