as3.0播放条 flash
2011-09-09 20:09:00 iteye_264 阅读数 17
package com.game.mr{
	import flash.media.*;
	public class CSound {
		private var scl:SoundChannel = new SoundChannel();
		private var sound:Sound;
		public function CSound(sd:Sound) {
			this.sound = sd;
		}


		public function play():void {
			scl = sound.play();
		}
		public function stop():void {
			scl.stop();
		}

	}
}

2012-11-23 16:53:07 lhx300 阅读数 18

抄自: http://www.cnblogs.com/Longbin/articles/1929771.html

 

我们在使用ActionScript3.0进行Socket编程的时候需要关注下面的问题,我们将在今后的学习中逐个对下面的问题进行讨论,并尽量逐渐的改进我们的程序.
1.
Socket服务器建立连接.
2.
Socket服务器发送数据.
3.
Socket服务器读数据.
4.
Socket服务器进行握手,并确定收到了什么样的数据和如何处理这些数据.
5.
Socket服务器断开,或者当服务器想与你断开的时候发消息给你.
6.
处理使用Sockets时候引发的错误.


Socket服务器建立连接.

解决方法:
我们通过调用Socket.connect( )或者XMLSocket.connect( )方法并监听网络连接的事件消息.

讨论:
连接一台Socket服务器你需要确定两个信息,一个是Socket服务器的域名或者IP地址,另一个是服务器监听的端口号.
无论你使用的是Socket还是XMLSocket类的实例,连接请求都是完全的一样的,两个类都是使用一个名叫connect()的方法,该方法有两个参数:
host :
该参数为字符串类型,可以是一个域名,例如"www.example.com",也可以是一个IP地址,例如"192.168.1.101".如果Socket服务器与你该Flash影片发布的Web服务器是同一个,该参数为Null.
port :
该参数为一个表示Socket服务器监听端口的int.该值最小为1024.除非在服务器中有一个policy文件,用于指定允许端口号小于1024.
因为Flash Socket编程是一个异步的过程,connect()方法不会等到一个连接完成后再执行下一行代码的执行.如果你想在一个连接完全执行完之前与一个Socket完全绑定,那么你将会得到一个意想不到的结果,并且你当前的代码将不能工作.
在尝试一个新的Socket连接的时候我们最好先添加一个连接事件监听器.当一个连接建立成功,Socket或者XMLSocket会发出一个连接事件,这就可以让你知道交互已经准备好了.
下面举了一个Socket实例与本地Socket服务器的2900端口建立连接的例子:

package {
   import flash.display.Sprite;
   import flash.events.*;
   import flash.net.Socket;

   public class SocketExample extends Sprite {

private var socket:Socket;

public function SocketExample(   ) {
   socket = new Socket(   );
   
   // Add an event listener to be notified when the connection
   // is made
   socket.addEventListener( Event.CONNECT, onConnect );
   
   // Connect to the server
   socket.connect( "localhost", 2900 );
}

private function onConnect( event:Event ):void {
   trace( "The socket is now connected..." );
}

   }
}

如果你想通过XMLSocket与服务器建立连接代码也是基本一样的.首先你创建了一个连接事件监听器,然后调用connect()方法.所不同的是Socket实例改为了XMLSocket:

package {
   import flash.display.Sprite;
   import flash.events.*;
   import flash.net.XMLSocket;

   public class SocketExample extends Sprite {

private var socket:XMLSocket;

public function SocketExample(   ) {
   socket = new XMLSocket(   );
   
   // Add an event listener to be notified when the connection is made
   socket.addEventListener( Event.CONNECT, onConnect );
   
   // Connect to the server
   socket.connect( "localhost", 2900 );
}

private function onConnect( event:Event ):void {
   trace( "The xml socket is now connected..." );
}

   }
}

如果连接失败,可那是下面两种原因的一种:一种是连接立即失败和运行时错误,另一种是如果无法完成连接从而产生一个ioError或者securityError事件.关于错误事件处理信息的描述,我们打算改日讨论.
请牢记,当与一个主机建立一个Socket连接时,Flash Player要遵守如下安全沙箱规则.
1.Flash
.swf文件和主机必须严格的在同一个域名,只有这样才可以成功建立连接.
2.
一个从网上发布的.swf文件是不可以访问本地服务器的.
3.
本地未通过认证的.swf文件是不可以访问任何网络资源的.
4.
你想跨域访问或者连接低于1024的端口,必须使用一个跨域策略文件.
如果尝试连接未认证的域或者低端口服务,这样就违反了安全沙箱策略,同时会产生一个securityError事件.这些情况都可以通过使用一个跨域策略文件解决.无论是Socket对象还是XMLSocket对象的策略文件,都必须在连接之前通过使用flash.system.Security.loadPolicyFile()方法载入策略文件.具体如下:
Security.loadPolicyFile("http://www.rightactionscript.com/crossdomain.xml");
获得的改策略文件不仅定义了允许的域名,还定义了端口号.如果你不设置端口号,那么Flash Player默认为80端口(HTTP协议默认端口).<allow-access-from>标签中可以使用逗号隔开设置多个端口号.下面这个例子就是允许访问80110端口.

<?xml version="1.0"?>

<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" to-ports="80,110" />
</cross-domain-policy>

2.Socket服务器发送数据.

解决方法:
对于Socket对象来说,通过是用write方法(writeByte(),writeUTFBytes( )等方法.)先向缓存区写入数据,然后使用flush()方法发送数据.对于XMLSocket对象,使用send()方法.

讨论:
Socket
XMLSocket类向Socket服务器发送数据的方法是不相同的.让我们首先看一下Socket类的方法.
当你使用Socket对象向服务器发送数据的时候,你首先要将数据写入到一个缓冲区中.Socket类设置了一系列的方法来写数据.每一个方法都用于写不同的数据类型的数据(或者不同的数据).这些方法分别是: writeBoolean( ), writeByte( ), writeBytes( ), writeDouble( ), writeFloat( ), writeInt( ), writeMultiByte( ), writeObject( ), writeShort( ), write- UnsignedInt( ), writeUTF(), writeUTFBytes( ). 这些方法大多数都只接受一个参数,该参数的类型同方法的名字相匹配.例如,writeBoolean()方法接受一个布尔值作为参数, writeByte( ), writeDouble( ), writeFloat( ), writeInt( ), writeShort( ), writeUnsignedInt( ) 方法接受一个数字型参数.writeObject()方法接受一个对象类型作为参数,但该对象必须序列化成为AMF格式.writeBytes( )方法允许你传一个ByteArray参数,并带有偏移量和长度两个参数.例如,下面这段代码,调用了一个writeBytes( )方法,该方法将ByteArray对象中的所有byt值都传出去了(偏移量为0,长度和ByteArray数组长度等长):

