日立扶梯项目功能测试
对于试用期新人来说没正事做,并不等于没事做。一些练练手的东西还是源源不断的。这两天在捣鼓日立扶梯项目的一些功能。其实这项目还未正式签约的,但一些功能上的实践还是必须的。这项目是日立公司应用于新品发布会,运用Flash动态画面,实时展示他们扶梯的安全性。日立对扶梯运行状况进行实时监控,将一些扶梯的运行数据从服务端传输到客服端,也就是平板电脑。这是基于C/S模式的,Flash在这方面的应用我也是首次尝试。
Flash对实时数据传输提供了套接字连接,也即是Socket连接。AS3.0提供两种不同类型的套接字连接,XML套接字连接的XMLSocket类和二进制套接字连接的Socket类。对于我来说如果服务器能发送XML数据过来当然是最好的,但可惜服务器是传输十六进制的数据的。相对XMLSocket来说,Socket数据处理更为底层,可操控范围更大,但难度也较大。
对于实时传输数据有几大有解决的问题,如何持续正确连接,如何处理粘包,如何缓存数据等。正确连接,可以采用类似三次握手原则,服务端向服务器发起连接请求,服务器接收后返回一个验证码,客服端接收到后确认可以进行连接,然后后向服务器发回一个指定的数据,服务器确认了数据的正确性再发送指令确认客服端可以连接了。
Flash的Socket连接是基于TCP的,所以不存在掉包的情况,最要是如何解决粘包或断包。从网上看到一些解决法案是服务器发送的数据在包头上加上数据长度信息,当客服端接收到数据包,先读取信息头,读取指示后面的数据有多大。如果已经读了信息头,则看能不能收到满足条件的字节数,若数据流里的数据满足条件,开始读数据,如果数据流里还未满足读取数据条件,则继续读取数据。至于如何缓存数据,最简单的就是将数据保存到数组中,不过如何在适当的时候读取数组中的数据还未想到较好的办法。
1 package 2 { 3 import flash.display.Sprite; 4 import flash.net.Socket; 5 import flash.events.IOErrorEvent; 6 import flash.events.SecurityErrorEvent; 7 import flash.events.ProgressEvent; 8 import flash.events.Event; 9 import flash.errors.IOError; 10 import flash.events.MouseEvent; 11 import flash.errors.EOFError; 12 import flash.system.Security; 13 import flash.utils.ByteArray; 14 15 /** 16 * 17 * @author whk 18 */ 19 public class SocketExample extends Sprite 20 { 21 private var targetServer:String = "192.168.0.68"; //连接ip地址 22 private var port:uint = 9991; //连接端口 23 private var socket:Socket; 24 private var str:String; 25 private var response:String; 26 private var msgLenMax:int; //收到的消息最大长度 27 private var msgLen:int; //消息长度 28 private var headLen:int; //消息头长度 29 private var isReadHead:Boolean; //是否已经读了消息头 30 private var bytes:ByteArray; //所读数据的缓冲数据,读出的数据放在这里 31 32 public function SocketExample() 33 { 34 isReadHead = true; 35 headLen = 2; //2个字节 36 msgLenMax = 4028; 37 bytes = new ByteArray(); 38 //Security.loadPolicyFile("socket://"+targetServer+":"+port); 39 socket = new Socket(); 40 btnSend.enabled = false; 41 btnConnect.addEventListener(MouseEvent.CLICK, btnHandler); 42 } 43 44 /** 45 * 处理按钮事件 46 */ 47 private function btnHandler(event:MouseEvent):void 48 { 49 switch (event.target.name) 50 { 51 case "btnConnect": 52 btnLabel(); 53 break; 54 case "btnSend": 55 sendRequest(); 56 break; 57 } 58 } 59 60 private function btnLabel():void 61 { 62 if (btnConnect.label == "连接" || btnConnect.label == "重新连接") 63 { 64 //注册socket事件 65 configureListeners(); 66 //进行socket连接 67 connectToSocketServer(); 68 } 69 if (btnConnect.label == "关闭连接") 70 { 71 if (socket.connected) 72 { 73 socket.close(); 74 btnConnect.label = "连接"; 75 pringMessage("客服端已关闭连接"); 76 } 77 } 78 } 79 80 /** 81 * 连接socket服务器 82 */ 83 private function connectToSocketServer():void 84 { 85 try 86 { 87 socket.connect(targetServer, port); 88 } 89 catch (error:SecurityError) 90 { 91 pringMessage("SecurityError: " + error); 92 } 93 94 } 95 96 private function configureListeners():void 97 { 98 socket.addEventListener(Event.CONNECT, connectHandler); 99 socket.addEventListener(Event.CLOSE, closeHandler); 100 socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); 101 socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); 102 socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler); 103 } 104 105 /** 106 * 连接成功 107 */ 108 private function connectHandler(event:Event):void 109 { 110 btnSend.enabled = true; 111 btnConnect.label = "关闭连接"; 112 pringMessage("连接成功" + "\n" + "connectHandler: " + event + "\n"); 113 btnSend.addEventListener(MouseEvent.CLICK, btnHandler); 114 trace("connectHandler: " + event); 115 trace(txtRead.text); 116 } 117 118 /** 119 * 向服务器发送数据 120 */ 121 private function sendRequest():void 122 { 123 trace("sendRequest"); 124 response = ""; 125 //发送内容 126 writeln("GET /"); 127 } 128 129 private function writeln(str:String):void 130 { 131 str += txtSend.text + "\n"; 132 if (socket.connected) 133 { 134 try 135 { 136 //将UTF-8字符串写入套接字 137 socket.writeInt(int(str)); 138 txtRead.text += "发送数据的数据:" + str; 139 socket.flush(); 140 } 141 catch (error:IOError) 142 { 143 pringMessage("socket.flush error\n" + error); 144 } 145 } 146 else 147 { 148 //进行socket连接 149 connectToSocketServer(); 150 } 151 } 152 153 /** 154 * 接收数据,并读取 155 */ 156 private function socketDataHandler(event:ProgressEvent):void 157 { 158 trace("socketDataHandler: " + event); 159 readResponse(); 160 } 161 162 private function readResponse():void 163 { 164 try 165 { 166 //parseNetData(); 167 var str:String = socket.readUTFBytes(socket.bytesAvailable); 168 response += str; 169 trace(response); 170 //遍历数据包 171 while (socket.bytesAvailable) 172 { 173 var data:int = socket.readByte(); 174 trace(data); 175 } 176 txtRead.text += "接收的数据:" + str + "\n"; 177 } 178 catch (error:IOError) 179 { 180 pringMessage("当socket已关闭而去读取时引发I/O 异常\n" + "socket.read error\n" + error); 181 } 182 catch (error:EOFError) 183 { 184 pringMessage("没有数据可读而读取时引发EOF异常\n" + "socket.read error\n" + error); 185 } 186 187 } 188 189 /** 190 * 解析网络数据流 191 * 根据信息头长度读取数据 192 */ 193 private function parseNetData():void 194 { 195 //如果需要读信息头 196 if (isReadHead) 197 { 198 if (socket.bytesAvailable >= headLen) 199 { 200 //读出指示后面的数据有多大 201 msgLen = socket.readShort(); 202 isReadHead = false; 203 } 204 } 205 //如果已经读了信息头,则看能不能收到满足条件的字节数 206 if (!isReadHead && msgLen <= msgLenMax) 207 { 208 //如果为0,表示收到异常消息 209 if (msgLen == 0) 210 { 211 //一般消息长度为0的话,表示与服务器出了错,或者即将被断开等,通知客户端,进行特别处理 212 return; 213 } 214 //数据流里的数据满足条件,开始读数据 215 if (socket.bytesAvailable >= msgLen) 216 { 217 //指针回归 218 bytes.position = 0; 219 //取出指定长度的网络字节 220 socket.readBytes(bytes, 0, msgLen); 221 isReadHead = true; 222 } 223 } 224 //如果数据流里还满足读取数据条件,继续读取数据 225 if (socket.bytesAvailable >= headLen) 226 { 227 parseNetData(); 228 } 229 } 230 231 /** 232 * socket服务器关闭连接 233 */ 234 private function closeHandler(event:Event):void 235 { 236 btnConnect.label = "重新连接"; 237 pringMessage("socke服务器已关闭\n" + "closeHandler: " + event); 238 trace("closeHandler: " + event); 239 } 240 241 private function ioErrorHandler(event:IOErrorEvent):void 242 { 243 trace("ioErrorHandler: " + event); 244 pringMessage("输入/输出错误并导致发送或加载操作失败\n" + "ioErrorHandler: " + event); 245 } 246 247 private function securityErrorHandler(event:SecurityErrorEvent):void 248 { 249 trace("securityErrorHandler: " + event); 250 pringMessage("尝试连接到调用方安全沙箱外部的服务器或端口号低于 1024 的端口\n" + "securityErrorHandler: " + event); 251 } 252 253 /** 254 * 将消息打印到屏幕 255 * @param m:打印的消息 256 */ 257 private function pringMessage(m:String = null):void 258 { 259 txtError.visible = true; 260 if (m == null) 261 { 262 txtError.text = ""; 263 txtError.visible = false; 264 return; 265 } 266 if (txtError.text == null) 267 { 268 txtError.text = ""; 269 txtError.visible = false; 270 } 271 txtError.text = m; 272 } 273 274 } 275 }
Socket三次握手例子:
1 package 2 { 3 import flash.display.Sprite; 4 import flash.events.ProgressEvent; 5 import flash.net.Socket; 6 import flash.utils.ByteArray; 7 /** 8 * 9 * 三次握手连接socket 10 * 来源socket编程 11 */ 12 public class SocketHandshake extends Sprite 13 { 14 public const DETERMINE_VERSION:int = 0; 15 public const RECEIVE_CHALLENGE:int = 1; 16 public const NORMAL:int = 2; 17 private var stateMap:Object; 18 private var currentState:int; 19 private var socket:Socket; 20 public function SocketHandshake( ) 21 { 22 stateMap = new Object( ); 23 stateMap[DETERMINE_VERSION] = readVersion; 24 stateMap[RECEIVE_CHALLENGE] = readChallenge; 25 stateMap[NORMAL] = readNormalProtocol; 26 currentState = DETERMINE_VERSION; 27 socket = new Socket( ); 28 socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData ); 29 socket.connect( "localhost", 9000 ); 30 trace(currentState); 31 } 32 private function onSocketData( event:ProgressEvent ):void 33 { 34 trace(currentState); 35 var processFunc:Function = stateMap[currentState]; 36 processFunc( ); 37 } 38 private function readVersion( ):void 39 { 40 41 try 42 { 43 var version:int = socket.readInt(); 44 trace(version); 45 } 46 catch (error:Error) 47 { 48 trace("error:"+error); 49 } 50 currentState = RECEIVE_CHALLENGE; 51 socket.writeInt( version ); 52 socket.flush( ); 53 } 54 private function readChallenge( ):void 55 { 56 var bytes:ByteArray = new ByteArray( ); 57 socket.readBytes( bytes, 0, 8 ); 58 currentState = NORMAL; 59 socket.writeBytes( bytes ); 60 socket.flush( ); 61 } 62 private function readNormalProtocol( ):void 63 { 64 } 65 } 66 }