一个简单的聊天客户端,主要介绍flex如何与c++服务器端传递消息、解析消息。使用flash builder4和flex4.0开发。
客户端提供连接服务器,发送和接收通讯消息(发送登陆消息,发送用户列表请求消息等)。
客户端运行后的界面:

解决方案:

一、客户端主逻辑
客户端的主逻辑都在“ChatClientFX.mxml”文件中实现。
“ChatClientFX.mxml”文件:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="570" minHeight="300" width="570" height="300"
initialize="Init()">
<fx:Script>
<![CDATA[
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import mx.events.FlexEvent;
protected var _socketCon:SocketConnector;//网络连接
private var _arrayRole:ArrayList = new ArrayList();//用户列表
private var _strRoleName:String = "";//用户名
private var _nRoleIndex:int = -1;//用户连接在服务器的索引
private var _nReqRoleListMsgIndex:int = -1;//请求用户列表消息索引
private var _dictRoles:Dictionary = new Dictionary();//存放收到的用户列表
private function Init():void
{
_socketCon = new SocketConnector();
_socketCon.addEventListener(ReceiveEvent.Event_NetMsg, OnReceiveEvent);
var bhave:Boolean = _socketCon.hasEventListener(ReceiveEvent.Event_NetMsg);
listRole.dataProvider = _arrayRole;
_strRoleName = tbName.text;
tbSend.addEventListener(FlexEvent.ENTER, OnEnterText);
}
private function OnEnterText(e:FlexEvent):void
{
SendText();
}
//处理接收到的消息
private function OnReceiveEvent(e:ReceiveEvent):void
{
var netMsg:NetMsg = e.m_netMsg;
if (netMsg == null)
return;
switch (netMsg.AbNetMsgHead.Type)
{
case EnumType.NetMsg_RoleEvent:
{
var nRoleIndex1:int = netMsg.GetIntValue("nRoleIndex");
var strRoleName1:String = netMsg.GetStringValue("szRoleName");
switch (netMsg.GetIntValue("nType"))
{
case EnumType.NetMsg_RoleEvent_ValideLogin:
{
_nRoleIndex = nRoleIndex1;
_strRoleName = strRoleName1;
tbName.text = _strRoleName;
//发送请求用户列表消息
SendReqRoleList();
}
break;
case EnumType.NetMsg_RoleEvent_RoleIn:
{
_dictRoles[nRoleIndex1] = strRoleName1;
ReflashRoleList();
}
break;
case EnumType.NetMsg_RoleEvent_RoleOut:
{
delete _dictRoles[nRoleIndex1];
ReflashRoleList();
}
break;
default:
break;
}
}
break;
case EnumType.NetMsg_ChatInfo:
{
switch (netMsg.GetIntValue("nChatType"))
{
case 1:
case 2:
{
//var strMsg:String = netMsg.GetStringValue("szFromName") + " 对 " + netMsg.GetStringValue("szToName") + " 说: " + netMsg.GetStringValue("szContent");
var strMsg:String = netMsg.GetStringValue("szFromName") + " 说: " + netMsg.GetStringValue("szContent");
ShowText(strMsg);
}
break;
default:
break;
}
}
break;
case EnumType.NetMsg_RoleInfoList:
{
if (netMsg.AbNetMsgHead.Number == _nReqRoleListMsgIndex)
{
var detailArr:Array = netMsg.GetArrayValue("info");
for (var i:int = 0; i < detailArr.length; i++)
{
var detail:NetMsgItem = detailArr[i] as NetMsgItem;
var nRoleIndex:int = detail.GetIntValue("nRoleIndex");
var strRoleName:String = detail.GetStringValue("szRoleName");
_dictRoles[nRoleIndex] = strRoleName;
}
if (netMsg.AbNetMsgHead.EndFlag == EnumType.NetMsg_EndFlag_End)
{
//最后一个列表消息
//更新_arrayRole,并将_nReqRoleListMsgIndex置-1
ReflashRoleList();
}
}
}
break;
default:
break;
}
}
//显示聊天内容
public function ShowText(strText:String):void
{
if (strText == "")
return;
if (tbShow.text.length > 2000)
tbShow.text = "";
tbShow.text += strText + "\r\n";
tbShow.scroller.verticalScrollBar.value = tbShow.scroller.verticalScrollBar.maximum;
}
protected function btCon_clickHandler(event:MouseEvent):void
{
// 开始连接到服务器
_socketCon.m_socket.addEventListener(Event.CONNECT, OnConnect);
var bState:Boolean = _socketCon.Connect(tbIP.text, int(tbPort.text));
}
//连接服务处理方法
private function OnConnect(event:Event):void
{
var str:String = "已连接到: " + tbIP.text + ":" + tbPort.text;
ShowText(str);
SendRoleName();
_socketCon.m_socket.removeEventListener(Event.CONNECT, OnConnect);
_socketCon.m_socket.addEventListener(Event.CLOSE, OnDisConnect);
}
//断开服务连接处理方法
private function OnDisConnect(event:Event):void
{
var str:String = "已断开连接: " + tbIP.text + ":" + tbPort.text;
ShowText(str);
_socketCon.m_socket.removeEventListener(Event.CLOSE, OnDisConnect);
}
//发送请求用户列表消息
private function ReflashRoleList():void
{
_arrayRole.removeAll();
_nReqRoleListMsgIndex = -1;
for (var n:String in _dictRoles)
{
_arrayRole.addItem(_dictRoles[n]);
}
}
private function SendReqRoleList():void
{
var msg:NetMsg = _socketCon.GetNetMsgProcessor.GetSendNetMsg(EnumType.NetMsg_RoleEvent);
if (msg != null)
{
_nReqRoleListMsgIndex = msg.AbNetMsgHead.Number;
msg.SetValue("nType", EnumType.NetMsg_RoleEvent_ReqRoleList);
msg.SetValue("szRoleName", _strRoleName);
msg.SetValue("nRoleIndex", _nRoleIndex);
_socketCon.Send(msg);
}
}
//发送登陆消息
private function SendRoleName():void
{
var msg:NetMsg = _socketCon.GetNetMsgProcessor.GetSendNetMsg(EnumType.NetMsg_RoleEvent_Login);
if (msg == null)
return;
msg.SetValue("nType", EnumType.NetMsg_RoleEvent_Login);
msg.SetValue("szRoleName", _strRoleName);
_socketCon.Send(msg);
}
protected function btSend_clickHandler(event:MouseEvent):void
{
// TODO Auto-generated method stub
SendText();
}
//发送聊天消息
private function SendText():void
{
var msg:NetMsg = _socketCon.GetNetMsgProcessor.GetSendNetMsg(EnumType.NetMsg_ChatInfo);
if (msg == null)
return;
msg.SetValue("nChatType", EnumType.NetMsg_ChatInfo_Community);
msg.SetValue("szFromName", _strRoleName);
msg.SetValue("szToName", "大家");
msg.SetValue("szContent", tbSend.text);
_socketCon.Send(msg);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- 将非可视元素(例如服务、值对象)放在此处 -->
</fx:Declarations>
<s:TextInput x="98" y="10" id="tbIP" width="86" text="127.0.0.1"/>
<s:TextInput x="10" y="268" id="tbSend" width="290"/>
<s:TextInput x="192" y="10" id="tbPort" width="41" text="4503"/>
<s:TextInput x="320" y="10" id="tbName" width="70" text="FLEX"/>
<s:Button x="10" y="10" label="连接到" id="btCon" click="btCon_clickHandler(event)"/>
<s:TextArea x="10" y="39" id="tbShow" width="380" height="221"/>
<s:Button x="320" y="269" label="发送" id="btSend" click="btSend_clickHandler(event)"/>
<s:Label x="275" y="13" text="昵称:" fontSize="16"/>
<s:Panel x="395" y="10" width="161" height="281" dropShadowVisible="false" borderVisible="true" title="参与聊天的人" fontWeight="normal">
<s:List y="0" width="159" height="248" x="0" id="listRole"></s:List>
</s:Panel>
</s:Application>
二、客户端消息
客户端的消息结构定义在MSGConfig.xml文件中,同服务器端一样只有三种消息结构。
“MSGConfig.xml”文件:定义消息结构
<?xml version="1.0" encoding="utf-8"?>
<Config>
<Mapping type="1" name="RoleEvent">
<Int name="nType"/>
<String name="szRoleName" length="65" charset="utf-8"/>
<Int name="nRoleIndex"/>
</Mapping>
<Mapping type="2" name="ChatInfo" comment = " 聊天">
<Int name="nChatType"/>
<String name="szFromName" length="35" charset="utf-8" comment = " 聊天发送者名称"/>
<String name="szToName" length="35" charset="utf-8" comment = " 聊天接收者名称"/>
<String name="szContent" length="300" charset="utf-8" comment = " 聊天内容"/>
</Mapping>
<Mapping type="3" name="RoleInfoList" comment = " 用户列表">
<Int name="nCount" comment = " 总数量"/>
<Array name="info">
<Item>
<Int name="nRoleIndex" comment = "帐号索引"/>
<String name="szRoleName" length="65" charset="utf-8" comment = " 发送人名称"/>
</Item>
</Array>
</Mapping>
</Config>
“MsgConfig.as”文件:将“MSGConfig.xml”中的消息格式读到内存中待用。
/********************************************************************
author: 田园小蛙 ourtree@live.cn
purpose: 消息格式配置文件
*********************************************************************/
package
{
import flash.events.*;
import flash.net.*;
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
public class MsgConfig
{
private var _msgTypeMap:Dictionary = new Dictionary();
private var _configXml:XML = null;
public var m_bLoadFinish:Boolean = false;
public function LoadConfig():void
{
if(m_bLoadFinish)
{
return;
}
var externalReq:URLRequest = new URLRequest("MSGConfig.xml");
var externalLoad:URLLoader = new URLLoader();
externalLoad.load(externalReq);
externalLoad.addEventListener(Event.COMPLETE, TextReady);
}
private function TextReady(event:Event):void
{
_configXml = new XML(event.target.data);
ReadMsgConfigXML();
m_bLoadFinish = true;
}
private function ReadMsgConfigXML():void
{
var mapList:XMLList = _configXml.elements("Mapping");
for each(var mapElement:XML in mapList)
{
var nType:int = mapElement.attribute("type");
_msgTypeMap[nType] = mapElement;
}
}
public function GetMappingByType(nType:int):XML
{
return _msgTypeMap[nType];
}
}
}
“NetMsgHead.as”文件:消息头文件
/********************************************************************
author: 田园小蛙 ourtree@live.cn
purpose: 消息头
*********************************************************************/
package
{
public class NetMsgHead
{
private var m_size:int = 0;
private var m_type:int = 0;
private var m_endFlag:int = 0;
private var m_number:int = 0;
public function get Size():int
{
return m_size;
}
public function get Type():int
{
return m_type;
}
public function get EndFlag():int
{
return m_endFlag;
}
public function get Number():int
{
return m_number;
}
public function SetValue(nSize:int, nType:int, nEndFlag:int, nNumber:int):void
{
this.m_size = nSize;
this.m_type = nType;
this.m_endFlag = nEndFlag;
this.m_number = nNumber;
}
public function NetMsgHead()
{
}
}
}
“NetMsg.as”文件:消息类,根据字节流生成消息,根据消息生成字节流。这个类比银光客户端的“NetMsg”类更简洁一些。
/********************************************************************
author: 田园小蛙 ourtree@live.cn
purpose: 消息类
*********************************************************************/
package
{
import flash.utils.ByteArray;
import flash.utils.Endian;
public class NetMsg
{
private var _paramMap:Object = new Object();
private var _netMsgHead:NetMsgHead = new NetMsgHead();
protected var _mappingElement:XML = null;
//得消息头
public function get AbNetMsgHead():NetMsgHead
{
return _netMsgHead;
}
public function NetMsg()
{
}
//初始化一个不包含字节流的空消息供发送用
public function InitNetMsg(xmlItem:XML, nType:int, nNumber:int):void
{
_mappingElement = xmlItem;
this._netMsgHead.SetValue(0, nType, 0, nNumber);
}
//根据字节流生成消息(包含消息头的完整消息包)
public function Parse(byteArray:ByteArray, xmlItem:XML):void
{
var nSize:int = byteArray.readInt();
var nType:int = byteArray.readInt();
var nEndFlag:int = byteArray.readInt();
var nNumber:int = byteArray.readInt();
this._netMsgHead.SetValue(nSize, nType, nEndFlag, nNumber);
_mappingElement = xmlItem;
this.PushData(byteArray);
}
//根据字节流生成消息(传入字节中已经去掉了消息头)
protected function PushData(byteArray:ByteArray):void
{
var xmlList:XMLList = this._mappingElement.children();
for each(var node:XML in xmlList)
{
var valueName:String = node.attribute("name");
if(node.name() == "Int")
{
this.SetValue(valueName, byteArray.readInt());
}
else if(node.name() == "String")
{
var stringLength:int = new Number(node.attribute("length"));
var charSet:String = node.attribute("charset");
if(charSet == null)
{
charSet = "utf-8";
}
var value:String = byteArray.readMultiByte(stringLength, charSet);
this.SetValue(valueName, value);
}
else if(node.name() == "Boolean")
{
this.SetValue(valueName, byteArray.readBoolean());
}
else if(node.name() == "Array")
{
byteArray.position = byteArray.position - 4;
var count:int = byteArray.readInt();
this.SetValue(valueName, this.GetArray(byteArray, count, node));
}
else if(node.name() == "Byte")
{
var b:int = byteArray.readByte();
this.SetValue(valueName, b);
}
else if(node.name() == "Float")
{
var f:Number = byteArray.readFloat();
this.SetValue(valueName, f);
}
}
}
//得字节流
private function GetArray(byteArray:ByteArray, count:int, xml:XML):Array
{
var array:Array = new Array();
var map:XML = xml.child("Item")[0];
for(var i:int = 0 ; i < count ;i++)
{
var item:NetMsgItem = new NetMsgItem();
item.ParseItem(byteArray, map);
array.push(item);
}
return array;
}
public function SetValue(key:String, value:Object):void
{
this._paramMap[key] = value;
}
public function GetValue(key:String):Object
{
return this._paramMap[key];
}
public function GetIntValue(key:String):int
{
return this.GetValue(key) as int;
}
public function GetStringValue(key:String):String
{
return this.GetValue(key) as String;
}
public function GetArrayValue(key:String):Array
{
return this.GetValue(key) as Array;
}
public function GetBooleanValue(key:String):Boolean
{
return this.GetValue(key) as Boolean;
}
public function GetFloatValue(key:String):Number
{
return this.GetValue(key) as Number;
}
public function GetByte(key:String):int
{
var o:Object = this.GetValue(key);
if(o is Number)
{
return o as int;
}
else
{
return o.toString().charCodeAt(0);
}
}
//得此消息的字节流
public function GetByteArray():ByteArray
{
var byteArray:ByteArray = new ByteArray();
byteArray.endian = Endian.LITTLE_ENDIAN;
//消息头
byteArray.writeInt(0); //length位置预留
byteArray.writeInt(this._netMsgHead.Type);
byteArray.writeInt(this._netMsgHead.EndFlag);
byteArray.writeInt(this._netMsgHead.Number);
//消息体
this.GetBytes(byteArray);
//重新定义消息长度
byteArray.position = 0;
byteArray.writeInt(byteArray.length);
byteArray.position = 0;
return byteArray;
}
//得消息体字节流
protected function GetBytes(byteArray:ByteArray):void
{
var xmlList:XMLList= this._mappingElement.children();
for each(var node:XML in xmlList)
{
var name:String = node.attribute("name");
if(node.name() == "Int")
{
var value:Object = this.GetValue(name);
if(value == null)
{
byteArray.writeInt(0);
}
else if(value is Number)
{
byteArray.writeInt(this.GetValue(name) as int);
}
else if(value is String)
{
byteArray.writeInt(new Number(this.GetValue(name)));
}
}
else if(node.name() == "String")
{
var str:String = this.GetValue(name) as String;
if(str == null)
{
str = "";
}
var length:int = new Number(node.attribute("length"));
var charSet:String = node.attribute("charset");
if(charSet == null)
{
charSet = "utf-8";
}
var tempArray:ByteArray = new ByteArray();
tempArray.writeMultiByte(str, charSet);
if(tempArray.length > length)
{
byteArray.writeBytes(tempArray,0,length);
}
else if(tempArray.length < length)
{
byteArray.writeBytes(tempArray);
var lessLength:int = length - tempArray.length;
while(--lessLength >= 0)
{
byteArray.writeByte(0);
}
}
}
else if(node.name() == "Boolean")
{
var b:Boolean = this.GetBooleanValue(name);
byteArray.writeBoolean(b);
}
else if(node.name() == "Array")
{
var msgObjects:Array = this.GetArrayValue(name);
if(msgObjects == null)
{
msgObjects = new Array();
}
else
{
var count:int = msgObjects.length;
byteArray.writeInt(count);
for(var i:int = 0 ; i < msgObjects.length ; i++)
{
var msgObject:NetMsgItem = msgObjects[i];
byteArray.writeBytes(msgObject.GetItemByteArray(node.Item[0]));
}
}
}
else if(node.name() == "Byte")
{
byteArray.writeByte(this.GetByte(name));
}
else if(node.name() == "Float")
{
byteArray.writeFloat(this.GetFloatValue(name));
}
}
}
}
}
“NetMsgItem.as”文件:继承自NetMsg,为列表消息的列表项。
/********************************************************************
author: 田园小蛙 ourtree@live.cn
purpose: 列表消息子项
*********************************************************************/
package
{
import flash.utils.ByteArray;
import flash.utils.Endian;
public class NetMsgItem extends NetMsg
{
public function NetMsgItem()
{
}
//得列表项的字节流
public function GetItemByteArray(xmlItem:XML):ByteArray
{
this._mappingElement = xmlItem;
var byteArray:ByteArray = new ByteArray();
byteArray.endian = Endian.LITTLE_ENDIAN;
this.GetBytes(byteArray);
return byteArray;
}
//根据字节流生成列表项
public function ParseItem(byteArray:ByteArray, xmlItem:XML):void
{
this._mappingElement = xmlItem;
this.PushData(byteArray);
}
}
}
“NetMsgProcessor.as”文件:加载消息格式,调用“字节流生成消息”方法,调用“取消息的字节流”方法
/********************************************************************
author: 田园小蛙 ourtree@live.cn
purpose: 消息构建器
*********************************************************************/
package
{
import flash.utils.ByteArray;
import flash.utils.Endian;
public class NetMsgProcessor
{
private static var nNumber:int = 0;//同一批次消息的索引
private var _xmlMsgConfig:MsgConfig = new MsgConfig();
public function NetMsgProcessor()
{
Init();//目前在这里加载吧
}
public function Init():void
{
//加载配置文件
_xmlMsgConfig.LoadConfig();
}
//从字节流生成消息
public function BuildNetMsgFromBuffer(byteArray:ByteArray):NetMsg
{
byteArray.position = 4;
var nMsgType:int = byteArray.readInt();
var xmlItem:XML = _xmlMsgConfig.GetMappingByType(nMsgType);
if (xmlItem == null)
return null;
byteArray.position = 0;
var netMsg:NetMsg = new NetMsg();
netMsg.Parse(byteArray, xmlItem);
return netMsg;
}
//生成消息的字节流
public function GetSendNetMsg(nMsgType:int):NetMsg
{
var xmlItem:XML = _xmlMsgConfig.GetMappingByType(nMsgType);
if (xmlItem == null)
return null;
var netMsg:NetMsg = new NetMsg();
netMsg.InitNetMsg(xmlItem, nMsgType, nNumber++);
return netMsg;
}
}
}
“ReceiveEvent.as”文件:通知界面处理消息的事件
/********************************************************************
author: 田园小蛙 ourtree@live.cn
purpose: 收到消息事件
*********************************************************************/
package
{
import flash.events.Event;
public class ReceiveEvent extends Event
{
public static const Event_NetMsg:String = "NETMSG";
public var m_netMsg:NetMsg = null;
public function ReceiveEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
override public function clone():Event
{
var newEvent:ReceiveEvent = new ReceiveEvent(type);
newEvent.m_netMsg = m_netMsg;
return newEvent;
}
}
}
“EnumType.as”文件:消息类型。
/********************************************************************
author: 田园小蛙 ourtree@live.cn
purpose: 消息类型,公共枚举
*********************************************************************/
package
{
public class EnumType
{
public static const NetMsg_RoleEvent:int = 1;
public static const NetMsg_ChatInfo:int = 2;
public static const NetMsg_RoleInfoList:int = 3;
public static const NetMsg_ChatInfo_Community:int = 1;//公共聊天
public static const NetMsg_RoleEvent_Login:int = 1;//用户登陆
public static const NetMsg_RoleEvent_ValideLogin:int = 2;//验证用户登陆
public static const NetMsg_RoleEvent_ReqRoleList:int = 3;//请求用户列表
public static const NetMsg_RoleEvent_RoleIn:int = 4;//连接
public static const NetMsg_RoleEvent_RoleOut:int = 5;//退出
public static const NetMsg_EndFlag_Normal:int = 0;//普通消息
public static const NetMsg_EndFlag_End:int = 1;//结束消息
public function EnumType()
{
}
}
}
三、客户端socket通讯连接
“SocketConnnector.as”文件:
/********************************************************************
author: 田园小蛙 ourtree@live.cn
purpose: 网络连接
*********************************************************************/
package
{
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.net.Socket;
import flash.system.Security;
import flash.utils.ByteArray;
import flash.utils.Endian;
public class SocketConnector extends EventDispatcher
{
public var m_socket:Socket = new Socket();
private var _msgReceiveEvent:Object = new Object();
private var _limitType:Object = new Object();
private var _netMsgProcessor:NetMsgProcessor = null;
private var m_bufRecvMsg:ByteArray = new ByteArray();//接收到不够组成一个包的数据缓冲
public function get GetNetMsgProcessor():NetMsgProcessor
{
return _netMsgProcessor;
}
public function SocketConnector():void
{
m_socket.endian = Endian.LITTLE_ENDIAN;
m_bufRecvMsg.endian = Endian.LITTLE_ENDIAN;
_limitType[11] = true;
Init();
}
public function Init():void
{
_netMsgProcessor = new NetMsgProcessor();
BufReset();
}
public function Reset():void
{
m_socket = new Socket();
BufReset();
}
//初始化数据缓存
private function BufReset():void
{
m_bufRecvMsg.length = 1024 * 20;
m_bufRecvMsg.clear();
}
/**
*m_socket 连接
* */
public function Connect(host:String,port:int):Boolean
{
flash.system.Security.loadPolicyFile("xmlm_socket://"+host+":"+port);
m_socket.addEventListener(Event.CLOSE, CloseHandler);
//m_socket.addEventListener(ProgressEvent.SOCKET_DATA, InitHandler);
m_socket.addEventListener(ProgressEvent.SOCKET_DATA, DataHandler);
try
{
m_socket.connect(host,port);
}
catch (error:Error)
{
m_socket.close();
return false;
}
return true;
}
private function CloseHandler(event:Event):void
{
}
//收策略文件
private function InitHandler(event:Event):void
{
var tempByteArray:ByteArray = new ByteArray();
tempByteArray.length = m_socket.bytesAvailable;
if (tempByteArray.length > 0)
{
m_socket.readBytes(tempByteArray, 0, tempByteArray.length);
}
m_socket.removeEventListener(ProgressEvent.SOCKET_DATA, InitHandler);
m_socket.addEventListener(ProgressEvent.SOCKET_DATA, DataHandler);
}
//收字节流消息
private function DataHandler(event:Event):void
{
var tempByteArray:ByteArray = new ByteArray();
tempByteArray.endian = Endian.LITTLE_ENDIAN;
var nrevSize:int = m_socket.bytesAvailable;
if (nrevSize > 0)
{
m_socket.readBytes(m_bufRecvMsg, m_bufRecvMsg.bytesAvailable, nrevSize);
m_bufRecvMsg.position = 0;
var nMsgLength:int = m_bufRecvMsg.readInt();
var nMsgType:int = m_bufRecvMsg.readInt();
m_bufRecvMsg.position = 0;
while (m_bufRecvMsg.bytesAvailable >= nMsgLength)
{
var nLeftLength:int = m_bufRecvMsg.bytesAvailable - nMsgLength;//组成一包后多余的字节数
//新建个消息包字节流
var msgDataByteArray:ByteArray = new ByteArray();
msgDataByteArray.endian = Endian.LITTLE_ENDIAN;
msgDataByteArray.length = nMsgLength;
msgDataByteArray.position = 0;
//将字节拷入消息包流
m_bufRecvMsg.readBytes(msgDataByteArray,0,nMsgLength);
msgDataByteArray.bytesAvailable;
//将剩余字节拷入接收缓存
if (nLeftLength > 0)
{
m_bufRecvMsg.writeBytes(m_bufRecvMsg, 0, nLeftLength);
nMsgLength = m_bufRecvMsg.readInt();
nMsgType = m_bufRecvMsg.readInt();
m_bufRecvMsg.position = 0;
}
else
{
BufReset();
}
//生成一个NetMsg包
var netMsg:NetMsg = GetNetMsgProcessor.BuildNetMsgFromBuffer(msgDataByteArray);
//分发包
DispatchNetMsg(netMsg);
}
}
}
private function DispatchNetMsg(msg:NetMsg):void
{
var recvEvent:ReceiveEvent = new ReceiveEvent(ReceiveEvent.Event_NetMsg);
recvEvent.m_netMsg = msg;
dispatchEvent(recvEvent);
}
private function ErrorHandler(event:ErrorEvent):void
{
trace("error");
}
private function IoErrorHandler(event:IOErrorEvent):void
{
trace(event.text);
}
public function Send(msg:NetMsg):void
{
if(!m_socket.connected)
{
return;
}
var byteArray:ByteArray = msg.GetByteArray();
m_socket.writeBytes(byteArray);
m_socket.flush();
}
public function get Connected():Boolean
{
return m_socket.connected;
}
public function Close():void
{
m_socket.close();
}
}
}
封装的socket连接类,接收和发送字节流消息,将收到消息将通知界面处理。
DataHandler(event:Event)方法处理收到的字节流,分析字节流是否够组成一个消息包,如果够就组成一个消息包并通知界面,不够就将收到的数据缓存(m_bufRecvMsg)起来。
以上就是整个FLEX客户端代码。(完)
浙公网安备 33010602011771号