socket.writeBytes(byteArray, 0, byteArray.length);

writeUTF( )
writeUTFBytes( ) 方法允许你的发送字符串类型的参数.每个一个方法只接受一个字符串作为参数.writeUTFBytes( )方法简单的将字符串作为Bytes发送.writeUTF( )方法在写入真正数据之前,先写入byts的数量.
writeMultiByte( )
方法也允许字符串类型的参数,但是使用的为非默认字符集.该方法需要两个参数:字符串和字符集名称.FlashFlex的帮助文档中有一个自持所有字符集的列表,该列表中的标签和描述符是一一对应的.使用标签值作为writeMultiByte( )作为字符集.例如下面的代码发送了一个编码为Unicode的字符串:

socket.writeMultiByte("example", "unicode");

相一个Socket对象传数值的方法完全依赖于你所有数据的类型和服务所接受数据的类型.使用一个Socket对象,你完全可以使用 ActionScript写一个TelnetPOP mail客户端.这两种协议都支持ASCII字符指令.例如,在连接一个POP服务器之后,你可以通过使用USER指令指定一个用户.下面代码向一个 Socket对象发一条指令:

// POP servers expect a newline (\n) to execute the preceding command.
socket.writeUTFBytes("USER exampleUsername\n");

向一个Socket对象写入数据其实并没有将数据发送到Socket服务器.每调用一个write方法都向Socket对象添加一个数据.例如,下面代码向一个Socket对象添加了四个byte的数据,但是没有一个发出了.

socket.writeByte(1);
socket.writeByte(5);
socket.writeByte(4);
socket.writeByte(8);

当你想将这些累积的数据发送到Socket服务器需要调用flush()方法.flush()方法调用之后将把所有已经写入的数据发送出去,并清空缓冲区:

socket.flush(   );

XMLSocket
类是一个非常简单用于发送数据的API.写于发数据都是由send()这一个方法来完成的.send()方法可以接受任何数据类型的参数.它可以将所有的参数都转换为一个字符串类型并发送到服务器.通常参数为一个XML对象或者一个包含数据结构类似XML数据的字符串:

xmlSocket.send(xml);

然而,准确的格式完全依赖于服务器所能够接受的格式.如果服务器接受XML格式的数据,你必须发送XML格式的数据.如果服务器只接受URL编码的数据,你也必须发送URL编码的数据.

3.Socket服务器读数据

解决方法:
对于Socket实例,先收到socketData事件,然后调用如下两个方法的一个,比如,readByte()或者readInt(),在事件控制器中确定不会去读过去的bytesAvailable.
对于XMLSocket实例,先收到data事件,然后解析从事件控制器内部装载的XML数据.

讨论:
从一个socket连接接收的数据依赖于你使用的Socket的类型.socketXMLSocket都可以从服务器接受到数据,但是它们处于不同重量级的技术.让我们在讨论XMLSocket之前先关注下Socket.
我都知道socketFlash中是一个异步的行为.因此,它就不能简单的创建一个Socket连接,然后就立刻尝试去读取数据.read方法不能等到从服务器传过来数据之后在返回.换句话说,你只能在客户端从服务器载入所有数据之后才可以读取数据.在数据可用之前读数据会产生一个错误.
通过socketData事件广播到Socket实例,这样我们就可以知道什么时候数据可以被读取.那么我们要为socketData事件添加一个事件监听器,任何时候只要有新的数据从一个socket服务器发送过来,都会触发事件控制器.在事件处理器的内部我们写入我们要执行的代码去读取和处理收到的数据.
从一个前端服务器读取数据,Socket类为我们提供了许多不同的方法,这些方法依赖于你所读得数据类型.例如,你可以通过readByte()方法读一个byte数据,或者通过一个使用readUnsignedInt()方法去读一个无符号整数.下面这个表列出来能够从服务器读取的数据类型,返回值, read方法每次读入的字节数.

Table:Socket read methods for various datatypes
方法:返回值类型       描述       字节数
readBoolean( ):Boolean       
Socket读取一个Boolean.       1
readByte( ):int       
Socket读取一个byte.       1
readDouble( ):Number       
Socket读取一个IEEE 754双精度浮点数.       8
readFloat( ):Number       
Socket读取一个IEEE 754单精度浮点数.       4
readInt( ):int       
Socket读取一个有符号32-bit整数值.       4
readObject( ):*       
Socket读取一个AMF-encoded对象.       n
readShort( ):int       
Socket读取一个有符号16-bit整数值.       2
readUnsignedByte( ):uint       
Socket读取一个无符号字节.       1
readUnsignedInt( ):uint       
Socket读取一个无符号32-bit整数       4
readUnsignedShort( ):uint       
Socket读取一个无符号16-bit整数.       2
readUTF( ):String       
Socket读取一个一个UTF8字符串.       n


有两个额外的方法没有在上面这个表中描述.它们分别是readBytes()readUTFBytes().readBytes()方法只可以让socket读数据但不能返回一个值,并且该方法需要3个参数:

bytes:
一个flash.util.ByteArray实例读取从socket中收到的数据.
offset:
一个uint,指定从什么位置开始读取socket中收到数据的偏移量.默认值为0.
length:
一个uint,用于指定读取bytes的数量.默认值为0,意思就是说将所有的可用的数据都放入ByteArray.

另一个readUTFBytes()方法,只需要一个长度参数用于指定UTF-8字节的读入数量,并且该方法会将所有读入的字节码转换成为字符串类型.

注意:在从一个Socket读数据之前,首先要判断bytesAvailable的属性.如果你不知道要读入的数据类型是什么就去读数据的话,将会产生一个错误(flash.errors.EOFError).

下面的例子代码连接了一个socket服务器,读取并显示每次从服务器发来的数据.

