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í)行)

flash.net.Socket API參考