Flex2 中flash.net包中新增了一個類:Socket,該類是支持原始的二進制數(shù)據(jù)的讀?。ㄖС諦yte,Bytes,Double,Float, Int,Object,Short,UnsignedByte,UnsignedInt,UnsignedShort,UTF,UTFBytes,Boolean 的直接讀寫),可以直接實現(xiàn)基于TCP的底層的Socket的連接,就象其他語言中的Socket一樣,我們可以使用該類,建立Socket連接,通過 Socket連接實現(xiàn)C/S模式的常用應(yīng)用,如Ftp客戶端、點到點網(wǎng)絡(luò)傳輸,當(dāng)然也通過他來連接如QQ、MSN、GTalk等聊天軟件的連接。
以下我們將使用Socket類連接 time.nist.gov的time服務(wù),time服務(wù)是一種獲取互聯(lián)網(wǎng)時間的服務(wù),默認(rèn)端口是13,XP的Internet時間同步就是通過連接遠(yuǎn)端 服務(wù)器的TIME服務(wù)來實現(xiàn)時間同步的(time.window.com/time.nist.gov)。
首先我們構(gòu)造一個Socket:
private socket=new Socket()
因為,連接遠(yuǎn)程服務(wù)器是需要一個過程,這是個異步的連接,所以我們需要為我們的Socket添加相應(yīng)的監(jiān)聽器,flash.net.Socket支持七種事件,各自對應(yīng)Socket連接的相應(yīng)狀態(tài):
- close,當(dāng)網(wǎng)絡(luò)連接關(guān)閉時觸發(fā)。
- complete,數(shù)據(jù)導(dǎo)入完成時觸發(fā)。
- connect,當(dāng)網(wǎng)絡(luò)連接建立時觸發(fā)。
- ioError,當(dāng)發(fā)送或者接收數(shù)據(jù)出現(xiàn)I/O錯誤時觸發(fā)。
- progress,當(dāng)正在導(dǎo)入數(shù)據(jù)時觸發(fā)。
- securityError,當(dāng)調(diào)用Socket.connect()方法時,連接的服務(wù)器超出了安全沙箱限制或者連接的端口小于1024時觸 發(fā)。注意FlashPlayer現(xiàn)在的安全設(shè)置,不允許與與swf源服務(wù)器以外的服務(wù)建立連接。所以下面的示例實際打開會報securityError錯 (下載本地可以正常運行),解決辦法需要使用代理的方式連接外部服務(wù)器。當(dāng)然您可以通過Security.loadPolicyFile( ‘http://要連接的服務(wù)器/crossdomain.xml‘ );可以擴展sandbox改變安全設(shè)置,很不幸,目前大多數(shù)的服務(wù)器并沒有該文件。關(guān)于這個問題,我將在下期的“Flex2發(fā)現(xiàn)之旅”中詳細(xì)說明。
- socketData,當(dāng)Socket連接接收到數(shù)據(jù)時觸發(fā)。
我們使用addEventListener(type:String, listener:Function)方法為我們剛才創(chuàng)建的socket添加相應(yīng)事件的監(jiān)聽器:
socket.addEventListener("close", onClose);
socket.addEventListener("socketData", onDataReceived);
socket.addEventListener("close", onClose);
socket.addEventListener("connect", onConnect);
socket.addEventListener("socketData", onDataReceived);
socket.addEventListener("ioError", onIOError);
socket.addEventListener("progress", onProgress);
socket.addEventListener("securityError", onSecurityError);
然后調(diào)用Socket的connect(host:String, port:uint)方法與服務(wù)器建立Socket連接:
socket.connect("time.nist.gov",13);
Time服務(wù)的原理時服務(wù)器監(jiān)聽到13端口有新的 連接,服務(wù)器會馬上將本機的時間發(fā)送給連接的客戶端并斷開連接,所有當(dāng)socket.connect()連接time服務(wù)的時候,首先會觸發(fā)一個 connect事件,表示連接已經(jīng)建立,然后馬上就會接收到服務(wù)器返回的數(shù)據(jù),觸發(fā)socketData事件,最后由于服務(wù)器斷開了連接,又會觸發(fā)一個 close事件,告訴我們連接已斷開,整個Socket連接完成。所有我們要獲取遠(yuǎn)程返回的時間,就必須處理socketData事件,并讀取數(shù)據(jù):
private function onDataReceived(event:Event):Void { var input:String=""; while ( socket.bytesAvailable > 0 ) { var byte: uint = socket.readUnsignedByte(); input += String.fromCharCode( byte ); } ti_time.text=input.substring(1,input.length-1); traces("Client received: "+input); }
讀取數(shù)據(jù)時,我們循環(huán)檢查 socket.bytesAvailable,bytesAvailable屬性,存放的是尚未讀取的數(shù)據(jù)堆棧的字節(jié)大小,而每執(zhí)行一次 socket.readUnsignedByte(),就從數(shù)據(jù)堆棧中讀取一個UnsignedByte數(shù)據(jù),bytesAvailable就減1,一直 到bytesAvailable為0的時候表示我們已經(jīng)將數(shù)據(jù)堆棧中所有數(shù)據(jù)讀取完成。
至于ti_time.text=input.substring(1,input.length-1)
是將獲取的數(shù)據(jù)賦值給TextInput組件,其中input.substring(1,input.length-1)
是因為Time服務(wù)返回的數(shù)據(jù)會新起一行,起其返回的時間數(shù)據(jù),起始第一個字節(jié)是一個換行符,所以用substring方法將其去處。
TimeClient.mxml完整的代碼如下:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.macromedia.com/2005/mxml" xmlns="*"> <mx:Script> <![CDATA[ import flash.net.*; private var hostName:String = "localhost"; private var port:int = 8080; private var socket:Socket; public function SocketExample() { hostName=ti_server.text; port=int(ti_port.text); bt_send.enabled=false; traces(‘connecting ...‘); socket = new Socket(); configureListeners(socket); socket.connect(hostName, port); } private function configureListeners(dispatcher:IEventDispatcher):Void { dispatcher.addEventListener("close", onClose); dispatcher.addEventListener("connect", onConnect); dispatcher.addEventListener("socketData", onDataReceived); dispatcher.addEventListener("ioError", onIOError); dispatcher.addEventListener("progress", onProgress); dispatcher.addEventListener("securityError", onSecurityError); } private function onClose(event:Event):Void { traces("onClose: " + event); bt_send.enabled=true; bt_close.enabled=false; } private function onConnect(event:Event):Void { traces("onConnect: " + event); bt_send.enabled=true; bt_close.enabled=true; } private function onDataReceived(event:Event):Void { traces("onDataReceived: " + event); var input:String=""; while ( socket.bytesAvailable > 0 ) { var byte: uint = socket.readUnsignedByte(); var next: uint; input += String.fromCharCode( byte ); } ti_time.text=input.substring(1,input.length-1); traces("Client received: "+input); } private function onIOError(event:IOErrorEvent):Void { traces("onIOError: " + event); } private function onProgress(event:ProgressEvent):Void { traces("onProgress loaded:" + event.bytesLoaded + " total: " + event.bytesTotal); } private function onSecurityError(event:SecurityErrorEvent):Void { traces("onSecurityError: " + event); } private function traces(msg:String):Void{ ta_debug.text=ta_debug.text+"\n"+msg; } private function close():Void{ socket.close(); } ]]> </mx:Script> <mx:Canvas width="100%" height="100%"> <mx:Label x="31" y="18" text="Server:"/> <mx:TextInput x="79" y="18" id="ti_server" text="time.nist.gov"/> <mx:Label x="44" y="51" text="Port:"/> <mx:TextInput x="79" y="50" width="53" text="13" id="ti_port"/> <mx:Label x="39" y="79" text="Time:"/> <mx:TextInput x="79" y="80" id="ti_time" editable="false"/> <mx:Button x="79" y="112" label="Get Time" id="bt_send" click="SocketExample();"/> <mx:Button x="166" y="112" label="Close" id="bt_close" click="close();"/> <mx:HRule x="35" y="139" height="20" width="408"/> <mx:Label x="37" y="152" text="Debug Info:"/> <mx:TextArea x="38" y="175" width="404" height="111" editable="false" id="ta_debug"/> </mx:Canvas> </mx:Application>
運行示例(FlashPlayer8.5 required,由于安全限制,運行會出錯,請下載到您的硬盤,本地執(zhí)行)