package {
   import flash.display.Sprite;
   import flash.events.ProgressEvent;
   import flash.net.Socket;

   public class SocketExample extends Sprite {

private var socket:Socket;

public function SocketExample(   ) {
   socket = new Socket(   );
   
   // Listen for when data is received from the socket server
   socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData );

   // Connect to the server
   socket.connect( "localhost", 2900 );
}

private function onSocketData( eventrogressEvent ):void {
   trace( "Socket received " + socket.bytesAvailable + " byte(s) of data:" );

   // Loop over all of the received data, and only read a byte if there
   // is one available
   while ( socket.bytesAvailable ) {
       // Read a byte from the socket and display it
       var data:int = socket.readByte(   );
       trace( data );
   }
}
   }
}

在上面的这个例子中,如果一个socket服务器发送回一个消息(例如"hello"),当一个客户段连入服务器就会返回并输出下面类似的文字:

Socket received 5 byte(s) of data:
72
101
108
108
111

注意:一旦数据从socket读出,它就不能再次被读.例如,读一个字节之后,这个字节就不能再"放回来",只能读后边的字节.

当收到的数据为ASCII编码,你可以通过readUTFBytes()方法重新构建一个字符串.readUTFBytes()方法需要知道多少个字节需要转换为字符串.你可以使用bytesAvailable去读所有的字节数据:

var string:String = socket.readUTFBytes(socket.bytesAvailable);

XMLSocket
类的动作和Socket类相比在从服务器接受数据的风格相似.两者都是通过事件监听器来监听数据接收通知的,这主要取决于Flash异步的Socket实现.然而,在处理实际数据的时候有很大的不同.

有个XMLSocket实例在从服务器下载完数据后分发数据事件.通过flash.events.DataEvent.DATA常量定义的数据事件包含一个data属性,该属性包含了从服务器收到的信息.

注意:使用XMLSocket从服务器返回的数据总是认为是一个字符串类型的数据.这样不用为任何数据类型的数据指定读取方法.

这些从服务器返回的数据是没有经过任何处理的原始数据.因此,你不能通过XMLSocket连接立即使用XML,你发送和接收的都是纯字符串数据.如果你期望XML,在你处理数据之前,你必须首先将这些数据转换为一个XML的实例.
下面的这段代码在初始化的时候通过XMLSocket连接到了本地服务器的2900端口.在连接成功之后,一个<test>消息会发送到服务器.onData事件监听者控制从服务器返回的响应.在本例中返回字符串<response><test success='true'/></response>.你可以通过事件的data属性发现为字符串数据,然后XML类的构造函数将字符串转换成为了XML实例.最后,通过使用E4X语法的XML实例的一部分信息.(关于通过使用E4X处理XML的更多详细信息,我们需要另外讨论.)

package {
   import flash.display.Sprite;
   import flash.events.Event;
   import flash.events.DataEvent;
   import flash.net.XMLSocket;

   public class SocketExample extends Sprite {

private var xmlSocket:XMLSocket;

public function SocketExample(   ) {
   xmlSocket = new XMLSocket(   );
   
   // Connect listener to send a message to the server
   // after we make a successful connection
   xmlSocket.addEventListener( Event.CONNECT, onConnect );
   
   // Listen for when data is received from the socket server
   xmlSocket.addEventListener( DataEvent.DATA, onData );

   // Connect to the server
   xmlSocket.connect( "localhost", 2900 );
}

private function onConnect( event:Event ):void {
   xmlSocket.send( "<test/>" );
}

private function onData( eventataEvent ):void {
   // The raw string returned from the server.
   // It might look something like this:
   // <response><test success='true'/></response>
   trace( event.data );
   
   // Convert the string into XML   
 
   var response:XML = new XML( event.data );
   
   // Using E4X, access the success attribute of the "test"
   // element node in the response.
   // Output: true
   trace( response.test.@success );
}
   }
}

注意:data事件分发数据之前,XMLSocket实例必须从服务器收到一个表示为空的byte('\\0').也就是说,从服务器仅仅只发送所需要的字符串是不够的,必须在结尾处加入一个表示为空的byte.

.Socket服务器进行握手,并确定收到了什么样的数据和如何处理这些数据.

解决方法:
创建不同的常量来声明协议的状态.使用这些常量将指定的处理函数映射到相应的状态.在一个socketData事件控制器中,通过状态映射调用这些函数的.

讨论:
建立Socket连接通常要处理握手这个环节.尤其是在服务器初始化需要向客户端发送数据.然后客户端通过一种特殊的方式相应这些数据,接着服务器因此再次响应.整个处理过程直到握手完成并且建立起一个"正常的"连接为止.

处理服务器的不同响应是非难的,主要的原因是socketData事件控制器不能保存上下文的顺序.也就是说,服务器的响应不会告诉你"为什么"响应也不告诉你这些响应数据被那个处理程序来处理.要想知道如何处理这些从服务器返回的响应不能从响应的本身来获得,尤其在响应变化的时候.或许一个响应返回了两个字节码,另一个返回了一个整数值还跟了一个双精度浮点数.这样看来让响应本身处理自己是一大难题.

我们通过创建一个状态量来标注不同的上下文,服务器通过这些上下文将数据发送到客户端.与这些状态量都有一个相关联的函数来处理该数据,这样你就可以很轻松的按照当前的协议状态去调用正确的处理函数.

当你要与一个Socket服务器建立连接需要考虑如下几个步骤:
1.
当与服务器连接的时候,服务器立刻返回一个标志服务器可以支持的最高协议版本号的整数值.
2.
客户端在响应的时候会返回一个实际使用协议的版本号.
3.
服务器返回一个8byte的鉴定码.
4.
然后客户端将这鉴定码返回到服务器.
5.
如果客户端的响应不是服务器端所期望的,或者,就在这个时候该协议变成了一个常规操作模式,于是握手结束.

实际上在第四步可以在鉴定码中包含更多的安全响应.你可以通过发送各种加密方法的密匙来代替逐个发送的鉴定码.这通常使用在客户端向用户索要密码的时候然后密码成为了加密过的8byte鉴定码.该加密过的鉴定码接着返回到服务器.如果响应的鉴定码匙服务器所期望的,客户端就知道该密码是正确的,然后同意建立连接.

