对于试用期新人来说没正事做,并不等于没事做。一些练练手的东西还是源源不断的。这两天在捣鼓日立扶梯项目的一些功能。其实这项目还未正式签约的,但一些功能上的实践还是必须的。这项目是日立公司应用于新品发布会,运用Flash动态画面,实时展示他们扶梯的安全性。日立对扶梯运行状况进行实时监控,将一些扶梯的运行数据从服务端传输到客服端,也就是平板电脑。这是基于C/S模式的,Flash在这方面的应用我也是首次尝试。
Flash对实时数据传输提供了套接字连接,也即是Socket连接。AS3.0提供两种不同类型的套接字连接,XML套接字连接的XMLSocket类和二进制套接字连接的Socket类。对于我来说如果服务器能发送XML数据过来当然是最好的,但可惜服务器是传输十六进制的数据的。相对XMLSocket来说,Socket数据处理更为底层,可操控范围更大,但难度也较大。
对于实时传输数据有几大有解决的问题,如何持续正确连接,如何处理粘包,如何缓存数据等。正确连接,可以采用类似三次握手原则,服务端向服务器发起连接请求,服务器接收后返回一个验证码,客服端接收到后确认可以进行连接,然后后向服务器发回一个指定的数据,服务器确认了数据的正确性再发送指令确认客服端可以连接了。
Flash的Socket连接是基于TCP的,所以不存在掉包的情况,最要是如何解决粘包或断包。从网上看到一些解决法案是服务器发送的数据在包头上加上数据长度信息,当客服端接收到数据包,先读取信息头,读取指示后面的数据有多大。如果已经读了信息头,则看能不能收到满足条件的字节数,若数据流里的数据满足条件,开始读数据,如果数据流里还未满足读取数据条件,则继续读取数据。至于如何缓存数据,最简单的就是将数据保存到数组中,不过如何在适当的时候读取数组中的数据还未想到较好的办法。
package { import flash.display.Sprite; import flash.net.Socket; import flash.events.IOErrorEvent; import flash.events.SecurityErrorEvent; import flash.events.ProgressEvent; import flash.events.Event; import flash.errors.IOError; import flash.events.MouseEvent; import flash.errors.EOFError; import flash.system.Security; import flash.utils.ByteArray; /** * * @author whk */ public class SocketExample extends Sprite { private var targetServer:String = "192.168.0.68"; //连接ip地址 private var port:uint = 9991; //连接端口 private var socket:Socket; private var str:String; private var response:String; private var msgLenMax:int; //收到的消息最大长度 private var msgLen:int; //消息长度 private var headLen:int; //消息头长度 private var isReadHead:Boolean; //是否已经读了消息头 private var bytes:ByteArray; //所读数据的缓冲数据,读出的数据放在这里 public function SocketExample() { isReadHead = true; headLen = 2; //2个字节 msgLenMax = 4028; bytes = new ByteArray(); //Security.loadPolicyFile("socket://"+targetServer+":"+port); socket = new Socket(); btnSend.enabled = false; btnConnect.addEventListener(MouseEvent.CLICK, btnHandler); } /** * 处理按钮事件 */ private function btnHandler(event:MouseEvent):void { switch (event.target.name) { case "btnConnect": btnLabel(); break; case "btnSend": sendRequest(); break; } } private function btnLabel():void { if (btnConnect.label == "连接" || btnConnect.label == "重新连接") { //注册socket事件 configureListeners(); //进行socket连接 connectToSocketServer(); } if (btnConnect.label == "关闭连接") { if (socket.connected) { socket.close(); btnConnect.label = "连接"; pringMessage("客服端已关闭连接"); } } } /** * 连接socket服务器 */ private function connectToSocketServer():void { try { socket.connect(targetServer, port); } catch (error:SecurityError) { pringMessage("SecurityError: " + error); } } private function configureListeners():void { socket.addEventListener(Event.CONNECT, connectHandler); socket.addEventListener(Event.CLOSE, closeHandler); socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler); } /** * 连接成功 */ private function connectHandler(event:Event):void { btnSend.enabled = true; btnConnect.label = "关闭连接"; pringMessage("连接成功" + "\n" + "connectHandler: " + event + "\n"); btnSend.addEventListener(MouseEvent.CLICK, btnHandler); trace("connectHandler: " + event); trace(txtRead.text); } /** * 向服务器发送数据 */ private function sendRequest():void { trace("sendRequest"); response = ""; //发送内容 writeln("GET /"); } private function writeln(str:String):void { str += txtSend.text + "\n"; if (socket.connected) { try { //将UTF-8字符串写入套接字 socket.writeInt(int(str)); txtRead.text += "发送数据的数据:" + str; socket.flush(); } catch (error:IOError) { pringMessage("socket.flush error\n" + error); } } else { //进行socket连接 connectToSocketServer(); } } /** * 接收数据,并读取 */ private function socketDataHandler(event:ProgressEvent):void { trace("socketDataHandler: " + event); readResponse(); } private function readResponse():void { try { //parseNetData(); var str:String = socket.readUTFBytes(socket.bytesAvailable); response += str; trace(response); //遍历数据包 while (socket.bytesAvailable) { var data:int = socket.readByte(); trace(data); } txtRead.text += "接收的数据:" + str + "\n"; } catch (error:IOError) { pringMessage("当socket已关闭而去读取时引发I/O 异常\n" + "socket.read error\n" + error); } catch (error:EOFError) { pringMessage("没有数据可读而读取时引发EOF异常\n" + "socket.read error\n" + error); } } /** * 解析网络数据流 * 根据信息头长度读取数据 */ private function parseNetData():void { //如果需要读信息头 if (isReadHead) { if (socket.bytesAvailable >= headLen) { //读出指示后面的数据有多大 msgLen = socket.readShort(); isReadHead = false; } } //如果已经读了信息头,则看能不能收到满足条件的字节数 if (!isReadHead && msgLen <= msgLenMax) { //如果为0,表示收到异常消息 if (msgLen == 0) { //一般消息长度为0的话,表示与服务器出了错,或者即将被断开等,通知客户端,进行特别处理 return; } //数据流里的数据满足条件,开始读数据 if (socket.bytesAvailable >= msgLen) { //指针回归 bytes.position = 0; //取出指定长度的网络字节 socket.readBytes(bytes, 0, msgLen); isReadHead = true; } } //如果数据流里还满足读取数据条件,继续读取数据 if (socket.bytesAvailable >= headLen) { parseNetData(); } } /** * socket服务器关闭连接 */ private function closeHandler(event:Event):void { btnConnect.label = "重新连接"; pringMessage("socke服务器已关闭\n" + "closeHandler: " + event); trace("closeHandler: " + event); } private function ioErrorHandler(event:IOErrorEvent):void { trace("ioErrorHandler: " + event); pringMessage("输入/输出错误并导致发送或加载操作失败\n" + "ioErrorHandler: " + event); } private function securityErrorHandler(event:SecurityErrorEvent):void { trace("securityErrorHandler: " + event); pringMessage("尝试连接到调用方安全沙箱外部的服务器或端口号低于 1024 的端口\n" + "securityErrorHandler: " + event); } /** * 将消息打印到屏幕 * @param m:打印的消息 */ private function pringMessage(m:String = null):void { txtError.visible = true; if (m == null) { txtError.text = ""; txtError.visible = false; return; } if (txtError.text == null) { txtError.text = ""; txtError.visible = false; } txtError.text = m; } } } |
Socket三次握手例子:
package { import flash.display.Sprite; import flash.events.ProgressEvent; import flash.net.Socket; import flash.utils.ByteArray; /** * * 三次握手连接socket * 来源socket编程 */ public class SocketHandshake extends Sprite { public const DETERMINE_VERSION:int = 0; public const RECEIVE_CHALLENGE:int = 1; public const NORMAL:int = 2; private var stateMap:Object; private var currentState:int; private var socket:Socket; public function SocketHandshake( ) { stateMap = new Object( ); stateMap[DETERMINE_VERSION] = readVersion; stateMap[RECEIVE_CHALLENGE] = readChallenge; stateMap[NORMAL] = readNormalProtocol; currentState = DETERMINE_VERSION; socket = new Socket( ); socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData ); socket.connect( "localhost", 9000 ); trace(currentState); } private function onSocketData( event:ProgressEvent ):void { trace(currentState); var processFunc:Function = stateMap[currentState]; processFunc( ); } private function readVersion( ):void { try { var version:int = socket.readInt(); trace(version); } catch (error:Error) { trace("error:"+error); } currentState = RECEIVE_CHALLENGE; socket.writeInt( version ); socket.flush( ); } private function readChallenge( ):void { var bytes:ByteArray = new ByteArray( ); socket.readBytes( bytes, 0, 8 ); currentState = NORMAL; socket.writeBytes( bytes ); socket.flush( ); } private function readNormalProtocol( ):void { } } } |