As3 Socket通信
最近研究了下as3 Socket 跟Java服务器的通信,一些心得分享下。
客户端的代码如下:
package com
{
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.net.Socket;
import flash.utils.ByteArray;
import flash.utils.Endian;
import flash.utils.setTimeout;
/**
* ...
* @author me
*/
public class connection
{
private static const SERVER_URL:String = "localhost";
private static const PORT:int = 10002;
private static var ip:int = 1;
private static var _instance:connection;
private var _socket:Socket;
private static const checkNum:uint = 0;
public function connection()
{
if (_instance)
{
throw new Error("Single");
}
initSocket();
}
public function initSocket():void
{
if (_socket == null)
{
_socket = new Socket(SERVER_URL, PORT);
_socket.timeout = 2000;
_socket.addEventListener(IOErrorEvent.IO_ERROR, onError);
_socket.addEventListener(ProgressEvent.SOCKET_DATA, onProgress);
}
}
public function connectServer():void
{
if (_socket.connected == false)
{
_socket.connect(SERVER_URL, PORT);
}
else
{
trace("has connected the server");
}
}
public function read():void
{
connectServer();
}
public function write(bytes:ByteArray):void
{
connectServer();
_socket.writeUnsignedInt(ip++); //
_socket.writeDouble(10.444);
_socket.writeUnsignedInt(4355);
if (bytes)
{
_socket.writeBytes(bytes, 0, bytes.length);
}
_socket.writeMultiByte("Hello Java啊啊", "GBK");
_socket.flush();
}
private function onError(e:Event):void
{
trace(e.type + ", " + e.toString());
}
private function onProgress(e:ProgressEvent):void
{
//setTimeout(deal, 0.00000000000000000000000000000000000000000000000000001);
deal();
}
private function deal():void
{
var index:int = -1;
if (_socket.bytesAvailable >= 30)
{
//dealString();
index = _socket.readUnsignedInt();
ondeal(index);
}
}
private function ondeal(index:int):void
{
trace("Send to client --> " + index);
var cmd:int = _socket.readUnsignedInt();
trace("cmd -----> " + cmd);
switch(index)
{
case 1:
break;
case 2:
var len:int = _socket.bytesAvailable;
len = _socket.readUnsignedInt();
trace("服务器发的包的长度-- > " + len);
var bytes:ByteArray = getByteArray();
_socket.readBytes(bytes, 0, len);
var str:String = bytes.readMultiByte(len, "GBK");
bytes.position = 0;
trace(str);
break;
}
}
private function dealString():void
{
var i:int = _socket.bytesAvailable;
var str:String = "";
while (i-- > 0)
{
var value:int;
value = _socket.readByte();
if (value != 0)
{
str += String.fromCharCode(value);
}
}
trace(str);
}
private function getByteArray():ByteArray
{
var byteArray:ByteArray = new ByteArray();
byteArray.endian = Endian.LITTLE_ENDIAN;
byteArray.position = 0;
return byteArray;
}
public function get socket():Socket
{
return _socket;
}
static public function get instance():connection
{
if (_instance == null)
{
_instance = new connection();
}
return _instance;
}
}
}
Java服务器端的代码如下:
package test;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketExample {
public static final int PROT = 10002;
private static ServerSocket serverSocket;
private static Socket socket;
private static DataInputStream input;
private static DataOutputStream output;
@SuppressWarnings("unused")
private static OutputStreamWriter outputStreamWriter;
private static InputStreamReader inputStreamReader;
private static char []bytes = new char[1024 * 3];
public static void main(String[] args) throws IOException
{
SocketExample example = new SocketExample();
example.initSocket();
example.read();
}
private void initSocket() throws IOException
{
serverSocket = new ServerSocket(PROT);
System.out.println("start: " + serverSocket);
socket = serverSocket.accept();
input = new DataInputStream(socket.getInputStream());
output = new DataOutputStream(socket.getOutputStream());
outputStreamWriter = new OutputStreamWriter(output, "GBK");
inputStreamReader = new InputStreamReader(input,"GBK");
}
private void read() throws IOException
{
while(true)
{
try {
try {
int cmd = input.readInt();
System.out.println("As3 send a CMD number: " + cmd);
double i = input.readDouble();
System.out.println(i);
int j = input.readInt();
System.out.println(j);
int len = inputStreamReader.read(bytes, 0, 100);
String string = "-->";
string += new String(bytes, 0, len);
System.out.println(string);
write(2, "Hello as3 你好啊啊\n你大爷的啊啊啊".getBytes("GBK"), cmd);
output.flush();
} catch (Exception e) {
// TODO: handle exception
}
finally
{
// socket.close();
}
} catch (Exception e) {
// TODO: handle exception
}
finally
{
// serverSocket.close();
}
}
}
private void write(int index, byte bytes[], int cmd) throws IOException
{
System.out.println("Send to client --> " + index);
output.writeInt(index);
output.writeInt(cmd);
// output.flush();
switch (index) {
case 1:
break;
case 2:
if(bytes != null)
{
output.writeInt(bytes.length);
System.out.println("发送的数据包长度--> " + bytes.length);
output.write(bytes);
// output.flush();
}
break;
default:
break;
}
output.flush();
}
@SuppressWarnings("unused")
private void closeSocket() throws IOException
{
serverSocket.close();
socket.close();
}
}
刚开始的时候as3读取包的时候直接readInt()等读的,遇到了一些莫名其妙的问题,不是遇到文件尾,就是读出了一系列乱七八糟的数据,但是调用setTimeout过段时间再读的话,不管这个时间有多小,例如上面的0....1,都会读取正确。后来断点看了一下,每次读的时候_socket的bytesAvailable即服务器发的包长度在不断的变化,很多情况下都是比服务器发的长度小,这样用字节流读取的话肯定就会遇到文件尾的错误了。因此在as3 Socket通信中读取服务器数据的时候一定要验证服务器包的完整性,即在代码中:
if (_socket.bytesAvailable >= 30)
{
//dealString();
index = _socket.readUnsignedInt();
ondeal(index);
}
当客户端收到的包的长度>=服务器所发的包的长度时,再读取一次,而不是一收到数据就立即读取。但是相反的是,Java代码可以直接读取的,为什么是这样暂时没有搞清楚,看来还是要仔细学习一下TCP/IP通信。
究其原因,应该是AS3每收到一次服务器发的数据,即一串二进制流吧,就会派发ProgressEvent.SOCKET_DATA事件,但同一时间收到的并不是一个完整的通信包,而只是一部分,大概就相当于加载进度条了,每加载一部分就会派发事件,而不是加载完成才派发,加载完成派发Event.COMPLETED事件