实现握手框架,你首先要为处理从服务器返回的不同类型的数据分别创建常量.首先,你要从步骤1确定版本号.然后从步骤3收取鉴定码.最后就是步骤5的常规操作模式.我们可以声明

如下常量:

public const DETERMINE_VERSION:int = 0;
public const RECEIVE_CHALLENGE:int = 1;
public const NORMAL:int = 2;

常量的值并不重要,重要的是这些值要是不同的值,两两之间不能有相同的整数值.

下一个步骤我们就要为不同的数据创建不同处理函数了.创建的这三个函数分别被命名为readVersion( ), readChallenge( )  readNormalProtocol( ). 创建完这三个函数后,我们就必须将这三个函数分别映射到前面不同状态常量,从而分别处理在该状态中收到的数据.代码如下:

stateMap = new Object(   );
stateMap[ DETERMINE_VERSION ] = readVersion;
stateMap[ RECEIVE_CHALLENGE ] = readChallenge;
stateMap[ NORMAL          ] = readNormalProtocol;

最后一步是编写socketData事件处理控制器,只有通过这样的方式,建立在当前协议状态之上的正确的处理函数才可以被调用.首先需要创建一个 currentStateint变量.然后使用stateMap去查询与currentState相关联的函数,这样处理函数就可以被正确调用了.

var processFunc:Function = stateMap[ currentState ];
processFunc(   ); // Invoke the appropriate processing function

下面是一点与薄记相关的处理程序.在你的代码中更新currentState从而确保当前协议的状态.

前面我们所探讨的握手步骤的完整的代码如下:
package {
   import flash.display.Sprite;
   import flash.events.ProgressEvent;
   import flash.net.Socket;
   import flash.utils.ByteArray;

   public class SocketExample extends Sprite {

// The state constants to describe the protocol
public const DETERMINE_VERSION:int = 0;
public const RECEIVE_CHALLENGE:int = 1;
public const NORMAL:int = 2;

// Maps a state to a processing function
private var stateMap:Object;

// Keeps track of the current protocol state
private var currentState:int;

private var socket:Socket;

public function SocketExample(   ) {
   // Initialzes the states map
   stateMap = new Object(   );
   stateMap[ DETERMINE_VERSION ] = readVersion;
   stateMap[ RECEIVE_CHALLENGE ] = readChallenge;
   stateMap[ NORMAL          ] = readNormalProtocol;
   
   // Initialze the current state
   currentState = DETERMINE_VERSION;

   // Create and connect the socket
   socket = new Socket(   );
   socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData );
   socket.connect( "localhost", 2900 );
}

private function onSocketData( eventrogressEvent ):void {
   // Look up the processing function based on the current state
   var processFunc:Function = stateMap[ currentState ];
   processFunc(   );
}

private function readVersion(   ):void {
   // Step 1 - read the version from the server
   var version:int = socket.readInt(   );
   
   // Once the version is read, the next state is receiving
   // the challenge from the server
   currentState = RECEIVE_CHALLENGE;
   
   // Step 2 - write the version back to the server
   socket.writeInt( version );
   socket.flush(   );
}

private function readChallenge(   ):void {
   // Step 3 - read the 8 byte challenge into a byte array
   var bytes:ByteArray = new ByteArray(   );
   socket.readBytes( bytes, 0, 8 );
   
   // After the challenge is received, the next state is
   // the normal protocol operation
   currentState = NORMAL;
   
   // Step 4 - write the bytes back to the server
   socket.writeBytes( bytes );
   socket.flush(   );
}

private function readNormalProtocol(   ):void {
   // Step 5 - process the normal socket messages here now that
   // that handshaking process is complete
}
   }
}

5.Socket服务器断开,或者当服务器想与你断开的时候发消息给你.

解决方法:
通过调用Socket.close( )或者XMLSocket.close( )方法显性的断开与服务器的连接.同时可以通过监听close事件获得服务器主动断开的消息.

讨论:
通常情况下我们需要对程序进行下清理工作.比如说,你创建了一个对象,当这个对象没有用的时候我们就要删除它.因此,无论我们什么时候连接一个 Socket服务器,都要在我们完成了必要的任务之后显性的断开连接一直留着无用的Socket连接浪费网络资源,应该尽量避免这种情况.如果你没有断开一个连接,那么这个服务器会继续保持着这个无用的连接.这样一来就很快会超过了服务器最大Socket连接上线.

Socket
XMLSocket对象断开连接的方法是一样的.你只需要调用close()方法就可以了:

// Assume socket is a connected Socket instance
socket.close(   );   // Disconnect from the server

同样的,XMLSocket对象断开连接的方法一样:

// Assume xmlSocket is a connected XMLSocket instance
xmlSocket.close(   );   // Disconnect from the server

close()
方法用于通知服务器客户端想要断开连接.当服务器主动断开连接会发消息通知客户端.可以通过调用addEventListener()方法注册一个close事件的一个监听器.Socket  XMLSocket都是使用Event.CLOSE作为"连接断开"事件类型的;例如:

// Add an event listener to be notified when the server disconnects
// the client
socket.addEventListener( Event.CLOSE, onClose );

注意:调用close()方法是不会触发close事件的,只用服务器主动发起断开才会触发.一旦一个Socket断开了,就无法读写数据了.如果你想要从新这个连接,你只能再建立个新的连接了.

6.处理使用Sockets时候引发的错误.

解决方法:
使用try/catch处理I/OEOF(end of file)错误.

讨论:
Socket
XMLSocket类对错误的处理很类似.不如,当调用connect()方法的时候,在下面任何一个条件成立的情况下SocketXMLSocket对象会抛出一个类型为SecurityError的错误.

.swf未通过本地安全认证.
端口号大于655535.

当调用XMLSocket对象的send()或者Socket对象的flush()的时候,如果socket还没有连接这两个方法都会抛出一个类型为 IOError的错误.尽管你可以将send()或者flush()方法放入try/catch结构块中,你也不能依赖于try/catch结构块作为你应用程序的逻辑.更好的办法是,在调用send()或者flush()方法之前使用一个if语句首先判断一下Socket对象的connected属性是否为True.例如,下面的代码使用了if语句作为程序逻辑的一部分,Socket对象当前不是连接状态就调用 connectToSocketServer()方法.但是我们依然需要将flush()方法放到try/catch语句块中.通过使用 try/catch语句块将flush()方法抛出的错误写入到日志中:

