Flex2 发现之旅:flash.net.Socket的使用
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等聊天软件的连接...
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>