[AS/FLEX] Flex AS3与Java的Socket通信
新建flash文件 :SocketExample.fla
添加按钮:btnSend
修改文档 类为:SocketExample
新建AS3文件:SocketExample.as
01.
package
{
02.
import
flash.display.Sprite;
03.
import
flash.events.*;
04.
import
flash.net.Socket;
05.
06.
public
class
SocketExample
extends
Sprite {
07.
08.
private
var socket:Socket;
09.
10.
public
function SocketExample( ) {
11.
socket =
new
Socket( );
12.
13.
// Add an event listener to be notified when the connection
14.
// is made
15.
socket.addEventListener( Event.CONNECT, onConnect );
16.
17.
// Listen for when data is received from the socket server
18.
socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData );
19.
20.
//var btnSend:Button = new Button();
21.
btnSend.label =
"Send Data"
;
22.
btnSend.emphasized =
true
;
23.
btnSend.width =
150
;
24.
btnSend.move(
20
,
20
);
25.
addChild(btnSend);
26.
27.
btnSend.addEventListener(MouseEvent.CLICK, sendData);
28.
}
29.
30.
private
function sendData( event:Event ) {
31.
32.
if
(!socket.connected) {
33.
34.
// Connect to the server
35.
socket.connect(
"192.168.2.103"
,
5678
);
36.
}
37.
38.
trace(
"send..."
);
39.
socket.writeUTFBytes(
"example\n"
);
40.
socket.flush();
41.
42.
}
43.
44.
private
function onConnect( event:Event ):
void
{
45.
trace(
"The socket is now connected..."
);
46.
}
47.
48.
private
function onSocketData( eventrogressEvent ):
void
{
49.
trace(
"Socket received "
+ socket.bytesAvailable +
" byte(s) of data:"
);
50.
51.
trace(socket.readMultiByte(socket.bytesAvailable,
"UTF-8"
));
52.
}
53.
54.
55.
}
56.
}
Java 服务器 端:
MyServer .java
01.
import
java.io.*;
02.
import
java.net.*;
03.
04.
public
class
MyServer {
05.
public
static
void
main(String[] args)
throws
IOException{
06.
ServerSocket server=
new
ServerSocket(
5678
);
07.
08.
while
(
true
) {
09.
Socket client=server.accept();
10.
BufferedReader in=
new
BufferedReader(
new
InputStreamReader(client.getInputStream()));
11.
PrintWriter out=
new
PrintWriter(client.getOutputStream());
12.
//while(true){
13.
String str=in.readLine();
14.
System.out.println(str);
15.
out.println(
"has receive...."
);
16.
out.flush();
17.
if
(
"end"
.equals(str))
18.
break
;
19.
//}
20.
client.close();
21.
}
22.
}
23.
}
客户端 控制台输出:
send...
The socket is now connected...
Socket received 17 byte(s) of data:
has receive....
外加一些AS3的Socket方法:
我们在使用ActionScript3.0进行Socket编程的时候需要关注下面的问题,我们将在今后的学习 中逐个对下面的问题进行讨论,并尽量逐渐的改进我们 的程序 .
1.与Socket服务 器建立连接.
2.向Socket服务器发送数据 .
3.从Socket服务器读数据.
4.同Socket服务器进行握手,并确定收到了什么样的数据和如何处理这些数据.
5.与Socket服务器断开,或者当服务器想与你断开的时候发消息给你.
6.处理使用Sockets时候引发的错误.
1.与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端口建立连接的例子:
01.
package
{
02.
import
flash.display.Sprite;
03.
import
flash.events.*;
04.
import
flash.net.Socket;
05.
06.
public
class
SocketExample
extends
Sprite {
07.
08.
private
var socket:Socket;
09.
10.
public
function SocketExample( ) {
11.
socket =
new
Socket( );
12.
13.
// Add an event listener to be notified when the connection
14.
// is made
15.
socket.addEventListener( Event.CONNECT, onConnect );
16.
17.
// Connect to the server
18.
socket.connect(
"localhost"
,
2900
);
19.
}
20.
21.
private
function onConnect( event:Event ):
void
{
22.
trace(
"The socket is now connected..."
);
23.
}
24.
25.
}
26.
}
如 果你想通过XMLSocket与服务器建立连接代码也是基本一样的.首先你创建了一个连接事件监听器,然后调用connect()方法.所不同的是 Socket实例改为了XMLSocket:
01.
package
{
02.
import
flash.display.Sprite;
03.
import
flash.events.*;
04.
import
flash.net.XMLSocket;
05.
06.
public
class
SocketExample
extends
Sprite {
07.
08.
private
var socket:XMLSocket;
09.
10.
public
function SocketExample( ) {
11.
socket =
new
XMLSocket( );
12.
13.
// Add an event listener to be notified when the connection is made
14.
socket.addEventListener( Event.CONNECT, onConnect );
15.
16.
// Connect to the server
17.
socket.connect(
"localhost"
,
2900
);
18.
}
19.
20.
private
function onConnect( event:Event ):
void
{
21.
trace(
"The xml socket is now connected..."
);
22.
}
23.
24.
}
25.
}
如 果连接失败,可那是下面两种原因的一种:一种是连接立即失败和运行时错误,另一种是如果无法完成连接从而产生一个ioError或者 securityError事件.关于错误事件处理信息的描述,我们打算改日讨论.
请牢记,当与一个主机建立一个Socket连接时,Flash Player要遵守如下安全沙箱规则.
1.Flash的.swf文件和主机必须严格的在同一个域名,只有这样才可以成功建立连接.
2.一个从网上发布的.swf文件是不可以访问 本地服务器的.
3.本地未通过认证的.swf文件是不可以访问任何网络资源的.
4.你想跨域访问或者连接低于1024的端口,必须使用一个跨域策略文件.
如果尝试连接未认证的域或者低端口服务,这样就违反了安全沙箱策略,同时会产生一个securityError事件.这些情况都可以通过使用一个跨域策略 文件解决.无论是Socket对象 还是XMLSocket对象的策略文件,都必须在连 接之前通过使用 flash.system.Security.loadPolicyFile()方法载入策略文件.具体如下:
1.
Security.loadPolicyFile(
"[url=http://www.rightactionscript.com/crossdomain.xml ]http://www.rightactionscript.com/crossdomain.xml [/url]"
);
获得的改策略文件不仅定义了允许的 域名,还定义了端口号.如果你不设置 端口号,那么Flash Player默认为80端口(HTTP 协议默认端口).在<allow- access-from>标签中可以使用逗号隔开设置多个端口号.下面这个例子就是允许访问80和110端口.
1.
<?
xml
version
=
"1.0"
?>
2.
3.
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd ">
4.
<
cross-domain-policy
>
5.
<
allow-access-from
domain
=
"*"
to-ports
=
"80,110"
/>
6.
</
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( )方法也允许字符串类型的参数,但是使用的为非默认字符集.该方法需要两个参数:字符串和字符集名称.在Flash和Flex 的帮助文档中有一个自持所有字符集的列表,该列表中的标签和描述符是一一对应的.使用标签值作为 writeMultiByte( )作为字符集.例如下面的代码发送了一个编码为Unicode的字符串:
socket.writeMultiByte("example", "unicode");
相一个Socket对象传数值的方法完全依赖于你所有数据的类型和服务所接受数据的类型.使用一个Socket对象,你完全可以使用 ActionScript写一个Telnet和POP 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的类型.socket和XMLSocket都可以从服务器接受到数据,但是它们处于不同重量 级的技术 .让我们在讨论XMLSocket之前先关注下 Socket类.
我都知道socket在Flash中是一个异步的行为.因此,它就不能简单的创建一个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服务器,读取并显示每次从服务器发来的数据.
01.
package
{
02.
import
flash.display.Sprite;
03.
import
flash.events.ProgressEvent;
04.
import
flash.net.Socket;
05.
public
class
SocketExample
extends
Sprite {
06.
07.
private
var socket:Socket;
08.
09.
public
function SocketExample( ) {
10.
socket =
new
Socket( );
11.
12.
// Listen for when data is received from the socket server
13.
socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData );
14.
// Connect to the server
15.
socket.connect(
"localhost"
,
2900
);
16.
}
17.
private
function onSocketData( eventrogressEvent ):
void
{
18.
trace(
"Socket received "
+ socket.bytesAvailable +
" byte(s) of data:"
);
19.
// Loop over all of the received data, and only read a byte if there
20.
// is one available
21.
while
( socket.bytesAvailable ) {
22.
// Read a byte from the socket and display it
23.
var data:
int
= socket.readByte( );
24.
trace( data );
25.
}
26.
}
27.
}
28.
}
在 上面的这个例子中,如果一个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><testsuccess='true'/></response>.你可以通过事件的 data属性发现为字符串数据,然后XML类的构造函数 将字符串转换成为了XML实例.最后,通过使用 E4X语法的XML实例的一部分信息.(关于通过使用E4X处理XML的更多详细信息,我们需要另外讨论.)
01.
package
{
02.
import
flash.display.Sprite;
03.
import
flash.events.Event;
04.
import
flash.events.DataEvent;
05.
import
flash.net.XMLSocket;
06.
public
class
SocketExample
extends
Sprite {
07.
08.
private
var xmlSocket:XMLSocket;
09.
10.
public
function SocketExample( ) {
11.
xmlSocket =
new
XMLSocket( );
12.
13.
// Connect listener to send a message to the server
14.
// after we make a successful connection
15.
xmlSocket.addEventListener( Event.CONNECT, onConnect );
16.
17.
// Listen for when data is received from the socket server
18.
xmlSocket.addEventListener( DataEvent.DATA, onData );
19.
// Connect to the server
20.
xmlSocket.connect(
"localhost"
,
2900
);
21.
}
22.
23.
private
function onConnect( event:Event ):
void
{
24.
xmlSocket.send(
"<test/>"
);
25.
}
26.
private
function onData( eventataEvent ):
void
{
27.
// The raw string returned from the server.