if ( socket.connected ) {
try {
       socket.flush(   );
}
catch( error:IOError ) {
       logInstance.write( "socket.flush error\n" + error );
}
}
else {
connectToSocketServer(   );
}

所有的Socket类的read方法都能够抛出EOFErrorIOError类型的错误.当你试图读一个数据,但是没有任何可用数据将触发EOF错误.当你试图从一个已经关闭的Socket对象中对数据时将会抛出I/O错误.

除了SocketXMLSocket类的方法能够抛出的错误以外,这些类的对象还会分发错误事件.有两种基本的错误事件类型,他们分别由 socketIOErrorsecurityError错误引起.IOError事件为IOErrorEvent类型,当数据发送或接收失败触发该事件.SecurityError事件是SecurityErrorEvent类型,当一个Socket尝试连接一个服务器,但由于服务器不在安全沙箱范围之内或者端口号小于1024的时候触发该错误事件.

注意:这两种安全策略引起的错误都可以通过跨域访问策略文件解决.

2010-08-27 09:23:27 iteye_18746 阅读数 17
ArrayElementType:

这是指定自己定义的数组中元素的类型,应该类似java中的泛型,用的不多(至少我就没用过,找资料的时候顺便找到的),类似的代码如下:

[ArrayElementType("String")]
public var arrayOfStrings:Array;

这样,这个array就只能放String类型的对象。

Bindable:

毫无疑问这是最常用到的一个标签,几乎所有和数据打交道的地方都会用到这个标签。使用这个标签的属性一旦改变就会dispatch一个事件给监听器,可以选择后面带与不带参数两种方式:

[Bindable]:不带参数就表示使用默认事件,那么flex在满足触发条件是自动发送一个propertyChange事件。

[Bindable(event="eventname")]:带参数表示使用指定的事件,当然首先要把事件给注册好才行。

可以在3个地方使用[Bindable]标签:

1.在public class定义前,这个场景不太常用,这个时候[Bindable]会绑定所有作为变量定义的public属性,并且所有的public属性都定义有 getter和setter方法,[Bindable]没有参数,flex会自动创建一个propertyChange事件来处理所有的公有属性。类似代码如下:

[Bindable]

public class SelfInput extends SimpleWidget

2.在public, protected 或 private 属性前,格式为:
[Bindable]
public var foo:String;

一旦foo值被改变,那么所有使用foo属性的地方都会触发propertyChange事件。

3.在getter和setter事件前定义[Bindable]标签。使用标签时,需要同时定义getter和setter方法,如:

[Bindable]

public function set width(val:Integer):void {
}
public function get width():Integer{
}
如果需要指定发送事件,则如下:

[Bindable(event="redraw")]

public function set width(val:Integer):void {

dispatchEvent(new Event("redraw"));
}
public function get width():Integer{
}


CollapseWhiteSpace

没有找到文档,等到搞明白什么意思的时候再补完。

DataBindingInfo

没有找到这个标签的文档,官方的回答是这是内部标签,只在内部使用的。

DefaultBindingProperty

没有找到这个标签的文档,官方的回答是这是内部标签,只在内部使用的。

DefaultProperty

这个标签也是很少使用的,主要是将一个组件的某一属性指定为他的默认属性,这样在使用该组件时不需要指定属性的名字而是使用该组件的子元素即可。类似代码如下:

定义一个默认属性

[DefaultProperty("text")]

public class SelfInput extends SimpleWidget

使用默认属性

<comp:SelfInput>

<mx:String>testText</mx:String>

</comp:SelfInput>

DefaultTriggerEvent

没有找到这个标签的文档,官方的回答是这是内部标签,只在内部使用的。

Embed

用来把图片,音乐,视频什么的导入到程序中去,可以用这个标签把资源直接整合到swf中去,类似代码如下:

//定义代码

[Embed(source="testIcon.gif")]

public var testIcon:Class

//使用代码

<mx:Button icon="{testIcon}"/>

这时候这个按钮上面就有一个小图标啦。

Event

这个标签主要在编写显示组件时使用,使用这个标签就能够为组件提供一个事件处理函数的接口。定义组件时,可以为该组件赋一个方法在事件触发时调用。类似代码如下:

//定义代码

