Flex2 中flash.net包中新增了一个类:Socket,该类是支持原始的二进制数据的读取(支持Byte,Bytes,Double,Float, Int,Object,Short,UnsignedByte,UnsignedInt,UnsignedShort,UTF,UTFBytes,Boolean 的直接读写),可以直接实现基于TCP的底层的Socket的连接,就象其他语言中的Socket一样,我们可以使用该类,建立Socket连接,通过 Socket连接实现C/S模式的常用应用,如Ftp客户端、点到点网络传输,当然也通过他来连接如QQ、MSN、GTalk等聊天软件的连接。

以下我们将使用Socket类连接 time.nist.gov的time服务,time服务是一种获取互联网时间的服务,默认端口是13,XP的Internet时间同步就是通过连接远端服务器的TIME服务来实现时间同步的(time.window.com/time.nist.gov)。

首先我们构造一个Socket:

private socket=new Socket()

因为,连接远程服务器是需要一个过程,这是个异步的连接,所以我们需要为我们的Socket添加相应的监听器,flash.net.Socket支持七种事件,各自对应Socket连接的相应状态:

  • close,当网络连接关闭时触发。
  • complete,数据导入完成时触发。
  • connect,当网络连接建立时触发。
  • ioError,当发送或者接收数据出现I/O错误时触发。
  • progress,当正在导入数据时触发。
  • securityError,当调用Socket.connect()方法时,连接的服务器超出了安全沙箱限制或者连接的端口小于1024时触发。注意FlashPlayer现在的安全设置,不允许与与swf源服务器以外的服务建立连接。所以下面的示例实际打开会报securityError错(下载本地可以正常运行),解决办法需要使用代理的方式连接外部服务器。当然您可以通过Security.loadPolicyFile( 'http://要连接的服务器/crossdomain.xml' );可以扩展sandbox改变安全设置,很不幸,目前大多数的服务器并没有该文件。关于这个问题,我将在下期的“Flex2发现之旅”中详细说明。
  • socketData,当Socket连接接收到数据时触发。

我们使用addEventListener(type:String, listener:Function)方法为我们刚才创建的socket添加相应事件的监听器:

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);

然后调用Socket的connect(host:String, port:uint)方法与服务器建立Socket连接:

socket.connect("time.nist.gov",13);

Time服务的原理时服务器监听到13端口有新的连接,服务器会马上将本机的时间发送给连接的客户端并断开连接,所有当socket.connect()连接time服务的时候,首先会触发一个 connect事件,表示连接已经建立,然后马上就会接收到服务器返回的数据,触发socketData事件,最后由于服务器断开了连接,又会触发一个 close事件,告诉我们连接已断开,整个Socket连接完成。所有我们要获取远程返回的时间,就必须处理socketData事件,并读取数据:

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);
}

读取数据时,我们循环检查 socket.bytesAvailable,bytesAvailable属性,存放的是尚未读取的数据堆栈的字节大小,而每执行一次 socket.readUnsignedByte(),就从数据堆栈中读取一个UnsignedByte数据,bytesAvailable就减1,一直到bytesAvailable为0的时候表示我们已经将数据堆栈中所有数据读取完成。

至于ti_time.text=input.substring(1,input.length-1)是将获取的数据赋值给TextInput组件,其中input.substring(1,input.length-1)是因为Time服务返回的数据会新起一行,起其返回的时间数据,起始第一个字节是一个换行符,所以用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>