[Event(name="textChanged", type="flash.events.Event")]
public class SelfInput extends SimpleWidget{

private var _text:String;
public function set text(s:String):void {
_text= s;
var eventObj:Event = new Event(“textChanged");
dispatchEvent(eventObj);
}

}

//调用代码

<mx:Script>

<![CDATA[

public function textChanged(eventObj:Event):void {
trace(eventObj.target.text);
}

]]>

</mx:Script>

<myComp:SelfInput textChanged="textChanged(event)"/>


Effect

用来定义事件触发时产生的效果,和Event标签一起使用。类似代码如下:

//定义代码

[Event(name="textChanged", type="flash.events.Event")]

[Effect(name="textChangedEffect", event="textChanged")]
public class SelfInput extends SimpleWidget{

}

//使用代码

<myComp:SelfInput textChanged="textChanged(event)" textChangedEffect="changeEffect"/>

此时,input组件的text改变时产生效果。使用effect可以制作许多花哨的效果,比如翻页什么的,效果的使用以后再开一篇文章记录。

IconFile

IconFile标签类似Embed标签,也是用来嵌入资源的,只是Embed标签可以嵌入图片、swf、视频、音频等,IconFile只能用来潜入图片文件作为自定义类的图标,在FlexBuilder的Components视窗中找到该组件就能看到它所使用的icon。类似代码如下:

[IconFile(”inputIcon.png”)]

public class SelfInput extends SimpleWidget{

}

Inspectable

属性的代码提示和属性检查,在FlexBuilder中使用该组件时在properties视窗中可以看到该属性的值提示,主要用于一个属性的值为几个固定选项时。类似代码如下:

[Inspectable(category="General", enumeration="text,password", defaultValue="text",type="String")]

public var inputType;

InstanceType

似乎是可以用于模板模式,用于限定引用的对象的类型,与IDeferredInstance一起使用。类似代码如下:

//定义代码

[InstanceType("mx.controls.Button")]

public var uploadBtn:IDeferredInstance;

private function init():void{

addChild(UIComponent(uploadBtn.getInstance()));

}

//使用代码

<myComp:SelfInput>

<myComp:uploadBtn>

<mx:Button label="upload"/>

</myComp:uploadBtn>

</myComp:SelfInput>

还没有遇见过使用场景,不明白为什么不直接使用Button对象引用。

NonCommittingChangeEvent

NonCommittingChangeEvent元数据标签在某个特定事件发生的时候可以防止变量在事件发生的过程中被更改。

示例代码:

[Bindable(event="valueCommit")]
[NonCommittingChangeEvent("change")]
function get text():String {
return getText();
}
function set text(t):void {
setText(t);
// Dispatch events.
}
这样,修改text的时敲入字符就不会触发组件的change事件,只有在所有字符输入完成后才能触发valueCommit事件,可以用于对输入字符进行验证。目前主要遇到的使用场景为在输入框中输入选中组件的宽与高来改变组件的大小,此时不希望改变输入值立刻就改变组件的大小,而是希望改变输入值完成时点击enter键后改变组件的大小,就可以绑定enter事件,不提交change事件。

RemoteClass

大用处。在使用远程调用时将flex的对象和java对象绑定起来,直接将pojo对象通过二进制的方式从前台传递到服务器端。实例代码如下:

package com.priv {
[Bindable]
[RemoteClass(alias="com.priv.UserDo")]
public class UserDo{
public var id:int;

public var name:String;

}
}

这时,java的UserDo对像需要有id和name两个属性的getter和setter,通过BlazeDs框架或是lds框架就能够实现和servlet容器之间的对象远程传递。

ResourceBundle

用于在运行态加载一些文件,目前主要用于做国际化。不过似乎这种国际化方式不能像java那样通过虚拟机的语言来自动指定使用哪个语言文件,需要在编译命令中人工指定使用哪个目录下的资源文件,如下:

-locale zh_CN -allow-source-path-overlap=true -source-path=d:\flex3_projects\wide\locale\zh_CN

之后就可以在d:\flex3_projects\wide\locale\zh_CN这个目录下建立properties文件用于保存国际化信息。properties文件和java的properties文件格式相同。都是如果key=name的键值对。

最后就可以在代码中引用它了:

[ResourceBundle("wide")]

public function init():void{

trace(resourceManage.getString("key"));

}

Style

用于给自定义组件添加样式属性。只需要将style标签添加到类的定义中去,就可以使用getStyle去获取这个样式的值。在as中也能使用setStyle去给组件设置一个样式。实例代码如下:

//定义代码

[Style(name="borderColor",type="uint",format="Color",inherit="no")]
public class SelfInput extends UIComponent{

public function init():void{

trace(getStyle("borderColor"));

}

}

//使用代码

<myComp:SelfInput borderColor="#000000"/>
2012-09-28 16:02:32 gtncwy 阅读数 5125

正则表达式:
       正则表达式最早是由数学家Stephen Kleene在对自然语言的递增研究成果的基础上,于1956提出来的。具有完整语法的正则表达式,主要使用在字符串的格式的匹配方面上,后来也逐渐应用到信息技术领域。不同的编程语言对于正则表达式有细微的不同,ActionScript 3.0是按照ECMAScript第3版语言规范(ECMA-262)中的定义实现正则表达式。
熟悉windows操作系统读者,应该知道windows中是怎样查找文件的。不错,使用通配符“*”用来匹配任意字符,“?”用来匹配一个字符,来实现搜索功能的。
       字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。
       在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
        在ActionScript 3.0中引入了强大的字符串处理工具――正则表达式。正则表达式主要用于验证文本字段的值是否符合特定模式,比如验证用户输入的电话号码位数是否正确;或者替换与特定模式匹配的部分文本值。

正则表达式的构成:正则表达式一般由两个部分组成。一部分是在双斜杠内部的字符串,称为匹配模式,用来表示要匹配的字符串的特征;另一部分是在第二个斜杠的后面,是一个字母,叫做正则表达式的标志位,由五个字母组合而成,匹配模式由两部分组成,分别为:普通字符(例如字符a到z)和特殊字符(称为元字符)。正则表达式描述在查找字符串时待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
正则表达式的定义方法有两种,一种是上例中的方法,使用双斜杠来定义,语法格式如下所示:
var re1:RegExp=/\d{3}/g;
var re2:RegExp=new RegExp("\d{3}","g");

正则表达式的语法:ActionScript 3.0中的正则表达式是按照ECMAScript第3版语言规范(ECMA-262)中语言规范的定义的。在实际应用过程中,正则表达式都比较复杂。不过复杂的正则表达式都是按照一定的语法规则组合而成。
1)正则表达式中的普通字符
正则表达式中的普通字符,包括大小写英文字母,从0到9的数字和除了元字符的其它字符,比如#、!等。注意,中文也属于普通字符,不过它在正则表达式中使用Unicode码表示。由普通字符组成的正则表达式是简单的正则表达式。
下面是几个使用普通字符的正则表达式,代码如下所示:
//使用英文字母作为正则表达式匹配模式
var pattern1:RegExp = /hello/;
//使用中文字符作为正则表达式匹配模式
var pattern2:RegExp = /正则/;
//使用中英文混合字符作为正则表达式匹配模式
var pattern3:RegExp = /正则hello/;
//使用符号和英文字母作为正则表达式匹配模式
var pattern4:RegExp = /#hello~/;

2)正则表达式中的元字符
正则表达式中定义了一些具有特殊含义的字符,这些字符在正则表达式中有特殊的含义,称为元字符。ActionScript 3.0中的元字符如表17-1所示:
1.匹配字符串头部字符
2.匹配字符串尾部字符
3.反斜杠转义字符
4.匹配单个字符
5.数量表示符
6.逻辑“或”操作符

3)正则表达式中的元序列
元序列是指在ActionScript 3.0中具有特殊含义的字符序列。ActionScript 3.0中的元序列如表所示:
元序列     说  明
{n}{n,}和{n,n}   指定前一项目的数值数量或数量范围,属于数量标识符。 
\b      匹配单词字符和非单词字符之间的位置。
\B      匹配两个单词字符之间的位置,也匹配两个非单词字符之间的位置。
\d      匹配十进制数字。
\D      匹配除数字以外的任何字符。
\f      匹配换页符。
\n      匹配换行符。
\r      匹配回车符。
\s      匹配任何空白字符(空格、制表符、换行符或回车符)。
\S      匹配除空白字符以外的任何字符。
\t      匹配制表符。
\unnnn     匹配字符代码由十六进制数字 nnnn 指定的 Unicode 字符。
\v      匹配垂直换页符。
\w      匹配单词字符(A-Z、a-z、0-9 或 _)。请注意,\w 不匹配非英文字符,如 é、Ò 或 Á。
\W      匹配除单词字符以外的任何字符。
\xnn     匹配具有指定 ASCII 值(由十六进制数字 nn定义)的字符。

4)正则表达式中的字符类
字符类是指定义的一组字符以匹配正则表达式中的一个位置。此类使用元字符方括号[]来定义。例如,下面的正则表达式定义了匹配mat、mbt、mct、mdt或met的字符类:
/m[abcde]t/
字符类实现了字符类中只有一个字符符合特征要求,那么就能够实现字符串的匹配。下面示例实现字符类的匹配操作,代码如下所示:
//定义要检测的字符正则表达式
//检查字符串中是否存在正×则
var pattern1:RegExp = /正[123]则/g;
//要检测的字符串
var str1:String="正1则表达式/是一段检测代码,可以是正2则"
//使用String类的match()方法验证
trace(str1.match(pattern1))//输出:正1则,正2则
trace(str1.match(pattern1).length)//输出:2,说明有2个匹配

5)正则表达式中的标志位
标志指指定有关应如何使用正则表达式模式的一些选项,如是否区分大写和小写字符等。这些标志对应与正则表达式类的属性。在ActionScript3.0中有5个标志位,如表所示:
标志  属性   说    明
g   global   匹配多个匹配。
i   ignoreCase  不区分大小写的匹配。应用于 A-Z 和 a-z 字符,但不能应用于扩展字符,如 É 和 é。
m   multiline  设置此标志后,$ 和 ^ 可以分别匹配行的开头和结尾。
s   dotall   设置此标志后,.(点)可以匹配换行符 (\n)。
x   extended  允许扩展的正则表达式。

正则表达式的用法:
在ActionScript3.0中,要搜索和检测正则表达式,既可以利用RegExp类的方法来实现,也可以利用String类的方法类来实现。
1)RegExp类
RegExp类是ActionScript 3.0中的顶级类,它允许使用正则表达式在字符串中执行搜索和替换文本的模式。其有7个属性,如表所示:

正则表达式类属性

属性   返回类型  说明
dotall   Boolean  指定正则表达式模式中的点字符(.)是否与换行符相匹配。
extended  Boolean  指定是否为正则表达式使用扩展模式。
global   Boolean  指定是否为正则表达式使用全局匹配。
ignoreCase  Boolean  指定正则表达式是否忽略大小写。
lastIndex  Number   指定字符串中开始进行下一次搜索的索引位置。
multiline  Boolean  指定是否设置了 m(多行)标志。
source   String   指定正则表达式的模式部分。

2)String类的方法应用
String类与正则表达式的结合,能够实现更为丰富的匹配和搜索效果。在ActionScript 3.0中,String类的search()方法、match()方法和replace()方法常用于正则表达式操作。本节将介绍这三种方法与正则表达式的综合应用。
search()方法主要用于查找作业,返回搜到的字符串的位置索引;match()方法实现的是匹配作业,返回的是匹配的字符串;replace()方法实现替换作业,返回的是替换的结果。


AS3中的正则表达式是RegExp顶级类的实例。这意味着使用RegExp对象时不需要import任何类。

☆理解正则表达式标记
默认情况下,所有标记都为关闭状态。
  g:全局标记,允许表达式在源文本中重复使用,直到没有其他匹配为止。如果未设定该标记,则表达式将返回第一个匹配。
  i:忽略大小写标记,默认情况下,所有字母数字比较都是大小写敏感的。
  m:多行标记,修改^和$锚点在表达式中的行为。关闭时,它们匹配整个文本的首末字符,开启后,它们匹配每行的首末字符。
  s:dotall标记,未设定时点匹配换行符之外的所有字符,设定后点匹配包括换行符在内的任何可能的字符。singleline
  x:扩展标记,设定后将忽略表达式中的空白符,除非经过转义
  全局标记适用于match()、replace()、test()、exec()方法。在String.match()中,它将决定返回第一个匹配和捕获分组的数组(未设定),还是返回所有匹配的数组(设定);在String.replace()中,该标记将指定替换第一个匹配(未设定)还是替换所有匹配(设定)。在RegExp.test()和RegExp.exec()中,如果设定了全局标记,则表达式将继续匹配源文本中的第n个字符,其中n是表达式的lastIndex属性。该属性在每次匹配之后设定,因此如果设定了全局标记,则可以使用RegExp.test()或RegExp.exec()通过循环渐进式遍历所有的匹配。如果未设定全局标记,这些方法将返回从第一个字符开始的第一处匹配。
☆应用正则表达式
·测试
RegExp.test(str:String):Boolean
·定位
String.search(pattern:*):int //没有匹配则返回-1
RegExp.exec(str:String):Object //没有匹配则返回null,这是一个有状态的函数,可以记住上次匹配的位置
var s:String="H12_34_BCD";
var r:RegExp=/\d{2}/ig;
var obj:Object=r.exec(s);
if(obj!=null){
   trace(obj);//12
   trace(obj.index);//1
}
obj=r.exec(s);
if(obj!=null){
   trace(obj);//34
   trace(obj.index);//4
}
·识别
RegExp.exec(str:String):Object

var s:String="H12_34_BCD";
var r:RegExp=/\d{2}/ig;
var obj:Object;
while(obj=r.exec(s)){
   for(var o:String in obj){
    trace(o,obj[o]);
   }
}
/*
   输出结果
   0 12
   index 1
   input H12_34_BCD
   0 34
   index 4
   input H12_34_BCD
   exec()返回的对象包含带有下标的属性和命名属性,并且第一个下标含有与模式匹配的文本
*/
String.match(pattern:*):Array
返回与传递给它的模式相匹配的字符串,没有匹配返回null。
var s:String="H12_34_BCD";
var r:RegExp=/\d{2}/ig;
var obj:Array=s.match(r);
trace(obj.length);//2
·提取
String.match(pattern:*):Array

var s:String="H12_34_BCD";
var r1:RegExp=/_(\d{2})/g;
var r2:RegExp=/_(\d{2})/;
trace(s.match(r1).length);//1
trace(s.match(r2).length);//2
trace(s.match(r2)[0]);//_34
trace(s.match(r2)[1]);//34
传递给match()方法的表达式拥有全局标记集时,将在数组中返回与模式匹配的所有字串。其中包含所有捕获的内容。
传递给match()方法的表达式没有任何全局标记集时,它将在数组中返回第一个匹配的子串和该匹配表达式中的捕获分组。
RegExp.exec(str:String):Object

var s:String="H12_34_BCD";
var r:RegExp=/((\d)(\d))/ig;
var obj:Object;
while (obj=r.exec(s)) {
   for (var o:String in obj) {
    trace(o,obj[o]);
   }
}
/*
   输出结果
   0 12
   1 12
   2 1
   3 2
   index 1
   input H12_34_BCD
   0 34
   1 34
   2 3
   3 4
   index 4
   input H12_34_BCD
   从结果可以看到exec()方法更方便些
*/
·替换
String.replace(pattern:*,repl:Object):String
如果表达式中含有捕获分组,那么在替换文本时可以在替换字符串内部使用捕获文本。通过在替换字符串中使用命名为$1、$2……$99的变量,可以在替换文本中使用第1、第2和第99个捕获分组。
要为替换文本添加几分色彩,可以向replace()方法传递一个替换函数,而不是替换字符串。函数将接收匹配文本、该匹配的所有捕获分组和上下文中的完整字符串,并且可以实现任何所需的替换操作。
var s:String="H12_34_BCD";
var r:RegExp=/\d(\d)/ig;
var refun:Function=function refun(...args):String{
   var rtn:String;
   for(var i:int=0;i<args.length;i++){
    rtn=args[i];
    trace("------",rtn);
   }
   return rtn;
}
trace(s.replace(r,refun));
/*
   输出结果
   ------ 12
   ------ 2
   ------ 1
   ------ H12_34_BCD
   ------ 34
   ------ 4
   ------ 4
   ------ H12_34_BCD
   HH12_34_BCD_H12_34_BCD_BCD
*/
·拆分
String.split(delimiter:*,limit:Number=0x7fffffff):Array

☆构建表达式
如果设定了dotall标记,那么.将匹配任何字符,包括换行符。
var con:String=
"Call us at one of these numbers.\n"+
"Los Angeles:310-555-2910\n"+
"New York:212-555-2499\n"+
"Boston: 617-555-7141";
var re:RegExp=/^([\w\s]+):\s*(\d{3}-\d{3}-\d{4})/gm;
//var matches:Array=con.match(re);
//for(var i:int=0;i<matches.length;i++){
// trace(matches[i]);
//}
var obj:Object;
while(obj=re.exec(con)){
trace(obj[2]);
}
☆构建高级表达式
var s:String=
"<a href=\"http://www.baidu.com\"><span class=\"seoa\">BaiDu</span></a>\n"+
"<a href=\"http://www.google.cn\">Google</a>\n"+
"<li>Ha~~~</li>\n";
var r:RegExp=/<(\w+)[^>]*> .*? <\/\1>/ixg;
var o:Object;
while(o=r.exec(s)){
trace(o[0]);
}

2011-10-12 13:37:13 roadProgram 阅读数 895

一般网上看到的矩阵A*B = C 符合A列==B行 否侧没有效果

比如 1 0 1                 1 0 0                                     2 1 1

          0 0 1         *       0 1 0                      =           1 1 1        

                   (3列)         1 1 1    (3行)

实际就是A行对应数字 *B列 对应数字 想加

1*1+0*0+1*1        1*0+0*1+1*1       1*0+0*0+1*1

0*1+0*0+1*1         0*0+0*1+1*1      1*0+1*0+1*1

看完之口研究了下flash Matrix 里面的concat 组合矩阵 

 var m:Matrix = new Matrix(1,1,1,1,0,1);
var _m:Matrix = new Matrix(1,0,1,3,0,1);
m.concat(_m);

输出 (a=2, b=3, c=2, d=3, tx=1, ty=4) 百思不得其解 后来看资源才知道 ,原来flash 的矩阵 是一般情况下矩阵的转置矩阵(行列互换)

a b tx

c d ty

0 0 1

原先带入公式,计算始终跟输出不一致 输出 2 3 2 3 0 1

应该带入公式

a b 0

c d 0

tx ty 1

这样计算出来就对啦

flash as3.0 图片切换

阅读数 8451

flashas3.0图片切换flashimportfl.transitions.*;importfl.transitions.easing.*;importflash.system.fscommand;fscommand("fullScreen","true");//

博文 来自: jakeorion

FLASH AS3.0监听事件

阅读数 2365

在flash中我们可能会遇到,某要需要播放到某一帧或者某一片段是执行某段代码。我们可以用监听事件来完成,比如当舞台上的某个影片剪辑(super)需要在它播放完之后,播放另一个影片剪辑(man):super.addEventListener(Event.ENTER_FRAME,overHandler);functionoverHandler(e:Event) {v

博文 来自: alpha_xiao

Flash(as3.0) CSound

阅读数 947

packagecom.game.mr{ importflash.media.*; publicclassCSound{ privatevarscl:SoundChannel=newSoundChannel(); privatevarsound:

博文 来自: mrandexe

Flash AS3.0运算符

阅读数 1399

1、算术运算符:-取负-e-xpression+加e-xpression1+e-xpression2-减e-xpression1–e-xpression2*乘e-xpression1*e-xpression2/除e-xpression1/e-xpression2%求余e-xpression1%e-xpression212%5

博文 来自: billyao2006

flash as3.0的简单使用

阅读数 900

//刚接触as3.0,若有错误恳请指正,十分感谢。as(actionscript)提供了在flash中许多交互功能,有两种方法可以使代码生效,一,在时间轴上右键,动作面板内添加代码(快捷键F9)。在这种情况下所有内容视为主类的一部分,运行flash之后在动作面板内的代码直接生效。但该主类内不允许嵌套类,为了使用自定义类,即第二种方法,在flash的主面板属性面板上添加独立的as文件。在所使用的

博文 来自: qq_24714469
没有更多推荐了,返回首页